From f97bc88f5b51741e019ada0d10bf66e0ce14c91a Mon Sep 17 00:00:00 2001 From: Jaykumar Pitambarbhai Patel Date: Mon, 3 Jun 2019 11:57:48 +0530 Subject: [PATCH] v0.9 PSA APIs test suite update This release includes the following major enhancements: 1. Enhanced Crypto and IPC suites for new tests 2. Added TFM-MUSCA_B support 3. Bug fixes, feature enhancement and documentation update --- api-specs/include/psa/crypto.h | 1277 ++--------------- api-specs/include/psa/crypto_extra.h | 113 +- api-specs/include/psa/crypto_platform.h | 62 +- api-specs/include/psa/crypto_sizes.h | 66 +- api-specs/include/psa/crypto_struct.h | 184 ++- api-specs/include/psa/crypto_types.h | 21 +- api-specs/include/psa/crypto_values.h | 288 ++-- api-tests/README.md | 33 +- api-tests/dev_apis/README.md | 81 +- .../dev_apis/crypto/test_c002/test_c002.c | 6 +- .../dev_apis/crypto/test_c002/test_data.h | 12 +- .../dev_apis/crypto/test_c003/test_c003.c | 5 +- .../dev_apis/crypto/test_c003/test_data.h | 10 +- .../dev_apis/crypto/test_c004/test_c004.c | 2 +- .../dev_apis/crypto/test_c005/test_c005.c | 2 +- .../dev_apis/crypto/test_c006/test_c006.c | 4 +- .../dev_apis/crypto/test_c006/test_data.h | 3 +- .../dev_apis/crypto/test_c007/test_c007.c | 2 +- .../dev_apis/crypto/test_c007/test_data.h | 2 +- .../dev_apis/crypto/test_c008/test_c008.c | 2 + .../dev_apis/crypto/test_c009/test_c009.c | 15 +- .../dev_apis/crypto/test_c009/test_data.h | 34 +- .../dev_apis/crypto/test_c011/test_c011.c | 15 +- .../dev_apis/crypto/test_c011/test_data.h | 9 + .../dev_apis/crypto/test_c012/test_c012.c | 10 +- .../dev_apis/crypto/test_c014/test_c014.c | 8 +- .../dev_apis/crypto/test_c015/test_c015.c | 2 +- .../dev_apis/crypto/test_c016/test_c016.c | 22 +- .../dev_apis/crypto/test_c017/test_c017.c | 52 +- .../dev_apis/crypto/test_c017/test_data.h | 3 - .../dev_apis/crypto/test_c018/test_c018.c | 53 +- .../dev_apis/crypto/test_c018/test_c018.h | 1 + .../dev_apis/crypto/test_c018/test_data.h | 4 +- .../dev_apis/crypto/test_c019/test_c019.c | 50 +- .../dev_apis/crypto/test_c019/test_c019.h | 1 + .../dev_apis/crypto/test_c020/test_c020.c | 69 +- .../dev_apis/crypto/test_c020/test_data.h | 2 +- .../dev_apis/crypto/test_c021/test_c021.c | 4 + .../dev_apis/crypto/test_c022/test_c022.c | 23 +- .../dev_apis/crypto/test_c022/test_data.h | 10 +- .../dev_apis/crypto/test_c024/test_c024.c | 33 +- .../dev_apis/crypto/test_c024/test_data.h | 2 +- .../dev_apis/crypto/test_c025/test_c025.c | 34 +- .../dev_apis/crypto/test_c025/test_data.h | 17 + .../dev_apis/crypto/test_c026/test_c026.c | 20 +- .../dev_apis/crypto/test_c027/test_c027.c | 41 +- .../dev_apis/crypto/test_c028/test_c028.c | 20 +- .../dev_apis/crypto/test_c029/test_c029.c | 20 +- .../dev_apis/crypto/test_c030/test_c030.c | 15 +- .../dev_apis/crypto/test_c031/test_c031.c | 12 +- .../dev_apis/crypto/test_c032/test_c032.c | 17 +- .../dev_apis/crypto/test_c033/test_c033.c | 18 +- .../dev_apis/crypto/test_c034/test_c034.c | 17 +- .../dev_apis/crypto/test_c035/test_c035.c | 6 +- .../dev_apis/crypto/test_c036/test_c036.c | 49 +- .../dev_apis/crypto/test_c036/test_c036.h | 1 + .../dev_apis/crypto/test_c036/test_data.h | 2 + .../dev_apis/crypto/test_c037/test_c037.c | 18 +- .../dev_apis/crypto/test_c038/test_c038.c | 14 +- .../dev_apis/crypto/test_c039/test_c039.c | 29 +- .../dev_apis/crypto/test_c039/test_data.h | 8 +- .../dev_apis/crypto/test_c040/test_c040.c | 25 +- .../dev_apis/crypto/test_c041/test_c041.c | 20 +- .../dev_apis/crypto/test_c042/test_c042.c | 6 +- .../dev_apis/crypto/test_c043/test_c043.c | 20 +- api-tests/dev_apis/crypto/test_c044/source.mk | 20 + .../dev_apis/crypto/test_c044/test_c044.c | 302 ++++ .../dev_apis/crypto/test_c044/test_c044.h | 31 + .../dev_apis/crypto/test_c044/test_data.h | 348 +++++ .../dev_apis/crypto/test_c044/test_entry.c | 53 + api-tests/dev_apis/crypto/testsuite.db | 1 + .../test_s002/test_its_data.h | 3 + .../test_s004/test_s004.c | 4 +- .../test_s006/test_s006.c | 2 +- .../test_s009/test_its_data.h | 18 +- .../test_s009/test_ps_data.h | 18 +- .../test_s009/test_s009.c | 48 +- .../test_s009/test_s009.h | 2 +- .../test_s010/test_its_data.h | 2 +- .../test_s010/test_ps_data.h | 2 +- .../test_s010/test_s010.c | 2 +- ..._APIs_Arch_Test_Validation_Methodology.pdf | Bin 587853 -> 590051 bytes api-tests/docs/porting_guide_dev_apis.md | 56 +- api-tests/docs/porting_guide_ff.md | 56 +- api-tests/docs/psa_attestation_testlist.md | 2 +- api-tests/docs/psa_crypto_testlist.md | 193 ++- api-tests/docs/psa_ipc_testlist.md | 218 +-- api-tests/docs/psa_its_testlist.md | 26 +- api-tests/docs/psa_ps_testlist.md | 39 +- api-tests/docs/sw_requirements.md | 38 +- api-tests/ff/README.md | 114 +- api-tests/ff/ipc/test_i001/test_i001.c | 8 +- api-tests/ff/ipc/test_i001/test_supp_i001.c | 15 +- api-tests/ff/ipc/test_i002/test_i002.c | 41 +- api-tests/ff/ipc/test_i002/test_i002.h | 2 +- api-tests/ff/ipc/test_i002/test_supp_i002.c | 152 +- api-tests/ff/ipc/test_i003/test_i003.c | 16 +- api-tests/ff/ipc/test_i003/test_supp_i003.c | 203 +-- api-tests/ff/ipc/test_i004/test_i004.c | 44 +- api-tests/ff/ipc/test_i004/test_supp_i004.c | 13 +- api-tests/ff/ipc/test_i005/test_i005.c | 43 +- api-tests/ff/ipc/test_i005/test_supp_i005.c | 26 +- api-tests/ff/ipc/test_i006/test_i006.c | 45 +- api-tests/ff/ipc/test_i006/test_supp_i006.c | 26 +- api-tests/ff/ipc/test_i007/test_i007.c | 45 +- api-tests/ff/ipc/test_i007/test_supp_i007.c | 26 +- api-tests/ff/ipc/test_i008/test_i008.c | 59 +- api-tests/ff/ipc/test_i008/test_supp_i008.c | 45 +- api-tests/ff/ipc/test_i009/test_i009.c | 38 +- api-tests/ff/ipc/test_i009/test_supp_i009.c | 26 +- api-tests/ff/ipc/test_i010/test_i010.c | 48 +- api-tests/ff/ipc/test_i010/test_supp_i010.c | 25 +- api-tests/ff/ipc/test_i011/test_i011.c | 48 +- api-tests/ff/ipc/test_i011/test_supp_i011.c | 26 +- api-tests/ff/ipc/test_i012/test_i012.c | 42 +- api-tests/ff/ipc/test_i012/test_supp_i012.c | 13 +- api-tests/ff/ipc/test_i013/test_i013.c | 8 +- api-tests/ff/ipc/test_i013/test_supp_i013.c | 64 +- api-tests/ff/ipc/test_i014/test_i014.c | 8 +- api-tests/ff/ipc/test_i014/test_supp_i014.c | 62 +- api-tests/ff/ipc/test_i015/test_i015.c | 8 +- api-tests/ff/ipc/test_i015/test_supp_i015.c | 63 +- api-tests/ff/ipc/test_i016/test_i016.c | 8 +- api-tests/ff/ipc/test_i016/test_supp_i016.c | 61 +- api-tests/ff/ipc/test_i017/test_i017.c | 8 +- api-tests/ff/ipc/test_i017/test_supp_i017.c | 63 +- api-tests/ff/ipc/test_i018/test_i018.c | 8 +- api-tests/ff/ipc/test_i018/test_supp_i018.c | 71 +- api-tests/ff/ipc/test_i019/test_i019.c | 8 +- api-tests/ff/ipc/test_i019/test_supp_i019.c | 72 +- api-tests/ff/ipc/test_i020/test_i020.c | 8 +- api-tests/ff/ipc/test_i020/test_supp_i020.c | 67 +- api-tests/ff/ipc/test_i021/test_entry.c | 4 +- api-tests/ff/ipc/test_i021/test_i021.c | 44 +- api-tests/ff/ipc/test_i021/test_i021.h | 2 +- api-tests/ff/ipc/test_i021/test_supp_i021.c | 79 +- api-tests/ff/ipc/test_i022/test_i022.c | 8 +- api-tests/ff/ipc/test_i022/test_supp_i022.c | 65 +- api-tests/ff/ipc/test_i023/test_i023.c | 8 +- api-tests/ff/ipc/test_i023/test_supp_i023.c | 67 +- api-tests/ff/ipc/test_i024/test_i024.c | 59 +- api-tests/ff/ipc/test_i024/test_supp_i024.c | 33 +- api-tests/ff/ipc/test_i025/test_i025.c | 59 +- api-tests/ff/ipc/test_i025/test_supp_i025.c | 34 +- api-tests/ff/ipc/test_i026/test_i026.c | 46 +- api-tests/ff/ipc/test_i026/test_i026.h | 5 - api-tests/ff/ipc/test_i026/test_supp_i026.c | 55 +- api-tests/ff/ipc/test_i027/test_entry.c | 4 +- api-tests/ff/ipc/test_i027/test_i027.c | 111 +- api-tests/ff/ipc/test_i027/test_supp_i027.c | 43 +- api-tests/ff/ipc/test_i028/test_entry.c | 2 +- api-tests/ff/ipc/test_i028/test_i028.c | 6 +- api-tests/ff/ipc/test_i028/test_supp_i028.c | 61 +- api-tests/ff/ipc/test_i029/test_i029.c | 13 +- api-tests/ff/ipc/test_i029/test_supp_i029.c | 68 +- api-tests/ff/ipc/test_i030/test_i030.c | 8 +- api-tests/ff/ipc/test_i030/test_supp_i030.c | 77 +- api-tests/ff/ipc/test_i031/test_i031.c | 8 +- api-tests/ff/ipc/test_i031/test_supp_i031.c | 77 +- api-tests/ff/ipc/test_i032/test_i032.c | 8 +- api-tests/ff/ipc/test_i032/test_supp_i032.c | 77 +- api-tests/ff/ipc/test_i033/test_i033.c | 8 +- api-tests/ff/ipc/test_i033/test_supp_i033.c | 77 +- api-tests/ff/ipc/test_i034/test_i034.c | 6 +- api-tests/ff/ipc/test_i034/test_supp_i034.c | 61 +- api-tests/ff/ipc/test_i035/test_i035.c | 6 +- api-tests/ff/ipc/test_i035/test_supp_i035.c | 69 +- api-tests/ff/ipc/test_i036/test_i036.c | 8 +- api-tests/ff/ipc/test_i036/test_supp_i036.c | 77 +- api-tests/ff/ipc/test_i037/test_i037.c | 8 +- api-tests/ff/ipc/test_i037/test_supp_i037.c | 75 +- api-tests/ff/ipc/test_i038/test_i038.c | 8 +- api-tests/ff/ipc/test_i038/test_supp_i038.c | 77 +- api-tests/ff/ipc/test_i039/test_i039.c | 8 +- api-tests/ff/ipc/test_i039/test_supp_i039.c | 77 +- api-tests/ff/ipc/test_i040/test_i040.c | 6 +- api-tests/ff/ipc/test_i040/test_supp_i040.c | 61 +- api-tests/ff/ipc/test_i041/test_i041.c | 6 +- api-tests/ff/ipc/test_i041/test_supp_i041.c | 69 +- api-tests/ff/ipc/test_i042/test_i042.c | 8 +- api-tests/ff/ipc/test_i042/test_supp_i042.c | 74 +- api-tests/ff/ipc/test_i043/test_i043.c | 8 +- api-tests/ff/ipc/test_i043/test_supp_i043.c | 77 +- api-tests/ff/ipc/test_i044/test_i044.c | 8 +- api-tests/ff/ipc/test_i044/test_supp_i044.c | 75 +- api-tests/ff/ipc/test_i045/test_i045.c | 8 +- api-tests/ff/ipc/test_i045/test_supp_i045.c | 77 +- api-tests/ff/ipc/test_i046/test_i046.c | 8 +- api-tests/ff/ipc/test_i046/test_supp_i046.c | 77 +- api-tests/ff/ipc/test_i047/test_i047.c | 6 +- api-tests/ff/ipc/test_i047/test_supp_i047.c | 67 +- api-tests/ff/ipc/test_i048/test_i048.c | 51 +- api-tests/ff/ipc/test_i048/test_supp_i048.c | 53 +- api-tests/ff/ipc/test_i049/test_i049.c | 51 +- api-tests/ff/ipc/test_i049/test_supp_i049.c | 53 +- api-tests/ff/ipc/test_i050/test_i050.c | 51 +- api-tests/ff/ipc/test_i050/test_supp_i050.c | 53 +- api-tests/ff/ipc/test_i051/test_i051.c | 53 +- api-tests/ff/ipc/test_i051/test_supp_i051.c | 53 +- api-tests/ff/ipc/test_i052/test_i052.c | 56 +- api-tests/ff/ipc/test_i052/test_supp_i052.c | 53 +- api-tests/ff/ipc/test_i053/test_i053.c | 55 +- api-tests/ff/ipc/test_i053/test_supp_i053.c | 53 +- api-tests/ff/ipc/test_i054/test_entry.c | 7 - api-tests/ff/ipc/test_i054/test_i054.c | 42 +- api-tests/ff/ipc/test_i054/test_supp_i054.c | 53 +- api-tests/ff/ipc/test_i055/test_i055.c | 6 +- api-tests/ff/ipc/test_i055/test_supp_i055.c | 93 +- api-tests/ff/ipc/test_i056/test_i056.c | 6 +- api-tests/ff/ipc/test_i056/test_supp_i056.c | 77 +- api-tests/ff/ipc/test_i057/test_i057.c | 6 +- api-tests/ff/ipc/test_i057/test_supp_i057.c | 91 +- api-tests/ff/ipc/test_i058/test_i058.c | 7 +- api-tests/ff/ipc/test_i058/test_supp_i058.c | 27 +- api-tests/ff/ipc/test_i059/test_i059.c | 2 +- api-tests/ff/ipc/test_i059/test_supp_i059.c | 51 +- api-tests/ff/ipc/test_i060/test_i060.c | 2 +- api-tests/ff/ipc/test_i060/test_supp_i060.c | 51 +- api-tests/ff/ipc/test_i061/test_i061.c | 2 +- api-tests/ff/ipc/test_i061/test_supp_i061.c | 51 +- api-tests/ff/ipc/test_i062/test_i062.c | 2 +- api-tests/ff/ipc/test_i062/test_supp_i062.c | 51 +- api-tests/ff/ipc/test_i063/test_i063.c | 8 +- api-tests/ff/ipc/test_i063/test_supp_i063.c | 47 +- api-tests/ff/ipc/test_i064/test_i064.c | 18 +- api-tests/ff/ipc/test_i064/test_supp_i064.c | 9 +- api-tests/ff/ipc/test_i065/test_i065.c | 18 +- api-tests/ff/ipc/test_i065/test_supp_i065.c | 9 +- api-tests/ff/ipc/test_i066/test_i066.c | 18 +- api-tests/ff/ipc/test_i066/test_supp_i066.c | 9 +- api-tests/ff/ipc/test_i067/source.mk | 25 + api-tests/ff/ipc/test_i067/test_entry.c | 52 + api-tests/ff/ipc/test_i067/test_i067.c | 65 + api-tests/ff/ipc/test_i067/test_i067.h | 37 + api-tests/ff/ipc/test_i067/test_supp_i067.c | 127 ++ api-tests/ff/ipc/test_i068/source.mk | 25 + api-tests/ff/ipc/test_i068/test_entry.c | 52 + api-tests/ff/ipc/test_i068/test_i068.c | 94 ++ api-tests/ff/ipc/test_i068/test_i068.h | 37 + api-tests/ff/ipc/test_i068/test_supp_i068.c | 37 + api-tests/ff/ipc/test_i069/source.mk | 25 + api-tests/ff/ipc/test_i069/test_entry.c | 52 + api-tests/ff/ipc/test_i069/test_i069.c | 68 + api-tests/ff/ipc/test_i069/test_i069.h | 37 + api-tests/ff/ipc/test_i069/test_supp_i069.c | 37 + api-tests/ff/ipc/test_i070/source.mk | 25 + api-tests/ff/ipc/test_i070/test_entry.c | 52 + api-tests/ff/ipc/test_i070/test_i070.c | 75 + api-tests/ff/ipc/test_i070/test_i070.h | 37 + api-tests/ff/ipc/test_i070/test_supp_i070.c | 37 + api-tests/ff/ipc/test_i071/source.mk | 25 + api-tests/ff/ipc/test_i071/test_entry.c | 52 + api-tests/ff/ipc/test_i071/test_i071.c | 120 ++ api-tests/ff/ipc/test_i071/test_i071.h | 37 + api-tests/ff/ipc/test_i071/test_supp_i071.c | 37 + api-tests/ff/ipc/test_i072/source.mk | 25 + api-tests/ff/ipc/test_i072/test_entry.c | 51 + api-tests/ff/ipc/test_i072/test_i072.c | 125 ++ api-tests/ff/ipc/test_i072/test_i072.h | 38 + api-tests/ff/ipc/test_i072/test_supp_i072.c | 113 ++ api-tests/ff/ipc/test_i073/source.mk | 25 + api-tests/ff/ipc/test_i073/test_entry.c | 51 + api-tests/ff/ipc/test_i073/test_i073.c | 125 ++ api-tests/ff/ipc/test_i073/test_i073.h | 38 + api-tests/ff/ipc/test_i073/test_supp_i073.c | 120 ++ api-tests/ff/ipc/test_i074/source.mk | 25 + api-tests/ff/ipc/test_i074/test_entry.c | 51 + api-tests/ff/ipc/test_i074/test_i074.c | 145 ++ api-tests/ff/ipc/test_i074/test_i074.h | 38 + api-tests/ff/ipc/test_i074/test_supp_i074.c | 146 ++ api-tests/ff/ipc/test_i075/source.mk | 25 + api-tests/ff/ipc/test_i075/test_entry.c | 51 + api-tests/ff/ipc/test_i075/test_i075.c | 127 ++ api-tests/ff/ipc/test_i075/test_i075.h | 38 + api-tests/ff/ipc/test_i075/test_supp_i075.c | 143 ++ api-tests/ff/ipc/test_i076/source.mk | 25 + api-tests/ff/ipc/test_i076/test_entry.c | 51 + api-tests/ff/ipc/test_i076/test_i076.c | 144 ++ api-tests/ff/ipc/test_i076/test_i076.h | 38 + api-tests/ff/ipc/test_i076/test_supp_i076.c | 44 + api-tests/ff/ipc/test_i077/source.mk | 25 + api-tests/ff/ipc/test_i077/test_entry.c | 51 + api-tests/ff/ipc/test_i077/test_i077.c | 145 ++ api-tests/ff/ipc/test_i077/test_i077.h | 38 + api-tests/ff/ipc/test_i077/test_supp_i077.c | 44 + api-tests/ff/ipc/test_i078/source.mk | 25 + api-tests/ff/ipc/test_i078/test_entry.c | 51 + api-tests/ff/ipc/test_i078/test_i078.c | 162 +++ api-tests/ff/ipc/test_i078/test_i078.h | 38 + api-tests/ff/ipc/test_i078/test_supp_i078.c | 44 + api-tests/ff/ipc/test_i079/source.mk | 25 + api-tests/ff/ipc/test_i079/test_entry.c | 51 + api-tests/ff/ipc/test_i079/test_i079.c | 144 ++ api-tests/ff/ipc/test_i079/test_i079.h | 38 + api-tests/ff/ipc/test_i079/test_supp_i079.c | 46 + api-tests/ff/ipc/test_i080/source.mk | 25 + api-tests/ff/ipc/test_i080/test_entry.c | 52 + api-tests/ff/ipc/test_i080/test_i080.c | 144 ++ api-tests/ff/ipc/test_i080/test_i080.h | 38 + api-tests/ff/ipc/test_i080/test_supp_i080.c | 44 + api-tests/ff/ipc/test_i081/source.mk | 25 + api-tests/ff/ipc/test_i081/test_entry.c | 52 + api-tests/ff/ipc/test_i081/test_i081.c | 145 ++ api-tests/ff/ipc/test_i081/test_i081.h | 38 + api-tests/ff/ipc/test_i081/test_supp_i081.c | 44 + api-tests/ff/ipc/test_i082/source.mk | 25 + api-tests/ff/ipc/test_i082/test_entry.c | 52 + api-tests/ff/ipc/test_i082/test_i082.c | 162 +++ api-tests/ff/ipc/test_i082/test_i082.h | 38 + api-tests/ff/ipc/test_i082/test_supp_i082.c | 44 + api-tests/ff/ipc/test_i083/source.mk | 25 + api-tests/ff/ipc/test_i083/test_entry.c | 52 + api-tests/ff/ipc/test_i083/test_i083.c | 144 ++ api-tests/ff/ipc/test_i083/test_i083.h | 38 + api-tests/ff/ipc/test_i083/test_supp_i083.c | 46 + api-tests/ff/ipc/test_i084/source.mk | 25 + api-tests/ff/ipc/test_i084/test_entry.c | 52 + api-tests/ff/ipc/test_i084/test_i084.c | 125 ++ api-tests/ff/ipc/test_i084/test_i084.h | 38 + api-tests/ff/ipc/test_i084/test_supp_i084.c | 113 ++ api-tests/ff/ipc/test_i085/source.mk | 25 + api-tests/ff/ipc/test_i085/test_entry.c | 52 + api-tests/ff/ipc/test_i085/test_i085.c | 125 ++ api-tests/ff/ipc/test_i085/test_i085.h | 38 + api-tests/ff/ipc/test_i085/test_supp_i085.c | 120 ++ api-tests/ff/ipc/test_i086/source.mk | 25 + api-tests/ff/ipc/test_i086/test_entry.c | 52 + api-tests/ff/ipc/test_i086/test_i086.c | 145 ++ api-tests/ff/ipc/test_i086/test_i086.h | 38 + api-tests/ff/ipc/test_i086/test_supp_i086.c | 146 ++ api-tests/ff/ipc/test_i087/source.mk | 25 + api-tests/ff/ipc/test_i087/test_entry.c | 52 + api-tests/ff/ipc/test_i087/test_i087.c | 127 ++ api-tests/ff/ipc/test_i087/test_i087.h | 38 + api-tests/ff/ipc/test_i087/test_supp_i087.c | 143 ++ api-tests/ff/ipc/test_l088/source.mk | 25 + api-tests/ff/ipc/test_l088/test_entry.c | 52 + api-tests/ff/ipc/test_l088/test_l088.c | 37 + api-tests/ff/ipc/test_l088/test_l088.h | 37 + api-tests/ff/ipc/test_l088/test_supp_l088.c | 59 + api-tests/ff/ipc/testsuite.db | 142 +- .../ff/partition/common/driver_partition.c | 407 +++++- api-tests/ff/partition/ipc/client_partition.c | 13 +- api-tests/ff/partition/ipc/server_partition.c | 9 +- api-tests/platform/drivers/nvmem/pal_nvmem.c | 2 +- api-tests/platform/drivers/nvmem/pal_nvmem.h | 2 +- .../platform/drivers/uart/cmsdk/pal_uart.c | 51 +- .../platform/drivers/uart/cmsdk/pal_uart.h | 15 +- .../platform/drivers/uart/pl011/pal_uart.c | 69 +- .../platform/drivers/uart/pl011/pal_uart.h | 19 +- .../drivers/watchdog/cmsdk/pal_wd_cmsdk.h | 2 +- .../tgt_dev_apis_mbedos_fvp_mps2_m4/Makefile | 3 +- .../common/driver_partition_psa.json | 14 +- .../manifests/ipc/client_partition_psa.json | 11 +- .../manifests/ipc/server_partition_psa.json | 8 +- .../nspe/common/pal_config.h | 19 +- .../nspe/common/pal_driver_ipc_intf.c | 2 +- .../nspe/common/pal_driver_ns_intf.c | 2 +- .../nspe/crypto/pal_crypto_intf.c | 7 +- .../nspe/crypto/pal_crypto_intf.h | 1 + .../pal_attestation_crypto.c | 346 +++++ .../pal_attestation_crypto.h | 102 ++ .../initial_attestation/pal_attestation_eat.c | 130 +- .../initial_attestation/pal_attestation_eat.h | 105 +- .../pal_attestation_intf.h | 2 +- .../spe/pal_driver_intf.c | 22 +- .../spe/pal_driver_intf.h | 4 +- .../targets/tgt_dev_apis_tfm_an521/Makefile | 3 +- .../common/driver_partition_psa.json | 16 +- .../manifests/ipc/client_partition_psa.json | 11 +- .../manifests/ipc/server_partition_psa.json | 8 +- .../nspe/common/pal_config.h | 19 +- .../nspe/common/pal_driver_ipc_intf.c | 2 +- .../nspe/common/pal_driver_ns_intf.c | 2 +- .../nspe/crypto/pal_crypto_config.h | 30 +- .../nspe/crypto/pal_crypto_intf.c | 7 +- .../nspe/crypto/pal_crypto_intf.h | 1 + .../pal_attestation_crypto.c | 346 +++++ .../pal_attestation_crypto.h | 102 ++ .../initial_attestation/pal_attestation_eat.c | 130 +- .../initial_attestation/pal_attestation_eat.h | 105 +- .../pal_attestation_intf.h | 2 +- .../spe/pal_driver_intf.c | 22 +- .../spe/pal_driver_intf.h | 4 +- .../targets/tgt_dev_apis_tfm_musca_a/Makefile | 3 +- .../common/driver_partition_psa.json | 16 +- .../manifests/ipc/client_partition_psa.json | 11 +- .../manifests/ipc/server_partition_psa.json | 8 +- .../nspe/common/pal_config.h | 19 +- .../nspe/common/pal_driver_ipc_intf.c | 2 +- .../nspe/common/pal_driver_ns_intf.c | 2 +- .../nspe/crypto/pal_crypto_config.h | 30 +- .../nspe/crypto/pal_crypto_intf.c | 7 +- .../nspe/crypto/pal_crypto_intf.h | 1 + .../pal_attestation_crypto.c | 346 +++++ .../pal_attestation_crypto.h | 102 ++ .../initial_attestation/pal_attestation_eat.c | 130 +- .../initial_attestation/pal_attestation_eat.h | 105 +- .../pal_attestation_intf.h | 2 +- .../reference_logs/attestation.txt | 36 + .../reference_logs/crypto.txt | 766 ++++++++++ .../reference_logs/protected_storage.txt | 157 ++ .../spe/pal_driver_intf.c | 22 +- .../spe/pal_driver_intf.h | 4 +- .../tgt_dev_apis_tfm_musca_b1/Makefile | 160 +++ .../common/driver_partition_psa.json | 75 + .../manifests/ipc/client_partition_psa.json | 37 + .../manifests/ipc/server_partition_psa.json | 69 + .../nspe/common/pal_client_api_empty_intf.c | 93 ++ .../nspe/common/pal_client_api_intf.c | 97 ++ .../nspe/common/pal_client_api_intf.h | 32 + .../nspe/common/pal_common.h | 118 ++ .../nspe/common/pal_config.h | 119 ++ .../nspe/common/pal_driver_ipc_intf.c | 305 ++++ .../nspe/common/pal_driver_ns_intf.c | 145 ++ .../nspe/crypto/pal_crypto_config.h | 323 +++++ .../nspe/crypto/pal_crypto_config_check.h | 223 +++ .../nspe/crypto/pal_crypto_empty_intf.c | 30 + .../nspe/crypto/pal_crypto_intf.c | 340 +++++ .../nspe/crypto/pal_crypto_intf.h | 76 + .../pal_attestation_crypto.c | 346 +++++ .../pal_attestation_crypto.h | 102 ++ .../initial_attestation/pal_attestation_eat.c | 491 +++++++ .../initial_attestation/pal_attestation_eat.h | 170 +++ .../pal_attestation_empty_intf.c | 30 + .../pal_attestation_intf.c | 54 + .../pal_attestation_intf.h | 30 + .../pal_internal_trusted_storage_empty_intf.c | 30 + .../pal_internal_trusted_storage_intf.c | 60 + .../pal_internal_trusted_storage_intf.h | 31 + .../pal_protected_storage_empty_intf.c | 30 + .../pal_protected_storage_intf.c | 75 + .../pal_protected_storage_intf.h | 34 + .../reference_logs/attestation.txt | 36 + .../reference_logs/crypto.txt | 403 ++++++ .../reference_logs/protected_storage.txt | 154 ++ .../spe/pal_driver_intf.c | 131 ++ .../spe/pal_driver_intf.h | 35 + .../tgt_dev_apis_tfm_musca_b1/target.cfg | 56 + .../tgt_ff_mbedos_fvp_mps2_m4/Makefile | 3 +- .../common/driver_partition_psa.json | 14 +- .../manifests/ipc/client_partition_psa.json | 11 +- .../manifests/ipc/server_partition_psa.json | 8 +- .../nspe/common/pal_config.h | 19 +- .../nspe/common/pal_driver_ipc_intf.c | 2 +- .../nspe/common/pal_driver_ns_intf.c | 2 +- .../nspe/crypto/pal_crypto_intf.c | 7 +- .../nspe/crypto/pal_crypto_intf.h | 1 + .../pal_attestation_crypto.c | 346 +++++ .../pal_attestation_crypto.h | 102 ++ .../initial_attestation/pal_attestation_eat.c | 130 +- .../initial_attestation/pal_attestation_eat.h | 105 +- .../pal_attestation_intf.h | 2 +- .../spe/pal_driver_intf.c | 22 +- .../spe/pal_driver_intf.h | 4 +- .../tgt_ff_mbedos_fvp_mps2_m4/target.cfg | 13 +- api-tests/tools/makefiles/Makefile | 34 +- api-tests/tools/makefiles/linker/test.linker | 4 +- api-tests/tools/makefiles/linker/test.sct | 2 +- api-tests/tools/makefiles/spbuild.mk | 6 + api-tests/tools/makefiles/toolchain.mk | 20 +- api-tests/tools/scripts/gen_tests_list.pl | 8 +- .../tools/scripts/process_test_linker_file.pl | 2 - api-tests/tools/scripts/setup.sh | 83 +- api-tests/tools/scripts/targetConfigGen.pl | 238 --- api-tests/tools/scripts/targetConfigGen.py | 183 +++ api-tests/tools/scripts/test_elf_combine.pl | 4 +- api-tests/val/common/val.h | 31 +- api-tests/val/common/val_target.c | 15 +- api-tests/val/common/val_target.h | 10 +- api-tests/val/nspe/pal_interfaces_ns.h | 2 +- api-tests/val/nspe/val_attestation.h | 1 - api-tests/val/nspe/val_crypto.c | 2 +- api-tests/val/nspe/val_crypto.h | 2 + api-tests/val/nspe/val_dispatcher.c | 4 +- api-tests/val/nspe/val_entry.h | 2 +- api-tests/val/nspe/val_framework.c | 74 +- api-tests/val/nspe/val_interfaces.h | 2 +- api-tests/val/nspe/val_peripherals.c | 2 +- api-tests/val/nspe/val_peripherals.h | 2 +- api-tests/val/spe/pal_interfaces_s.h | 21 +- api-tests/val/spe/val_driver_service_apis.c | 50 +- api-tests/val/spe/val_driver_service_apis.h | 16 +- api-tests/val/spe/val_partition_common.h | 44 +- api-tests/val/spe/val_service_defs.h | 68 +- 485 files changed, 22490 insertions(+), 4819 deletions(-) create mode 100644 api-tests/dev_apis/crypto/test_c044/source.mk create mode 100644 api-tests/dev_apis/crypto/test_c044/test_c044.c create mode 100644 api-tests/dev_apis/crypto/test_c044/test_c044.h create mode 100644 api-tests/dev_apis/crypto/test_c044/test_data.h create mode 100644 api-tests/dev_apis/crypto/test_c044/test_entry.c mode change 100644 => 100755 api-tests/docs/Arm_PSA_APIs_Arch_Test_Validation_Methodology.pdf create mode 100644 api-tests/ff/ipc/test_i067/source.mk create mode 100644 api-tests/ff/ipc/test_i067/test_entry.c create mode 100644 api-tests/ff/ipc/test_i067/test_i067.c create mode 100644 api-tests/ff/ipc/test_i067/test_i067.h create mode 100644 api-tests/ff/ipc/test_i067/test_supp_i067.c create mode 100644 api-tests/ff/ipc/test_i068/source.mk create mode 100644 api-tests/ff/ipc/test_i068/test_entry.c create mode 100644 api-tests/ff/ipc/test_i068/test_i068.c create mode 100644 api-tests/ff/ipc/test_i068/test_i068.h create mode 100644 api-tests/ff/ipc/test_i068/test_supp_i068.c create mode 100644 api-tests/ff/ipc/test_i069/source.mk create mode 100644 api-tests/ff/ipc/test_i069/test_entry.c create mode 100644 api-tests/ff/ipc/test_i069/test_i069.c create mode 100644 api-tests/ff/ipc/test_i069/test_i069.h create mode 100644 api-tests/ff/ipc/test_i069/test_supp_i069.c create mode 100644 api-tests/ff/ipc/test_i070/source.mk create mode 100644 api-tests/ff/ipc/test_i070/test_entry.c create mode 100644 api-tests/ff/ipc/test_i070/test_i070.c create mode 100644 api-tests/ff/ipc/test_i070/test_i070.h create mode 100644 api-tests/ff/ipc/test_i070/test_supp_i070.c create mode 100644 api-tests/ff/ipc/test_i071/source.mk create mode 100644 api-tests/ff/ipc/test_i071/test_entry.c create mode 100644 api-tests/ff/ipc/test_i071/test_i071.c create mode 100644 api-tests/ff/ipc/test_i071/test_i071.h create mode 100644 api-tests/ff/ipc/test_i071/test_supp_i071.c create mode 100644 api-tests/ff/ipc/test_i072/source.mk create mode 100644 api-tests/ff/ipc/test_i072/test_entry.c create mode 100644 api-tests/ff/ipc/test_i072/test_i072.c create mode 100644 api-tests/ff/ipc/test_i072/test_i072.h create mode 100644 api-tests/ff/ipc/test_i072/test_supp_i072.c create mode 100644 api-tests/ff/ipc/test_i073/source.mk create mode 100644 api-tests/ff/ipc/test_i073/test_entry.c create mode 100644 api-tests/ff/ipc/test_i073/test_i073.c create mode 100644 api-tests/ff/ipc/test_i073/test_i073.h create mode 100644 api-tests/ff/ipc/test_i073/test_supp_i073.c create mode 100644 api-tests/ff/ipc/test_i074/source.mk create mode 100644 api-tests/ff/ipc/test_i074/test_entry.c create mode 100644 api-tests/ff/ipc/test_i074/test_i074.c create mode 100644 api-tests/ff/ipc/test_i074/test_i074.h create mode 100644 api-tests/ff/ipc/test_i074/test_supp_i074.c create mode 100644 api-tests/ff/ipc/test_i075/source.mk create mode 100644 api-tests/ff/ipc/test_i075/test_entry.c create mode 100644 api-tests/ff/ipc/test_i075/test_i075.c create mode 100644 api-tests/ff/ipc/test_i075/test_i075.h create mode 100644 api-tests/ff/ipc/test_i075/test_supp_i075.c create mode 100644 api-tests/ff/ipc/test_i076/source.mk create mode 100644 api-tests/ff/ipc/test_i076/test_entry.c create mode 100644 api-tests/ff/ipc/test_i076/test_i076.c create mode 100644 api-tests/ff/ipc/test_i076/test_i076.h create mode 100644 api-tests/ff/ipc/test_i076/test_supp_i076.c create mode 100644 api-tests/ff/ipc/test_i077/source.mk create mode 100644 api-tests/ff/ipc/test_i077/test_entry.c create mode 100644 api-tests/ff/ipc/test_i077/test_i077.c create mode 100644 api-tests/ff/ipc/test_i077/test_i077.h create mode 100644 api-tests/ff/ipc/test_i077/test_supp_i077.c create mode 100644 api-tests/ff/ipc/test_i078/source.mk create mode 100644 api-tests/ff/ipc/test_i078/test_entry.c create mode 100644 api-tests/ff/ipc/test_i078/test_i078.c create mode 100644 api-tests/ff/ipc/test_i078/test_i078.h create mode 100644 api-tests/ff/ipc/test_i078/test_supp_i078.c create mode 100644 api-tests/ff/ipc/test_i079/source.mk create mode 100644 api-tests/ff/ipc/test_i079/test_entry.c create mode 100644 api-tests/ff/ipc/test_i079/test_i079.c create mode 100644 api-tests/ff/ipc/test_i079/test_i079.h create mode 100644 api-tests/ff/ipc/test_i079/test_supp_i079.c create mode 100644 api-tests/ff/ipc/test_i080/source.mk create mode 100644 api-tests/ff/ipc/test_i080/test_entry.c create mode 100644 api-tests/ff/ipc/test_i080/test_i080.c create mode 100644 api-tests/ff/ipc/test_i080/test_i080.h create mode 100644 api-tests/ff/ipc/test_i080/test_supp_i080.c create mode 100644 api-tests/ff/ipc/test_i081/source.mk create mode 100644 api-tests/ff/ipc/test_i081/test_entry.c create mode 100644 api-tests/ff/ipc/test_i081/test_i081.c create mode 100644 api-tests/ff/ipc/test_i081/test_i081.h create mode 100644 api-tests/ff/ipc/test_i081/test_supp_i081.c create mode 100644 api-tests/ff/ipc/test_i082/source.mk create mode 100644 api-tests/ff/ipc/test_i082/test_entry.c create mode 100644 api-tests/ff/ipc/test_i082/test_i082.c create mode 100644 api-tests/ff/ipc/test_i082/test_i082.h create mode 100644 api-tests/ff/ipc/test_i082/test_supp_i082.c create mode 100644 api-tests/ff/ipc/test_i083/source.mk create mode 100644 api-tests/ff/ipc/test_i083/test_entry.c create mode 100644 api-tests/ff/ipc/test_i083/test_i083.c create mode 100644 api-tests/ff/ipc/test_i083/test_i083.h create mode 100644 api-tests/ff/ipc/test_i083/test_supp_i083.c create mode 100644 api-tests/ff/ipc/test_i084/source.mk create mode 100644 api-tests/ff/ipc/test_i084/test_entry.c create mode 100644 api-tests/ff/ipc/test_i084/test_i084.c create mode 100644 api-tests/ff/ipc/test_i084/test_i084.h create mode 100644 api-tests/ff/ipc/test_i084/test_supp_i084.c create mode 100644 api-tests/ff/ipc/test_i085/source.mk create mode 100644 api-tests/ff/ipc/test_i085/test_entry.c create mode 100644 api-tests/ff/ipc/test_i085/test_i085.c create mode 100644 api-tests/ff/ipc/test_i085/test_i085.h create mode 100644 api-tests/ff/ipc/test_i085/test_supp_i085.c create mode 100644 api-tests/ff/ipc/test_i086/source.mk create mode 100644 api-tests/ff/ipc/test_i086/test_entry.c create mode 100644 api-tests/ff/ipc/test_i086/test_i086.c create mode 100644 api-tests/ff/ipc/test_i086/test_i086.h create mode 100644 api-tests/ff/ipc/test_i086/test_supp_i086.c create mode 100644 api-tests/ff/ipc/test_i087/source.mk create mode 100644 api-tests/ff/ipc/test_i087/test_entry.c create mode 100644 api-tests/ff/ipc/test_i087/test_i087.c create mode 100644 api-tests/ff/ipc/test_i087/test_i087.h create mode 100644 api-tests/ff/ipc/test_i087/test_supp_i087.c create mode 100644 api-tests/ff/ipc/test_l088/source.mk create mode 100644 api-tests/ff/ipc/test_l088/test_entry.c create mode 100644 api-tests/ff/ipc/test_l088/test_l088.c create mode 100644 api-tests/ff/ipc/test_l088/test_l088.h create mode 100644 api-tests/ff/ipc/test_l088/test_supp_l088.c create mode 100644 api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_crypto.c create mode 100644 api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_crypto.h create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_crypto.c create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_crypto.h create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_crypto.c create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_crypto.h create mode 100755 api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/reference_logs/attestation.txt create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/reference_logs/crypto.txt create mode 100755 api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/reference_logs/protected_storage.txt create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/Makefile create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/manifests/common/driver_partition_psa.json create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/manifests/ipc/client_partition_psa.json create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/manifests/ipc/server_partition_psa.json create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_client_api_empty_intf.c create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_client_api_intf.c create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_client_api_intf.h create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_common.h create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_config.h create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_driver_ipc_intf.c create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_driver_ns_intf.c create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_config.h create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_config_check.h create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_empty_intf.c create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_intf.c create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_intf.h create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_crypto.c create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_crypto.h create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_eat.c create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_eat.h create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_empty_intf.c create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_intf.c create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_intf.h create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/internal_trusted_storage/pal_internal_trusted_storage_empty_intf.c create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/internal_trusted_storage/pal_internal_trusted_storage_intf.c create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/internal_trusted_storage/pal_internal_trusted_storage_intf.h create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/protected_storage/pal_protected_storage_empty_intf.c create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/protected_storage/pal_protected_storage_intf.c create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/protected_storage/pal_protected_storage_intf.h create mode 100755 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/reference_logs/attestation.txt create mode 100755 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/reference_logs/crypto.txt create mode 100755 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/reference_logs/protected_storage.txt create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/spe/pal_driver_intf.c create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/spe/pal_driver_intf.h create mode 100644 api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/target.cfg create mode 100644 api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_crypto.c create mode 100644 api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_crypto.h delete mode 100755 api-tests/tools/scripts/targetConfigGen.pl create mode 100644 api-tests/tools/scripts/targetConfigGen.py diff --git a/api-specs/include/psa/crypto.h b/api-specs/include/psa/crypto.h index 4c5c3495..c8a1c18f 100644 --- a/api-specs/include/psa/crypto.h +++ b/api-specs/include/psa/crypto.h @@ -193,7 +193,7 @@ psa_algorithm_t psa_key_policy_get_algorithm(const psa_key_policy_t *policy); * the policy has been saved to persistent storage. Implementations * may defer saving the policy until the key material is created. * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_OCCUPIED_SLOT + * \retval #PSA_ERROR_ALREADY_EXISTS * \retval #PSA_ERROR_NOT_SUPPORTED * \retval #PSA_ERROR_INVALID_ARGUMENT * \retval #PSA_ERROR_COMMUNICATION_FAILURE @@ -285,7 +285,7 @@ psa_status_t psa_allocate_key(psa_key_handle_t *handle); * Success. The application can now use the value of `*handle` * to access the newly allocated key slot. * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * \retval #PSA_ERROR_EMPTY_SLOT + * \retval #PSA_ERROR_DOES_NOT_EXIST * \retval #PSA_ERROR_INVALID_ARGUMENT * \p lifetime is invalid, for example #PSA_KEY_LIFETIME_VOLATILE. * \retval #PSA_ERROR_INVALID_ARGUMENT @@ -322,7 +322,7 @@ psa_status_t psa_open_key(psa_key_lifetime_t lifetime, * to access the newly allocated key slot. * \retval #PSA_ERROR_INSUFFICIENT_MEMORY * \retval #PSA_ERROR_INSUFFICIENT_STORAGE - * \retval #PSA_ERROR_OCCUPIED_SLOT + * \retval #PSA_ERROR_ALREADY_EXISTS * There is already a key with the identifier \p id in the storage * area designated by \p lifetime. * \retval #PSA_ERROR_INVALID_ARGUMENT @@ -348,9 +348,6 @@ psa_status_t psa_create_key(psa_key_lifetime_t lifetime, * with the key in volatile memory. The key slot in persistent storage is * not affected and can be opened again later with psa_open_key(). * - * If the key is currently in use in a multipart operation, - * the multipart operation is aborted. - * * \param handle The key handle to close. * * \retval #PSA_SUCCESS @@ -404,7 +401,7 @@ psa_status_t psa_close_key(psa_key_handle_t handle); * \retval #PSA_ERROR_INVALID_ARGUMENT * The key slot is invalid, * or the key data is not correctly formatted. - * \retval #PSA_ERROR_OCCUPIED_SLOT + * \retval #PSA_ERROR_ALREADY_EXISTS * There is already a key in the specified slot. * \retval #PSA_ERROR_INSUFFICIENT_MEMORY * \retval #PSA_ERROR_INSUFFICIENT_STORAGE @@ -433,9 +430,6 @@ psa_status_t psa_import_key(psa_key_handle_t handle, * This function also erases any metadata such as policies and frees all * resources associated with the key. * - * If the key is currently in use in a multipart operation, - * the multipart operation is aborted. - * * \param handle Handle to the key slot to erase. * * \retval #PSA_SUCCESS @@ -476,7 +470,7 @@ psa_status_t psa_destroy_key(psa_key_handle_t handle); * * \retval #PSA_SUCCESS * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_EMPTY_SLOT + * \retval #PSA_ERROR_DOES_NOT_EXIST * The handle is to a key slot which does not contain key material yet. * \retval #PSA_ERROR_COMMUNICATION_FAILURE * \retval #PSA_ERROR_HARDWARE_FAILURE @@ -490,106 +484,6 @@ psa_status_t psa_get_key_information(psa_key_handle_t handle, psa_key_type_t *type, size_t *bits); -/** - * \brief Set domain parameters for a key. - * - * Some key types require additional domain parameters to be set before import - * or generation of the key. The domain parameters can be set with this - * function or, for key generation, through the \c extra parameter of - * psa_generate_key(). - * - * The format for the required domain parameters varies by the key type. - * - For DSA public keys (#PSA_KEY_TYPE_DSA_PUBLIC_KEY), - * the `Dss-Parms` format as defined by RFC 3279 §2.3.2. - * ``` - * Dss-Parms ::= SEQUENCE { - * p INTEGER, - * q INTEGER, - * g INTEGER - * } - * ``` - * - For Diffie-Hellman key exchange keys (#PSA_KEY_TYPE_DH_PUBLIC_KEY), the - * `DomainParameters` format as defined by RFC 3279 §2.3.3. - * ``` - * DomainParameters ::= SEQUENCE { - * p INTEGER, -- odd prime, p=jq +1 - * g INTEGER, -- generator, g - * q INTEGER, -- factor of p-1 - * j INTEGER OPTIONAL, -- subgroup factor - * validationParms ValidationParms OPTIONAL - * } - * ValidationParms ::= SEQUENCE { - * seed BIT STRING, - * pgenCounter INTEGER - * } - * ``` - * - * \param handle Handle to the slot where the key will be stored. - * This must be a valid slot for a key of the chosen - * type: it must have been obtained by calling - * psa_allocate_key() or psa_create_key() with the - * correct \p type and with a maximum size that is - * compatible with \p data. It must not contain - * key material yet. - * \param type Key type (a \c PSA_KEY_TYPE_XXX value). When - * subsequently creating key material into \p handle, - * the type must be compatible. - * \param[in] data Buffer containing the key domain parameters. The content - * of this buffer is interpreted according to \p type. of - * psa_export_key() or psa_export_public_key() for the - * chosen type. - * \param data_length Size of the \p data buffer in bytes. - * - * \retval #PSA_SUCCESS - * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_OCCUPIED_SLOT - * There is already a key in the specified slot. - * \retval #PSA_ERROR_INVALID_ARGUMENT - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - * \retval #PSA_ERROR_BAD_STATE - * The library has not been previously initialized by psa_crypto_init(). - * It is implementation-dependent whether a failure to initialize - * results in this error code. - */ -psa_status_t psa_set_key_domain_parameters(psa_key_handle_t handle, - psa_key_type_t type, - const uint8_t *data, - size_t data_length); - -/** - * \brief Get domain parameters for a key. - * - * Get the domain parameters for a key with this function, if any. The format - * of the domain parameters written to \p data is specified in the - * documentation for psa_set_key_domain_parameters(). - * - * \param handle Handle to the key to get domain parameters from. - * \param[out] data On success, the key domain parameters. - * \param data_size Size of the \p data buffer in bytes. - * \param[out] data_length On success, the number of bytes - * that make up the key domain parameters data. - * - * \retval #PSA_SUCCESS - * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_EMPTY_SLOT - * There is no key in the specified slot. - * \retval #PSA_ERROR_INVALID_ARGUMENT - * \retval #PSA_ERROR_NOT_SUPPORTED - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - * \retval #PSA_ERROR_BAD_STATE - * The library has not been previously initialized by psa_crypto_init(). - * It is implementation-dependent whether a failure to initialize - * results in this error code. - */ -psa_status_t psa_get_key_domain_parameters(psa_key_handle_t handle, - uint8_t *data, - size_t data_size, - size_t *data_length); - /** * \brief Export a key in binary format. * @@ -625,10 +519,19 @@ psa_status_t psa_get_key_domain_parameters(psa_key_handle_t handle, * coefficient INTEGER, -- (inverse of q) mod p * } * ``` - * - For DSA private keys (#PSA_KEY_TYPE_DSA_KEYPAIR), the format is the - * representation of the private key `x` as a big-endian byte string. The - * length of the byte string is the private key size in bytes (leading zeroes - * are not stripped). + * - For DSA private keys (#PSA_KEY_TYPE_DSA_KEYPAIR), the format + * is the non-encrypted DER encoding of the representation used by + * OpenSSL and OpenSSH, whose structure is described in ASN.1 as follows: + * ``` + * DSAPrivateKey ::= SEQUENCE { + * version INTEGER, -- must be 0 + * prime INTEGER, -- p + * subprime INTEGER, -- q + * generator INTEGER, -- g + * public INTEGER, -- y + * private INTEGER, -- x + * } + * ``` * - For elliptic curve key pairs (key types for which * #PSA_KEY_TYPE_IS_ECC_KEYPAIR is true), the format is * a representation of the private value as a `ceiling(m/8)`-byte string @@ -640,10 +543,6 @@ psa_status_t psa_get_key_domain_parameters(psa_key_handle_t handle, * and `PSA_ECC_CURVE_BRAINPOOL_PXXX`). * This is the content of the `privateKey` field of the `ECPrivateKey` * format defined by RFC 5915. - * - For Diffie-Hellman key exchange key pairs (#PSA_KEY_TYPE_DH_KEYPAIR), the - * format is the representation of the private key `x` as a big-endian byte - * string. The length of the byte string is the private key size in bytes - * (leading zeroes are not stripped). * - For public keys (key types for which #PSA_KEY_TYPE_IS_PUBLIC_KEY is * true), the format is the same as for psa_export_public_key(). * @@ -655,7 +554,7 @@ psa_status_t psa_get_key_domain_parameters(psa_key_handle_t handle, * * \retval #PSA_SUCCESS * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_EMPTY_SLOT + * \retval #PSA_ERROR_DOES_NOT_EXIST * \retval #PSA_ERROR_NOT_PERMITTED * \retval #PSA_ERROR_NOT_SUPPORTED * \retval #PSA_ERROR_BUFFER_TOO_SMALL @@ -700,20 +599,39 @@ psa_status_t psa_export_key(psa_key_handle_t handle, * ``` * - For elliptic curve public keys (key types for which * #PSA_KEY_TYPE_IS_ECC_PUBLIC_KEY is true), the format is the uncompressed - * representation defined by SEC1 §2.3.3 as the content of an ECPoint. + * representation defined by SEC1 §2.3.3 as the content of an ECPoint: * Let `m` be the bit size associated with the curve, i.e. the bit size of * `q` for a curve over `F_q`. The representation consists of: * - The byte 0x04; * - `x_P` as a `ceiling(m/8)`-byte string, big-endian; * - `y_P` as a `ceiling(m/8)`-byte string, big-endian. - * - For DSA public keys (#PSA_KEY_TYPE_DSA_PUBLIC_KEY), the format is the - * representation of the public key `y = g^x mod p` as a big-endian byte - * string. The length of the byte string is the length of the base prime `p` - * in bytes. - * - For Diffie-Hellman key exchange public keys (#PSA_KEY_TYPE_DH_PUBLIC_KEY), - * the format is the representation of the public key `y = g^x mod p` as a - * big-endian byte string. The length of the byte string is the length of the - * base prime `p` in bytes. + * + * For other public key types, the format is the DER representation defined by + * RFC 5280 as `SubjectPublicKeyInfo`, with the `subjectPublicKey` format + * specified below. + * ``` + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL } + * ``` + * - For DSA public keys (#PSA_KEY_TYPE_DSA_PUBLIC_KEY), + * the `subjectPublicKey` format is defined by RFC 3279 §2.3.2 as + * `DSAPublicKey`, + * with the OID `id-dsa`, + * and with the parameters `DSS-Parms`. + * ``` + * id-dsa OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 1 } + * + * Dss-Parms ::= SEQUENCE { + * p INTEGER, + * q INTEGER, + * g INTEGER } + * DSAPublicKey ::= INTEGER -- public key, Y + * ``` * * \param handle Handle to the key to export. * \param[out] data Buffer where the key data is to be written. @@ -723,7 +641,7 @@ psa_status_t psa_export_key(psa_key_handle_t handle, * * \retval #PSA_SUCCESS * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_EMPTY_SLOT + * \retval #PSA_ERROR_DOES_NOT_EXIST * \retval #PSA_ERROR_INVALID_ARGUMENT * The key is neither a public key nor a key pair. * \retval #PSA_ERROR_NOT_SUPPORTED @@ -750,11 +668,12 @@ psa_status_t psa_export_public_key(psa_key_handle_t handle, * * Copy key material from one location to another. * - * This function is primarily useful to copy a key from one lifetime - * to another. The target key retains its lifetime and location. + * This function is primarily useful to copy a key from one location + * to another, since it populates a key using the material from + * another key which may have a different lifetime. * * In an implementation where slots have different ownerships, - * this functin may be used to share a key with a different party, + * this function may be used to share a key with a different party, * subject to implementation-defined restrictions on key sharing. * In this case \p constraint would typically prevent the recipient * from exporting the key. @@ -791,9 +710,9 @@ psa_status_t psa_export_public_key(psa_key_handle_t handle, * * \retval #PSA_SUCCESS * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_OCCUPIED_SLOT + * \retval #PSA_ERROR_ALREADY_EXISTS * \p target already contains key material. - * \retval #PSA_ERROR_EMPTY_SLOT + * \retval #PSA_ERROR_DOES_NOT_EXIST * \p source does not contain key material. * \retval #PSA_ERROR_INVALID_ARGUMENT * The policy constraints on the source, on the target and @@ -817,66 +736,6 @@ psa_status_t psa_copy_key(psa_key_handle_t source_handle, * @{ */ -/** Calculate the hash (digest) of a message. - * - * \note To verify the hash of a message against an - * expected value, use psa_hash_compare() instead. - * - * \param alg The hash algorithm to compute (\c PSA_ALG_XXX value - * such that #PSA_ALG_IS_HASH(\p alg) is true). - * \param[in] input Buffer containing the message to hash. - * \param input_length Size of the \p input buffer in bytes. - * \param[out] hash Buffer where the hash is to be written. - * \param hash_size Size of the \p hash buffer in bytes. - * \param[out] hash_length On success, the number of bytes - * that make up the hash value. This is always - * #PSA_HASH_SIZE(\c alg) where \c alg is the - * hash algorithm that is calculated. - * - * \retval #PSA_SUCCESS - * Success. - * \retval #PSA_ERROR_NOT_SUPPORTED - * \p alg is not supported or is not a hash algorithm. - * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - */ -psa_status_t psa_hash_compute(psa_algorithm_t alg, - const uint8_t *input, - size_t input_length, - uint8_t *hash, - size_t hash_size, - size_t *hash_length); - -/** Calculate the hash (digest) of a message and compare it with a - * reference value. - * - * \param alg The hash algorithm to compute (\c PSA_ALG_XXX value - * such that #PSA_ALG_IS_HASH(\p alg) is true). - * \param[in] input Buffer containing the message to hash. - * \param input_length Size of the \p input buffer in bytes. - * \param[out] hash Buffer containing the expected hash value. - * \param hash_length Size of the \p hash buffer in bytes. - * - * \retval #PSA_SUCCESS - * The expected hash is identical to the actual hash of the input. - * \retval #PSA_ERROR_INVALID_SIGNATURE - * The hash of the message was calculated successfully, but it - * differs from the expected hash. - * \retval #PSA_ERROR_NOT_SUPPORTED - * \p alg is not supported or is not a hash algorithm. - * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - */ -psa_status_t psa_hash_compare(psa_algorithm_t alg, - const uint8_t *input, - size_t input_length, - const uint8_t *hash, - const size_t hash_length); - /** The type of the state data structure for multipart hash operations. * * Before calling any function on a hash operation object, the application must @@ -957,6 +816,9 @@ static psa_hash_operation_t psa_hash_operation_init(void); * Success. * \retval #PSA_ERROR_NOT_SUPPORTED * \p alg is not supported or is not a hash algorithm. + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (already set up and not + * subsequently completed). * \retval #PSA_ERROR_INSUFFICIENT_MEMORY * \retval #PSA_ERROR_COMMUNICATION_FAILURE * \retval #PSA_ERROR_HARDWARE_FAILURE @@ -1127,88 +989,6 @@ psa_status_t psa_hash_clone(const psa_hash_operation_t *source_operation, * @{ */ -/** Calculate the MAC (message authentication code) of a message. - * - * \note To verify the MAC of a message against an - * expected value, use psa_mac_verify() instead. - * Beware that comparing integrity or authenticity data such as - * MAC values with a function such as \c memcmp is risky - * because the time taken by the comparison may leak information - * about the MAC value which could allow an attacker to guess - * a valid MAC and thereby bypass security controls. - * - * \param handle Handle to the key to use for the operation. - * \param alg The MAC algorithm to compute (\c PSA_ALG_XXX value - * such that #PSA_ALG_IS_MAC(alg) is true). - * \param[in] input Buffer containing the input message. - * \param input_length Size of the \p input buffer in bytes. - * \param[out] mac Buffer where the MAC value is to be written. - * \param mac_size Size of the \p mac buffer in bytes. - * \param[out] mac_length On success, the number of bytes - * that make up the mac value. This is always - * #PSA_HASH_SIZE(\c alg) where \c alg is the - * hash algorithm that is calculated. - * - * \retval #PSA_SUCCESS - * Success. - * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_EMPTY_SLOT - * \retval #PSA_ERROR_NOT_PERMITTED - * \retval #PSA_ERROR_INVALID_ARGUMENT - * \p key is not compatible with \p alg. - * \retval #PSA_ERROR_NOT_SUPPORTED - * \p alg is not supported or is not a MAC algorithm. - * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - * \retval #PSA_ERROR_BAD_STATE - * The library has not been previously initialized by psa_crypto_init(). - * It is implementation-dependent whether a failure to initialize - * results in this error code. - */ -psa_status_t psa_mac_compute(psa_key_handle_t handle, - psa_algorithm_t alg, - const uint8_t *input, - size_t input_length, - uint8_t *mac, - size_t mac_size, - size_t *mac_length); - -/** Calculate the MAC of a message and compare it with a reference value. - * - * \param handle Handle to the key to use for the operation. - * \param alg The MAC algorithm to compute (\c PSA_ALG_XXX value - * such that #PSA_ALG_IS_MAC(alg) is true). - * \param[in] input Buffer containing the input message. - * \param input_length Size of the \p input buffer in bytes. - * \param[out] mac Buffer containing the expected MAC value. - * \param mac_length Size of the \p mac buffer in bytes. - * - * \retval #PSA_SUCCESS - * The expected MAC is identical to the actual MAC of the input. - * \retval #PSA_ERROR_INVALID_SIGNATURE - * The MAC of the message was calculated successfully, but it - * differs from the expected value. - * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_EMPTY_SLOT - * \retval #PSA_ERROR_NOT_PERMITTED - * \retval #PSA_ERROR_INVALID_ARGUMENT - * \p key is not compatible with \p alg. - * \retval #PSA_ERROR_NOT_SUPPORTED - * \p alg is not supported or is not a MAC algorithm. - * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - */ -psa_status_t psa_mac_verify(psa_key_handle_t handle, - psa_algorithm_t alg, - const uint8_t *input, - size_t input_length, - const uint8_t *mac, - const size_t mac_length); - /** The type of the state data structure for multipart MAC operations. * * Before calling any function on a MAC operation object, the application must @@ -1268,6 +1048,8 @@ static psa_mac_operation_t psa_mac_operation_init(void); * -# Initialize the operation object with one of the methods described in the * documentation for #psa_mac_operation_t, e.g. PSA_MAC_OPERATION_INIT. * -# Call psa_mac_sign_setup() to specify the algorithm and key. + * The key remains associated with the operation even if the content + * of the key slot changes. * -# Call psa_mac_update() zero, one or more times, passing a fragment * of the message each time. The MAC that is calculated is the MAC * of the concatenation of these messages in order. @@ -1286,15 +1068,13 @@ static psa_mac_operation_t psa_mac_operation_init(void); * been initialized as per the documentation for * #psa_mac_operation_t and not yet in use. * \param handle Handle to the key to use for the operation. - * It must remain valid until the operation - * terminates. * \param alg The MAC algorithm to compute (\c PSA_ALG_XXX value * such that #PSA_ALG_IS_MAC(alg) is true). * * \retval #PSA_SUCCESS * Success. * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_EMPTY_SLOT + * \retval #PSA_ERROR_DOES_NOT_EXIST * \retval #PSA_ERROR_NOT_PERMITTED * \retval #PSA_ERROR_INVALID_ARGUMENT * \p key is not compatible with \p alg. @@ -1305,6 +1085,9 @@ static psa_mac_operation_t psa_mac_operation_init(void); * \retval #PSA_ERROR_HARDWARE_FAILURE * \retval #PSA_ERROR_TAMPERING_DETECTED * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (already set up and not + * subsequently completed). + * \retval #PSA_ERROR_BAD_STATE * The library has not been previously initialized by psa_crypto_init(). * It is implementation-dependent whether a failure to initialize * results in this error code. @@ -1324,6 +1107,8 @@ psa_status_t psa_mac_sign_setup(psa_mac_operation_t *operation, * -# Initialize the operation object with one of the methods described in the * documentation for #psa_mac_operation_t, e.g. PSA_MAC_OPERATION_INIT. * -# Call psa_mac_verify_setup() to specify the algorithm and key. + * The key remains associated with the operation even if the content + * of the key slot changes. * -# Call psa_mac_update() zero, one or more times, passing a fragment * of the message each time. The MAC that is calculated is the MAC * of the concatenation of these messages in order. @@ -1343,15 +1128,13 @@ psa_status_t psa_mac_sign_setup(psa_mac_operation_t *operation, * been initialized as per the documentation for * #psa_mac_operation_t and not yet in use. * \param handle Handle to the key to use for the operation. - * It must remain valid until the operation - * terminates. * \param alg The MAC algorithm to compute (\c PSA_ALG_XXX value * such that #PSA_ALG_IS_MAC(\p alg) is true). * * \retval #PSA_SUCCESS * Success. * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_EMPTY_SLOT + * \retval #PSA_ERROR_DOES_NOT_EXIST * \retval #PSA_ERROR_NOT_PERMITTED * \retval #PSA_ERROR_INVALID_ARGUMENT * \c key is not compatible with \c alg. @@ -1362,6 +1145,9 @@ psa_status_t psa_mac_sign_setup(psa_mac_operation_t *operation, * \retval #PSA_ERROR_HARDWARE_FAILURE * \retval #PSA_ERROR_TAMPERING_DETECTED * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (already set up and not + * subsequently completed). + * \retval #PSA_ERROR_BAD_STATE * The library has not been previously initialized by psa_crypto_init(). * It is implementation-dependent whether a failure to initialize * results in this error code. @@ -1509,91 +1295,6 @@ psa_status_t psa_mac_abort(psa_mac_operation_t *operation); * @{ */ -/** Encrypt a message using a symmetric cipher. - * - * This function encrypts a message with a random IV (initialization - * vector). - * - * \param handle Handle to the key to use for the operation. - * It must remain valid until the operation - * terminates. - * \param alg The cipher algorithm to compute - * (\c PSA_ALG_XXX value such that - * #PSA_ALG_IS_CIPHER(\p alg) is true). - * \param[in] input Buffer containing the message to encrypt. - * \param input_length Size of the \p input buffer in bytes. - * \param[out] output Buffer where the output is to be written. - * The output contains the IV followed by - * the ciphertext proper. - * \param output_size Size of the \p output buffer in bytes. - * \param[out] output_length On success, the number of bytes - * that make up the output. - * - * \retval #PSA_SUCCESS - * Success. - * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_EMPTY_SLOT - * \retval #PSA_ERROR_NOT_PERMITTED - * \retval #PSA_ERROR_INVALID_ARGUMENT - * \p key is not compatible with \p alg. - * \retval #PSA_ERROR_NOT_SUPPORTED - * \p alg is not supported or is not a cipher algorithm. - * \retval #PSA_ERROR_BUFFER_TOO_SMALL - * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - */ -psa_status_t psa_cipher_encrypt(psa_key_handle_t handle, - psa_algorithm_t alg, - const uint8_t *input, - size_t input_length, - uint8_t *output, - size_t output_size, - size_t *output_length); - -/** Decrypt a message using a symmetric cipher. - * - * This function decrypts a message encrypted with a symmetric cipher. - * - * \param handle Handle to the key to use for the operation. - * It must remain valid until the operation - * terminates. - * \param alg The cipher algorithm to compute - * (\c PSA_ALG_XXX value such that - * #PSA_ALG_IS_CIPHER(\p alg) is true). - * \param[in] input Buffer containing the message to decrypt. - * This consists of the IV followed by the - * ciphertext proper. - * \param input_length Size of the \p input buffer in bytes. - * \param[out] output Buffer where the plaintext is to be written. - * \param output_size Size of the \p output buffer in bytes. - * \param[out] output_length On success, the number of bytes - * that make up the output. - * - * \retval #PSA_SUCCESS - * Success. - * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_EMPTY_SLOT - * \retval #PSA_ERROR_NOT_PERMITTED - * \retval #PSA_ERROR_INVALID_ARGUMENT - * \p key is not compatible with \p alg. - * \retval #PSA_ERROR_NOT_SUPPORTED - * \p alg is not supported or is not a cipher algorithm. - * \retval #PSA_ERROR_BUFFER_TOO_SMALL - * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - */ -psa_status_t psa_cipher_decrypt(psa_key_handle_t handle, - psa_algorithm_t alg, - const uint8_t *input, - size_t input_length, - uint8_t *output, - size_t output_size, - size_t *output_length); - /** The type of the state data structure for multipart cipher operations. * * Before calling any function on a cipher operation object, the application @@ -1650,6 +1351,8 @@ static psa_cipher_operation_t psa_cipher_operation_init(void); * documentation for #psa_cipher_operation_t, e.g. * PSA_CIPHER_OPERATION_INIT. * -# Call psa_cipher_encrypt_setup() to specify the algorithm and key. + * The key remains associated with the operation even if the content + * of the key slot changes. * -# Call either psa_cipher_generate_iv() or psa_cipher_set_iv() to * generate or set the IV (initialization vector). You should use * psa_cipher_generate_iv() unless the protocol you are implementing @@ -1664,15 +1367,14 @@ static psa_cipher_operation_t psa_cipher_operation_init(void); * After a successful call to psa_cipher_encrypt_setup(), the application must * eventually terminate the operation. The following events terminate an * operation: - * - A failed call to any of the \c psa_cipher_xxx functions. + * - A failed call to psa_cipher_generate_iv(), psa_cipher_set_iv() + * or psa_cipher_update(). * - A call to psa_cipher_finish() or psa_cipher_abort(). * * \param[in,out] operation The operation object to set up. It must have * been initialized as per the documentation for * #psa_cipher_operation_t and not yet in use. * \param handle Handle to the key to use for the operation. - * It must remain valid until the operation - * terminates. * \param alg The cipher algorithm to compute * (\c PSA_ALG_XXX value such that * #PSA_ALG_IS_CIPHER(\p alg) is true). @@ -1680,7 +1382,7 @@ static psa_cipher_operation_t psa_cipher_operation_init(void); * \retval #PSA_SUCCESS * Success. * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_EMPTY_SLOT + * \retval #PSA_ERROR_DOES_NOT_EXIST * \retval #PSA_ERROR_NOT_PERMITTED * \retval #PSA_ERROR_INVALID_ARGUMENT * \p key is not compatible with \p alg. @@ -1691,6 +1393,9 @@ static psa_cipher_operation_t psa_cipher_operation_init(void); * \retval #PSA_ERROR_HARDWARE_FAILURE * \retval #PSA_ERROR_TAMPERING_DETECTED * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (already set up and not + * subsequently completed). + * \retval #PSA_ERROR_BAD_STATE * The library has not been previously initialized by psa_crypto_init(). * It is implementation-dependent whether a failure to initialize * results in this error code. @@ -1709,7 +1414,9 @@ psa_status_t psa_cipher_encrypt_setup(psa_cipher_operation_t *operation, * documentation for #psa_cipher_operation_t, e.g. * PSA_CIPHER_OPERATION_INIT. * -# Call psa_cipher_decrypt_setup() to specify the algorithm and key. - * -# Call psa_cipher_set_iv() with the IV (initialization vector) for the + * The key remains associated with the operation even if the content + * of the key slot changes. + * -# Call psa_cipher_update() with the IV (initialization vector) for the * decryption. If the IV is prepended to the ciphertext, you can call * psa_cipher_update() on a buffer containing the IV followed by the * beginning of the message. @@ -1723,15 +1430,13 @@ psa_status_t psa_cipher_encrypt_setup(psa_cipher_operation_t *operation, * After a successful call to psa_cipher_decrypt_setup(), the application must * eventually terminate the operation. The following events terminate an * operation: - * - A failed call to any of the \c psa_cipher_xxx functions. + * - A failed call to psa_cipher_update(). * - A call to psa_cipher_finish() or psa_cipher_abort(). * * \param[in,out] operation The operation object to set up. It must have * been initialized as per the documentation for * #psa_cipher_operation_t and not yet in use. * \param handle Handle to the key to use for the operation. - * It must remain valid until the operation - * terminates. * \param alg The cipher algorithm to compute * (\c PSA_ALG_XXX value such that * #PSA_ALG_IS_CIPHER(\p alg) is true). @@ -1739,7 +1444,7 @@ psa_status_t psa_cipher_encrypt_setup(psa_cipher_operation_t *operation, * \retval #PSA_SUCCESS * Success. * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_EMPTY_SLOT + * \retval #PSA_ERROR_DOES_NOT_EXIST * \retval #PSA_ERROR_NOT_PERMITTED * \retval #PSA_ERROR_INVALID_ARGUMENT * \p key is not compatible with \p alg. @@ -1750,6 +1455,9 @@ psa_status_t psa_cipher_encrypt_setup(psa_cipher_operation_t *operation, * \retval #PSA_ERROR_HARDWARE_FAILURE * \retval #PSA_ERROR_TAMPERING_DETECTED * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (already set up and not + * subsequently completed). + * \retval #PSA_ERROR_BAD_STATE * The library has not been previously initialized by psa_crypto_init(). * It is implementation-dependent whether a failure to initialize * results in this error code. @@ -1793,7 +1501,7 @@ psa_status_t psa_cipher_generate_iv(psa_cipher_operation_t *operation, /** Set the IV for a symmetric encryption or decryption operation. * - * This function sets the IV (initialization vector), nonce + * This function sets the random IV (initialization vector), nonce * or initial counter value for the encryption or decryption operation. * * The application must call psa_cipher_encrypt_setup() before @@ -1967,7 +1675,7 @@ psa_status_t psa_cipher_abort(psa_cipher_operation_t *operation); * \retval #PSA_SUCCESS * Success. * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_EMPTY_SLOT + * \retval #PSA_ERROR_DOES_NOT_EXIST * \retval #PSA_ERROR_NOT_PERMITTED * \retval #PSA_ERROR_INVALID_ARGUMENT * \p key is not compatible with \p alg. @@ -2023,7 +1731,7 @@ psa_status_t psa_aead_encrypt(psa_key_handle_t handle, * \retval #PSA_SUCCESS * Success. * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_EMPTY_SLOT + * \retval #PSA_ERROR_DOES_NOT_EXIST * \retval #PSA_ERROR_INVALID_SIGNATURE * The ciphertext is not authentic. * \retval #PSA_ERROR_NOT_PERMITTED @@ -2052,529 +1760,20 @@ psa_status_t psa_aead_decrypt(psa_key_handle_t handle, size_t plaintext_size, size_t *plaintext_length); -/** The type of the state data structure for multipart AEAD operations. - * - * Before calling any function on an AEAD operation object, the application - * must initialize it by any of the following means: - * - Set the structure to all-bits-zero, for example: - * \code - * psa_aead_operation_t operation; - * memset(&operation, 0, sizeof(operation)); - * \endcode - * - Initialize the structure to logical zero values, for example: - * \code - * psa_aead_operation_t operation = {0}; - * \endcode - * - Initialize the structure to the initializer #PSA_AEAD_OPERATION_INIT, - * for example: - * \code - * psa_aead_operation_t operation = PSA_AEAD_OPERATION_INIT; - * \endcode - * - Assign the result of the function psa_aead_operation_init() - * to the structure, for example: - * \code - * psa_aead_operation_t operation; - * operation = psa_aead_operation_init(); - * \endcode - * - * This is an implementation-defined \c struct. Applications should not - * make any assumptions about the content of this structure except - * as directed by the documentation of a specific implementation. */ -typedef struct psa_aead_operation_s psa_aead_operation_t; - -/** \def PSA_AEAD_OPERATION_INIT - * - * This macro returns a suitable initializer for an AEAD operation object of - * type #psa_aead_operation_t. - */ -#ifdef __DOXYGEN_ONLY__ -/* This is an example definition for documentation purposes. - * Implementations should define a suitable value in `crypto_struct.h`. - */ -#define PSA_AEAD_OPERATION_INIT {0} -#endif +/**@}*/ -/** Return an initial value for an AEAD operation object. +/** \defgroup asymmetric Asymmetric cryptography + * @{ */ -static psa_aead_operation_t psa_aead_operation_init(void); -/** Set the key for a multipart authenticated encryption operation. - * - * The sequence of operations to encrypt a message with authentication - * is as follows: - * -# Allocate an operation object which will be passed to all the functions - * listed here. - * -# Initialize the operation object with one of the methods described in the - * documentation for #psa_aead_operation_t, e.g. - * PSA_AEAD_OPERATION_INIT. - * -# Call psa_aead_encrypt_setup() to specify the algorithm and key. - * -# If needed, call psa_aead_set_lengths() to specify the length of the - * inputs to the subsequent calls to psa_aead_update_ad() and - * psa_aead_update(). See the documentation of psa_aead_set_lengths() - * for details. - * -# Call either psa_aead_generate_nonce() or psa_aead_set_nonce() to - * generate or set the nonce. You should use - * psa_aead_generate_nonce() unless the protocol you are implementing - * requires a specific nonce value. - * -# Call psa_aead_update_ad() zero, one or more times, passing a fragment - * of the non-encrypted additional authenticated data each time. - * -# Call psa_aead_update() zero, one or more times, passing a fragment - * of the message to encrypt each time. - * -# Call psa_aead_finish(). - * - * The application may call psa_aead_abort() at any time after the operation - * has been initialized. +/** + * \brief Sign a hash or short message with a private key. * - * After a successful call to psa_aead_encrypt_setup(), the application must - * eventually terminate the operation. The following events terminate an - * operation: - * - A failed call to any of the \c psa_aead_xxx functions. - * - A call to psa_aead_finish(), psa_aead_verify() or psa_aead_abort(). - * - * \param[in,out] operation The operation object to set up. It must have - * been initialized as per the documentation for - * #psa_aead_operation_t and not yet in use. - * \param handle Handle to the key to use for the operation. - * It must remain valid until the operation - * terminates. - * \param alg The AEAD algorithm to compute - * (\c PSA_ALG_XXX value such that - * #PSA_ALG_IS_AEAD(\p alg) is true). - * - * \retval #PSA_SUCCESS - * Success. - * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_EMPTY_SLOT - * \retval #PSA_ERROR_NOT_PERMITTED - * \retval #PSA_ERROR_INVALID_ARGUMENT - * \p key is not compatible with \p alg. - * \retval #PSA_ERROR_NOT_SUPPORTED - * \p alg is not supported or is not an AEAD algorithm. - * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - * \retval #PSA_ERROR_BAD_STATE - * The library has not been previously initialized by psa_crypto_init(). - * It is implementation-dependent whether a failure to initialize - * results in this error code. - */ -psa_status_t psa_aead_encrypt_setup(psa_aead_operation_t *operation, - psa_key_handle_t handle, - psa_algorithm_t alg); - -/** Set the key for a multipart authenticated decryption operation. - * - * The sequence of operations to decrypt a message with authentication - * is as follows: - * -# Allocate an operation object which will be passed to all the functions - * listed here. - * -# Initialize the operation object with one of the methods described in the - * documentation for #psa_aead_operation_t, e.g. - * PSA_AEAD_OPERATION_INIT. - * -# Call psa_aead_decrypt_setup() to specify the algorithm and key. - * -# If needed, call psa_aead_set_lengths() to specify the length of the - * inputs to the subsequent calls to psa_aead_update_ad() and - * psa_aead_update(). See the documentation of psa_aead_set_lengths() - * for details. - * -# Call psa_aead_set_nonce() with the nonce for the decryption. - * -# Call psa_aead_update_ad() zero, one or more times, passing a fragment - * of the non-encrypted additional authenticated data each time. - * -# Call psa_aead_update() zero, one or more times, passing a fragment - * of the ciphertext to decrypt each time. - * -# Call psa_aead_verify(). - * - * The application may call psa_aead_abort() at any time after the operation - * has been initialized. - * - * After a successful call to psa_aead_decrypt_setup(), the application must - * eventually terminate the operation. The following events terminate an - * operation: - * - A failed call to any of the \c psa_aead_xxx functions. - * - A call to psa_aead_finish(), psa_aead_verify() or psa_aead_abort(). - * - * \param[in,out] operation The operation object to set up. It must have - * been initialized as per the documentation for - * #psa_aead_operation_t and not yet in use. - * \param handle Handle to the key to use for the operation. - * It must remain valid until the operation - * terminates. - * \param alg The AEAD algorithm to compute - * (\c PSA_ALG_XXX value such that - * #PSA_ALG_IS_AEAD(\p alg) is true). - * - * \retval #PSA_SUCCESS - * Success. - * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_EMPTY_SLOT - * \retval #PSA_ERROR_NOT_PERMITTED - * \retval #PSA_ERROR_INVALID_ARGUMENT - * \p key is not compatible with \p alg. - * \retval #PSA_ERROR_NOT_SUPPORTED - * \p alg is not supported or is not an AEAD algorithm. - * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - * \retval #PSA_ERROR_BAD_STATE - * The library has not been previously initialized by psa_crypto_init(). - * It is implementation-dependent whether a failure to initialize - * results in this error code. - */ -psa_status_t psa_aead_decrypt_setup(psa_aead_operation_t *operation, - psa_key_handle_t handle, - psa_algorithm_t alg); - -/** Generate a random nonce for an authenticated encryption operation. - * - * This function generates a random nonce for the authenticated encryption - * operation with an appropriate size for the chosen algorithm, key type - * and key size. - * - * The application must call psa_aead_encrypt_setup() before - * calling this function. - * - * If this function returns an error status, the operation becomes inactive. - * - * \param[in,out] operation Active AEAD operation. - * \param[out] nonce Buffer where the generated nonce is to be - * written. - * \param nonce_size Size of the \p nonce buffer in bytes. - * \param[out] nonce_length On success, the number of bytes of the - * generated nonce. - * - * \retval #PSA_SUCCESS - * Success. - * \retval #PSA_ERROR_BAD_STATE - * The operation state is not valid (not set up, or nonce already set). - * \retval #PSA_ERROR_BUFFER_TOO_SMALL - * The size of the \p nonce buffer is too small. - * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - */ -psa_status_t psa_aead_generate_nonce(psa_aead_operation_t *operation, - unsigned char *nonce, - size_t nonce_size, - size_t *nonce_length); - -/** Set the nonce for an authenticated encryption or decryption operation. - * - * This function sets the nonce for the authenticated - * encryption or decryption operation. - * - * The application must call psa_aead_encrypt_setup() before - * calling this function. - * - * If this function returns an error status, the operation becomes inactive. - * - * \note When encrypting, applications should use psa_aead_generate_nonce() - * instead of this function, unless implementing a protocol that requires - * a non-random IV. - * - * \param[in,out] operation Active AEAD operation. - * \param[in] nonce Buffer containing the nonce to use. - * \param nonce_length Size of the nonce in bytes. - * - * \retval #PSA_SUCCESS - * Success. - * \retval #PSA_ERROR_BAD_STATE - * The operation state is not valid (not set up, or nonce already set). - * \retval #PSA_ERROR_INVALID_ARGUMENT - * The size of \p nonce is not acceptable for the chosen algorithm. - * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - */ -psa_status_t psa_aead_set_nonce(psa_aead_operation_t *operation, - const unsigned char *nonce, - size_t nonce_length); - -/** Declare the lengths of the message and additional data for AEAD. - * - * The application must call this function before calling - * psa_aead_update_ad() or psa_aead_update() if the algorithm for - * the operation requires it. If the algorithm does not require it, - * calling this function is optional, but if this function is called - * then the implementation must enforce the lengths. - * - * You may call this function before or after setting the nonce with - * psa_aead_set_nonce() or psa_aead_generate_nonce(). - * - * - For #PSA_ALG_CCM, calling this function is required. - * - For the other AEAD algorithms defined in this specification, calling - * this function is not required. - * - For vendor-defined algorithm, refer to the vendor documentation. - * - * \param[in,out] operation Active AEAD operation. - * \param ad_length Size of the non-encrypted additional - * authenticated data in bytes. - * \param plaintext_length Size of the plaintext to encrypt in bytes. - * - * \retval #PSA_SUCCESS - * Success. - * \retval #PSA_ERROR_BAD_STATE - * The operation state is not valid (not set up, already completed, - * or psa_aead_update_ad() or psa_aead_update() already called). - * \retval #PSA_ERROR_INVALID_ARGUMENT - * At least one of the lengths is not acceptable for the chosen - * algorithm. - * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - */ -psa_status_t psa_aead_set_lengths(psa_aead_operation_t *operation, - size_t ad_length, - size_t plaintext_length); - -/** Pass additional data to an active AEAD operation. - * - * Additional data is authenticated, but not encrypted. - * - * You may call this function multiple times to pass successive fragments - * of the additional data. You may not call this function after passing - * data to encrypt or decrypt with psa_aead_update(). - * - * Before calling this function, you must: - * 1. Call either psa_aead_encrypt_setup() or psa_aead_decrypt_setup(). - * 2. Set the nonce with psa_aead_generate_nonce() or psa_aead_set_nonce(). - * - * If this function returns an error status, the operation becomes inactive. - * - * \warning When decrypting, until psa_aead_verify() has returned #PSA_SUCCESS, - * there is no guarantee that the input is valid. Therefore, until - * you have called psa_aead_verify() and it has returned #PSA_SUCCESS, - * treat the input as untrusted and prepare to undo any action that - * depends on the input if psa_aead_verify() returns an error status. - * - * \param[in,out] operation Active AEAD operation. - * \param[in] input Buffer containing the fragment of - * additional data. - * \param input_length Size of the \p input buffer in bytes. - * - * \retval #PSA_SUCCESS - * Success. - * \retval #PSA_ERROR_BAD_STATE - * The operation state is not valid (not set up, nonce not set, - * psa_aead_update() already called, or operation already completed). - * \retval #PSA_ERROR_INVALID_ARGUMENT - * The total input length overflows the additional data length that - * was previously specified with psa_aead_set_lengths(). - * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - */ -psa_status_t psa_aead_update_ad(psa_aead_operation_t *operation, - const uint8_t *input, - size_t input_length); - -/** Encrypt or decrypt a message fragment in an active AEAD operation. - * - * Before calling this function, you must: - * 1. Call either psa_aead_encrypt_setup() or psa_aead_decrypt_setup(). - * The choice of setup function determines whether this function - * encrypts or decrypts its input. - * 2. Set the nonce with psa_aead_generate_nonce() or psa_aead_set_nonce(). - * 3. Call psa_aead_update_ad() to pass all the additional data. - * - * If this function returns an error status, the operation becomes inactive. - * - * \warning When decrypting, until psa_aead_verify() has returned #PSA_SUCCESS, - * there is no guarantee that the input is valid. Therefore, until - * you have called psa_aead_verify() and it has returned #PSA_SUCCESS: - * - Do not use the output in any way other than storing it in a - * confidential location. If you take any action that depends - * on the tentative decrypted data, this action will need to be - * undone if the input turns out not to be valid. Furthermore, - * if an adversary can observe that this action took place - * (for example through timing), they may be able to use this - * fact as an oracle to decrypt any message encrypted with the - * same key. - * - In particular, do not copy the output anywhere but to a - * memory or storage space that you have exclusive access to. - * - * \param[in,out] operation Active AEAD operation. - * \param[in] input Buffer containing the message fragment to - * encrypt or decrypt. - * \param input_length Size of the \p input buffer in bytes. - * \param[out] output Buffer where the output is to be written. - * \param output_size Size of the \p output buffer in bytes. - * \param[out] output_length On success, the number of bytes - * that make up the returned output. - * - * \retval #PSA_SUCCESS - * Success. - * \retval #PSA_ERROR_BAD_STATE - * The operation state is not valid (not set up, nonce not set - * or already completed). - * \retval #PSA_ERROR_BUFFER_TOO_SMALL - * The size of the \p output buffer is too small. - * \retval #PSA_ERROR_INVALID_ARGUMENT - * The total length of input to psa_aead_update_ad() so far is - * less than the additional data length that was previously - * specified with psa_aead_set_lengths(). - * \retval #PSA_ERROR_INVALID_ARGUMENT - * The total input length overflows the plaintext length that - * was previously specified with psa_aead_set_lengths(). - * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - */ -psa_status_t psa_aead_update(psa_aead_operation_t *operation, - const uint8_t *input, - size_t input_length, - unsigned char *output, - size_t output_size, - size_t *output_length); - -/** Finish encrypting a message in an AEAD operation. - * - * The operation must have been set up with psa_aead_encrypt_setup(). - * - * This function finishes the authentication of the additional data - * formed by concatenating the inputs passed to preceding calls to - * psa_aead_update_ad() with the plaintext formed by concatenating the - * inputs passed to preceding calls to psa_aead_update(). - * - * This function has two output buffers: - * - \p ciphertext contains trailing ciphertext that was buffered from - * preceding calls to psa_aead_update(). For all standard AEAD algorithms, - * psa_aead_update() does not buffer any output and therefore \p ciphertext - * will not contain any output and can be a 0-sized buffer. - * - \p tag contains the authentication tag. Its length is always - * #PSA_AEAD_TAG_LENGTH(\p alg) where \p alg is the AEAD algorithm - * that the operation performs. - * - * When this function returns, the operation becomes inactive. - * - * \param[in,out] operation Active AEAD operation. - * \param[out] ciphertext Buffer where the last part of the ciphertext - * is to be written. - * \param ciphertext_size Size of the \p ciphertext buffer in bytes. - * \param[out] ciphertext_length On success, the number of bytes of - * returned ciphertext. - * \param[out] tag Buffer where the authentication tag is - * to be written. - * \param tag_size Size of the \p tag buffer in bytes. - * \param[out] tag_length On success, the number of bytes - * that make up the returned tag. - * - * \retval #PSA_SUCCESS - * Success. - * \retval #PSA_ERROR_BAD_STATE - * The operation state is not valid (not set up, nonce not set, - * decryption, or already completed). - * \retval #PSA_ERROR_BUFFER_TOO_SMALL - * The size of the \p output buffer is too small. - * \retval #PSA_ERROR_INVALID_ARGUMENT - * The total length of input to psa_aead_update_ad() so far is - * less than the additional data length that was previously - * specified with psa_aead_set_lengths(). - * \retval #PSA_ERROR_INVALID_ARGUMENT - * The total length of input to psa_aead_update() so far is - * less than the plaintext length that was previously - * specified with psa_aead_set_lengths(). - * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - */ -psa_status_t psa_aead_finish(psa_aead_operation_t *operation, - uint8_t *ciphertext, - size_t ciphertext_size, - size_t *ciphertext_length, - uint8_t *tag, - size_t tag_size, - size_t *tag_length); - -/** Finish authenticating and decrypting a message in an AEAD operation. - * - * The operation must have been set up with psa_aead_decrypt_setup(). - * - * This function finishes the authentication of the additional data - * formed by concatenating the inputs passed to preceding calls to - * psa_aead_update_ad() with the ciphertext formed by concatenating the - * inputs passed to preceding calls to psa_aead_update(). - * - * When this function returns, the operation becomes inactive. - * - * \param[in,out] operation Active AEAD operation. - * \param[in] tag Buffer containing the authentication tag. - * \param tag_length Size of the \p tag buffer in bytes. - * - * \retval #PSA_SUCCESS - * Success. - * \retval #PSA_ERROR_BAD_STATE - * The operation state is not valid (not set up, nonce not set, - * encryption, or already completed). - * \retval #PSA_ERROR_BUFFER_TOO_SMALL - * The size of the \p output buffer is too small. - * \retval #PSA_ERROR_INVALID_ARGUMENT - * The total length of input to psa_aead_update_ad() so far is - * less than the additional data length that was previously - * specified with psa_aead_set_lengths(). - * \retval #PSA_ERROR_INVALID_ARGUMENT - * The total length of input to psa_aead_update() so far is - * less than the plaintext length that was previously - * specified with psa_aead_set_lengths(). - * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - */ -psa_status_t psa_aead_verify(psa_aead_operation_t *operation, - const uint8_t *tag, - size_t tag_length); - -/** Abort an AEAD operation. - * - * Aborting an operation frees all associated resources except for the - * \p operation structure itself. Once aborted, the operation object - * can be reused for another operation by calling - * psa_aead_encrypt_setup() or psa_aead_decrypt_setup() again. - * - * You may call this function any time after the operation object has - * been initialized by any of the following methods: - * - A call to psa_aead_encrypt_setup() or psa_aead_decrypt_setup(), - * whether it succeeds or not. - * - Initializing the \c struct to all-bits-zero. - * - Initializing the \c struct to logical zeros, e.g. - * `psa_aead_operation_t operation = {0}`. - * - * In particular, calling psa_aead_abort() after the operation has been - * terminated by a call to psa_aead_abort() or psa_aead_finish() - * is safe and has no effect. - * - * \param[in,out] operation Initialized AEAD operation. - * - * \retval #PSA_SUCCESS - * \retval #PSA_ERROR_BAD_STATE - * \p operation is not an active AEAD operation. - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - */ -psa_status_t psa_aead_abort(psa_aead_operation_t *operation); - -/**@}*/ - -/** \defgroup asymmetric Asymmetric cryptography - * @{ - */ - -/** - * \brief Sign a hash or short message with a private key. - * - * Note that to perform a hash-and-sign signature algorithm, you must - * first calculate the hash by calling psa_hash_setup(), psa_hash_update() - * and psa_hash_finish(). Then pass the resulting hash as the \p hash - * parameter to this function. You can use #PSA_ALG_SIGN_GET_HASH(\p alg) - * to determine the hash algorithm to use. + * Note that to perform a hash-and-sign signature algorithm, you must + * first calculate the hash by calling psa_hash_setup(), psa_hash_update() + * and psa_hash_finish(). Then pass the resulting hash as the \p hash + * parameter to this function. You can use #PSA_ALG_SIGN_GET_HASH(\p alg) + * to determine the hash algorithm to use. * * \param handle Handle to the key to use for the operation. * It must be an asymmetric key pair. @@ -2838,22 +2037,6 @@ static psa_crypto_generator_t psa_crypto_generator_init(void); psa_status_t psa_get_generator_capacity(const psa_crypto_generator_t *generator, size_t *capacity); -/** Set the maximum capacity of a generator. - * - * \param[in,out] generator The generator object to modify. - * \param capacity The new capacity of the generator. - * It must be less or equal to the generator's - * current capacity. - * - * \retval #PSA_SUCCESS - * \retval #PSA_ERROR_INVALID_ARGUMENT - * \p capacity is larger than the generator's current capacity. - * \retval #PSA_ERROR_BAD_STATE - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - */ -psa_status_t psa_set_generator_capacity(psa_crypto_generator_t *generator, - size_t capacity); - /** Read some data from a generator. * * This function reads and returns a sequence of bytes from a generator. @@ -2866,7 +2049,7 @@ psa_status_t psa_set_generator_capacity(psa_crypto_generator_t *generator, * \param output_length Number of bytes to output. * * \retval #PSA_SUCCESS - * \retval #PSA_ERROR_INSUFFICIENT_CAPACITY + * \retval #PSA_ERROR_INSUFFICIENT_DATA * There were fewer than \p output_length bytes * in the generator. Note that in this case, no * output is written to the output buffer. @@ -2908,7 +2091,7 @@ psa_status_t psa_generator_read(psa_crypto_generator_t *generator, * Success. * If the key is persistent, the key material and the key's metadata * have been saved to persistent storage. - * \retval #PSA_ERROR_INSUFFICIENT_CAPACITY + * \retval #PSA_ERROR_INSUFFICIENT_DATA * There were fewer than \p output_length bytes * in the generator. Note that in this case, no * output is written to the output buffer. @@ -2920,7 +2103,7 @@ psa_status_t psa_generator_read(psa_crypto_generator_t *generator, * implementation in general or in this particular slot. * \retval #PSA_ERROR_BAD_STATE * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_OCCUPIED_SLOT + * \retval #PSA_ERROR_ALREADY_EXISTS * There is already a key in the specified slot. * \retval #PSA_ERROR_INSUFFICIENT_MEMORY * \retval #PSA_ERROR_INSUFFICIENT_STORAGE @@ -2978,172 +2161,94 @@ psa_status_t psa_generator_abort(psa_crypto_generator_t *generator); /** Set up a key derivation operation. * - * A key derivation algorithm takes some inputs and uses them to create - * a byte generator which can be used to produce keys and other - * cryptographic material. - * - * To use a generator for key derivation: - * - Start with an initialized object of type #psa_crypto_generator_t. - * - Call psa_key_derivation_setup() to select the algorithm. - * - Provide the inputs for the key derivation by calling - * psa_key_derivation_input_bytes() or psa_key_derivation_input_key() - * as appropriate. Which inputs are needed, in what order, and whether - * they may be keys and if so of what type depends on the algorithm. - * - Optionally set the generator's maximum capacity with - * psa_set_generator_capacity(). You may do this before, in the middle of - * or after providing inputs. For some algorithms, this step is mandatory - * because the output depends on the maximum capacity. - * - Generate output with psa_generator_read() or - * psa_generator_import_key(). Successive calls to these functions - * use successive output bytes from the generator. - * - Clean up the generator object with psa_generator_abort(). - * - * \param[in,out] generator The generator object to set up. It must - * have been initialized but not set up yet. + * A key derivation algorithm takes three inputs: a secret input \p key and + * two non-secret inputs \p label and p salt. + * The result of this function is a byte generator which can + * be used to produce keys and other cryptographic material. + * + * The role of \p label and \p salt is as follows: + * - For HKDF (#PSA_ALG_HKDF), \p salt is the salt used in the "extract" step + * and \p label is the info string used in the "expand" step. + * + * \param[in,out] generator The generator object to set up. It must have + * been initialized as per the documentation for + * #psa_crypto_generator_t and not yet in use. + * \param handle Handle to the secret key. * \param alg The key derivation algorithm to compute * (\c PSA_ALG_XXX value such that * #PSA_ALG_IS_KEY_DERIVATION(\p alg) is true). - * - * \retval #PSA_SUCCESS - * Success. - * \retval #PSA_ERROR_INVALID_ARGUMENT - * \c alg is not a key derivation algorithm. - * \retval #PSA_ERROR_NOT_SUPPORTED - * \c alg is not supported or is not a key derivation algorithm. - * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - * \retval #PSA_ERROR_BAD_STATE - */ -psa_status_t psa_key_derivation_setup(psa_crypto_generator_t *generator, - psa_algorithm_t alg); - -/** Provide an input for key derivation or key agreement. - * - * Which inputs are required and in what order depends on the algorithm. - * Refer to the documentation of each key derivation or key agreement - * algorithm for information. - * - * This function passes direct inputs. Some inputs must be passed as keys - * using psa_key_derivation_input_key() instead of this function. Refer to - * the documentation of individual step types for information. - * - * \param[in,out] generator The generator object to use. It must - * have been set up with - * psa_key_derivation_setup() and must not - * have produced any output yet. - * \param step Which step the input data is for. - * \param[in] data Input data to use. - * \param data_length Size of the \p data buffer in bytes. - * - * \retval #PSA_SUCCESS - * Success. - * \retval #PSA_ERROR_INVALID_ARGUMENT - * \c step is not compatible with the generator's algorithm. - * \retval #PSA_ERROR_INVALID_ARGUMENT - * \c step does not allow direct inputs. - * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - * \retval #PSA_ERROR_BAD_STATE - * The value of \p step is not valid given the state of \p generator. - * \retval #PSA_ERROR_BAD_STATE - * The library has not been previously initialized by psa_crypto_init(). - * It is implementation-dependent whether a failure to initialize - * results in this error code. - */ -psa_status_t psa_key_derivation_input_bytes(psa_crypto_generator_t *generator, - psa_key_derivation_step_t step, - const uint8_t *data, - size_t data_length); - -/** Provide an input for key derivation in the form of a key. - * - * Which inputs are required and in what order depends on the algorithm. - * Refer to the documentation of each key derivation or key agreement - * algorithm for information. - * - * This function passes key inputs. Some inputs must be passed as keys - * of the appropriate type using this function, while others must be - * passed as direct inputs using psa_key_derivation_input_bytes(). Refer to - * the documentation of individual step types for information. - * - * \param[in,out] generator The generator object to use. It must - * have been set up with - * psa_key_derivation_setup() and must not - * have produced any output yet. - * \param step Which step the input data is for. - * \param handle Handle to the key. It must have an - * appropriate type for \p step and must - * allow the usage #PSA_KEY_USAGE_DERIVE. + * \param[in] salt Salt to use. + * \param salt_length Size of the \p salt buffer in bytes. + * \param[in] label Label to use. + * \param label_length Size of the \p label buffer in bytes. + * \param capacity The maximum number of bytes that the + * generator will be able to provide. * * \retval #PSA_SUCCESS * Success. * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_EMPTY_SLOT + * \retval #PSA_ERROR_DOES_NOT_EXIST * \retval #PSA_ERROR_NOT_PERMITTED * \retval #PSA_ERROR_INVALID_ARGUMENT - * \c step is not compatible with the generator's algorithm. - * \retval #PSA_ERROR_INVALID_ARGUMENT - * \c step does not allow key inputs. + * \c key is not compatible with \c alg, + * or \p capacity is too large for the specified algorithm and key. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \c alg is not supported or is not a key derivation algorithm. * \retval #PSA_ERROR_INSUFFICIENT_MEMORY * \retval #PSA_ERROR_COMMUNICATION_FAILURE * \retval #PSA_ERROR_HARDWARE_FAILURE * \retval #PSA_ERROR_TAMPERING_DETECTED * \retval #PSA_ERROR_BAD_STATE - * The value of \p step is not valid given the state of \p generator. - * \retval #PSA_ERROR_BAD_STATE * The library has not been previously initialized by psa_crypto_init(). * It is implementation-dependent whether a failure to initialize * results in this error code. */ -psa_status_t psa_key_derivation_input_key(psa_crypto_generator_t *generator, - psa_key_derivation_step_t step, - psa_key_handle_t handle); +psa_status_t psa_key_derivation(psa_crypto_generator_t *generator, + psa_key_handle_t handle, + psa_algorithm_t alg, + const uint8_t *salt, + size_t salt_length, + const uint8_t *label, + size_t label_length, + size_t capacity); -/** Perform a key agreement and use the shared secret as input to a key - * derivation. +/** Set up a key agreement operation. * * A key agreement algorithm takes two inputs: a private key \p private_key * a public key \p peer_key. - * The result of this function is passed as input to a key derivation. - * The output of this key derivation can be extracted by reading from the - * resulting generator to produce keys and other cryptographic material. - * - * \param[in,out] generator The generator object to use. It must - * have been set up with - * psa_key_derivation_setup() with a - * key agreement and derivation algorithm - * \c alg (\c PSA_ALG_XXX value such that - * #PSA_ALG_IS_KEY_AGREEMENT(\p alg) is true - * and #PSA_ALG_IS_RAW_KEY_AGREEMENT(\p alg) - * is false). - * The generator must be ready for an - * input of the type given by \p step. - * \param step Which step the input data is for. - * \param private_key Handle to the private key to use. + * The result of this function is a byte generator which can + * be used to produce keys and other cryptographic material. + * + * The resulting generator always has the maximum capacity permitted by + * the algorithm. + * + * \param[in,out] generator The generator object to set up. It must have been + * initialized as per the documentation for + * #psa_crypto_generator_t and not yet in use. + * \param private_key Handle to the private key to use. * \param[in] peer_key Public key of the peer. The peer key must be in the * same format that psa_import_key() accepts for the * public key type corresponding to the type of - * private_key. That is, this function performs the + * \p private_key. That is, this function performs the * equivalent of * `psa_import_key(internal_public_key_handle, * PSA_KEY_TYPE_PUBLIC_KEY_OF_KEYPAIR(private_key_type), * peer_key, peer_key_length)` where - * `private_key_type` is the type of `private_key`. - * For example, for EC keys, this means that peer_key - * is interpreted as a point on the curve that the - * private key is on. The standard formats for public - * keys are documented in the documentation of - * psa_export_public_key(). - * \param peer_key_length Size of \p peer_key in bytes. + * `private_key_type` is the type of \p private_key. + * For example, for EC keys, this means that \p + * peer_key is interpreted as a point on the curve + * that the private key is associated with. The + * standard formats for public keys are documented in + * the documentation of psa_export_public_key(). + * \param peer_key_length Size of \p peer_key in bytes. + * \param alg The key agreement algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_KEY_AGREEMENT(\p alg) is true). * * \retval #PSA_SUCCESS * Success. * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_EMPTY_SLOT + * \retval #PSA_ERROR_DOES_NOT_EXIST * \retval #PSA_ERROR_NOT_PERMITTED * \retval #PSA_ERROR_INVALID_ARGUMENT * \c private_key is not compatible with \c alg, @@ -3157,62 +2262,10 @@ psa_status_t psa_key_derivation_input_key(psa_crypto_generator_t *generator, * \retval #PSA_ERROR_TAMPERING_DETECTED */ psa_status_t psa_key_agreement(psa_crypto_generator_t *generator, - psa_key_derivation_step_t step, psa_key_handle_t private_key, const uint8_t *peer_key, - size_t peer_key_length); - -/** Perform a key agreement and use the shared secret as input to a key - * derivation. - * - * A key agreement algorithm takes two inputs: a private key \p private_key - * a public key \p peer_key. - * - * \warning The raw result of a key agreement algorithm such as finite-field - * Diffie-Hellman or elliptic curve Diffie-Hellman has biases and should - * not be used directly as key material. It should instead be passed as - * input to a key derivation algorithm. To chain a key agreement with - * a key derivation, use psa_key_agreement() and other functions from - * the key derivation and generator interface. - * - * \param private_key Handle to the private key to use. - * \param[in] peer_key Public key of the peer. It must be - * in the same format that psa_import_key() - * accepts. The standard formats for public - * keys are documented in the documentation - * of psa_export_public_key(). - * \param peer_key_length Size of \p peer_key in bytes. - * \param[out] output Buffer where the decrypted message is to - * be written. - * \param output_size Size of the \c output buffer in bytes. - * \param[out] output_length On success, the number of bytes - * that make up the returned output. - * - * \retval #PSA_SUCCESS - * Success. - * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_EMPTY_SLOT - * \retval #PSA_ERROR_NOT_PERMITTED - * \retval #PSA_ERROR_INVALID_ARGUMENT - * \p alg is not a key agreement algorithm - * \retval #PSA_ERROR_INVALID_ARGUMENT - * \p private_key is not compatible with \p alg, - * or \p peer_key is not valid for \p alg or not compatible with - * \p private_key. - * \retval #PSA_ERROR_NOT_SUPPORTED - * \p alg is not a supported key agreement algorithm. - * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * \retval #PSA_ERROR_COMMUNICATION_FAILURE - * \retval #PSA_ERROR_HARDWARE_FAILURE - * \retval #PSA_ERROR_TAMPERING_DETECTED - */ -psa_status_t psa_key_agreement_raw_shared_secret(psa_algorithm_t alg, - psa_key_handle_t private_key, - const uint8_t *peer_key, - size_t peer_key_length, - uint8_t *output, - size_t output_size, - size_t *output_length); + size_t peer_key_length, + psa_algorithm_t alg); /**@}*/ @@ -3285,18 +2338,6 @@ typedef struct { * specifying the public exponent. The * default public exponent used when \p extra * is \c NULL is 65537. - * - For an DSA key (\p type is - * #PSA_KEY_TYPE_DSA_KEYPAIR), \p extra is an - * optional structure specifying the key domain - * parameters. The key domain parameters can also be - * provided by psa_set_key_domain_parameters(), - * which documents the format of the structure. - * - For a DH key (\p type is - * #PSA_KEY_TYPE_DH_KEYPAIR), the \p extra is an - * optional structure specifying the key domain - * parameters. The key domain parameters can also be - * provided by psa_set_key_domain_parameters(), - * which documents the format of the structure. * \param extra_size Size of the buffer that \p extra * points to, in bytes. Note that if \p extra is * \c NULL then \p extra_size must be zero. @@ -3306,7 +2347,7 @@ typedef struct { * If the key is persistent, the key material and the key's metadata * have been saved to persistent storage. * \retval #PSA_ERROR_INVALID_HANDLE - * \retval #PSA_ERROR_OCCUPIED_SLOT + * \retval #PSA_ERROR_ALREADY_EXISTS * There is already a key in the specified slot. * \retval #PSA_ERROR_NOT_SUPPORTED * \retval #PSA_ERROR_INVALID_ARGUMENT diff --git a/api-specs/include/psa/crypto_extra.h b/api-specs/include/psa/crypto_extra.h index 6711860c..1e330aca 100644 --- a/api-specs/include/psa/crypto_extra.h +++ b/api-specs/include/psa/crypto_extra.h @@ -1,7 +1,7 @@ /** * \file psa/crypto_extra.h * - * \brief PSA cryptography module: vendor extensions + * \brief PSA cryptography module: Mbed TLS vendor extensions * * \note This file may not be included directly. Applications must * include psa/crypto.h. @@ -30,11 +30,120 @@ #ifndef PSA_CRYPTO_EXTRA_H #define PSA_CRYPTO_EXTRA_H +#include "mbedtls/platform_util.h" + #ifdef __cplusplus extern "C" { #endif -/* Add vendor extensions here. */ +/* UID for secure storage seed */ +#define PSA_CRYPTO_ITS_RANDOM_SEED_UID 0xFFFFFF52 + +/* + * Deprecated PSA Crypto error code definitions + */ +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +#define PSA_ERROR_UNKNOWN_ERROR \ + MBEDTLS_DEPRECATED_NUMERIC_CONSTANT( PSA_ERROR_GENERIC_ERROR ) +#endif + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +#define PSA_ERROR_OCCUPIED_SLOT \ + MBEDTLS_DEPRECATED_NUMERIC_CONSTANT( PSA_ERROR_ALREADY_EXISTS ) +#endif + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +#define PSA_ERROR_EMPTY_SLOT \ + MBEDTLS_DEPRECATED_NUMERIC_CONSTANT( PSA_ERROR_DOES_NOT_EXIST ) +#endif + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +#define PSA_ERROR_INSUFFICIENT_CAPACITY \ + MBEDTLS_DEPRECATED_NUMERIC_CONSTANT( PSA_ERROR_INSUFFICIENT_DATA ) +#endif + +/** + * \brief Library deinitialization. + * + * This function clears all data associated with the PSA layer, + * including the whole key store. + * + * This is an Mbed TLS extension. + */ +void mbedtls_psa_crypto_free( void ); + + +/** + * \brief Inject an initial entropy seed for the random generator into + * secure storage. + * + * This function injects data to be used as a seed for the random generator + * used by the PSA Crypto implementation. On devices that lack a trusted + * entropy source (preferably a hardware random number generator), + * the Mbed PSA Crypto implementation uses this value to seed its + * random generator. + * + * On devices without a trusted entropy source, this function must be + * called exactly once in the lifetime of the device. On devices with + * a trusted entropy source, calling this function is optional. + * In all cases, this function may only be called before calling any + * other function in the PSA Crypto API, including psa_crypto_init(). + * + * When this function returns successfully, it populates a file in + * persistent storage. Once the file has been created, this function + * can no longer succeed. + * + * If any error occurs, this function does not change the system state. + * You can call this function again after correcting the reason for the + * error if possible. + * + * \warning This function **can** fail! Callers MUST check the return status. + * + * \warning If you use this function, you should use it as part of a + * factory provisioning process. The value of the injected seed + * is critical to the security of the device. It must be + * *secret*, *unpredictable* and (statistically) *unique per device*. + * You should be generate it randomly using a cryptographically + * secure random generator seeded from trusted entropy sources. + * You should transmit it securely to the device and ensure + * that its value is not leaked or stored anywhere beyond the + * needs of transmitting it from the point of generation to + * the call of this function, and erase all copies of the value + * once this function returns. + * + * This is an Mbed TLS extension. + * + * \note This function is only available on the following platforms: + * * If the compile-time options MBEDTLS_ENTROPY_NV_SEED and + * MBEDTLS_PSA_HAS_ITS_IO are both enabled. Note that you + * must provide compatible implementations of mbedtls_nv_seed_read + * and mbedtls_nv_seed_write. + * * In a client-server integration of PSA Cryptography, on the client side, + * if the server supports this feature. + * \param[in] seed Buffer containing the seed value to inject. + * \param[in] seed_size Size of the \p seed buffer. + * The size of the seed in bytes must be greater + * or equal to both #MBEDTLS_ENTROPY_MIN_PLATFORM + * and #MBEDTLS_ENTROPY_BLOCK_SIZE. + * It must be less or equal to + * #MBEDTLS_ENTROPY_MAX_SEED_SIZE. + * + * \retval #PSA_SUCCESS + * The seed value was injected successfully. The random generator + * of the PSA Crypto implementation is now ready for use. + * You may now call psa_crypto_init() and use the PSA Crypto + * implementation. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p seed_size is out of range. + * \retval #PSA_ERROR_STORAGE_FAILURE + * There was a failure reading or writing from storage. + * \retval #PSA_ERROR_NOT_PERMITTED + * The library has already been initialized. It is no longer + * possible to call this function. + */ +psa_status_t mbedtls_psa_inject_entropy(const unsigned char *seed, + size_t seed_size); + #ifdef __cplusplus } diff --git a/api-specs/include/psa/crypto_platform.h b/api-specs/include/psa/crypto_platform.h index ea60c959..52ee822e 100644 --- a/api-specs/include/psa/crypto_platform.h +++ b/api-specs/include/psa/crypto_platform.h @@ -1,7 +1,7 @@ /** * \file psa/crypto_platform.h * - * \brief PSA cryptography module: Platfom-specific definitions + * \brief PSA cryptography module: Mbed TLS platfom definitions * * \note This file may not be included directly. Applications must * include psa/crypto.h. @@ -35,11 +35,67 @@ #ifndef PSA_CRYPTO_PLATFORM_H #define PSA_CRYPTO_PLATFORM_H +/* Include the Mbed TLS configuration file, the way Mbed TLS does it + * in each of its header files. */ +#if !defined(MBEDTLS_CONFIG_FILE) +#include "../mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + /* PSA requires several types which C99 provides in stdint.h. */ #include -/* Integral type representing a key handle. The choice of integral - * type is implementation-dependent. */ +/* Integral type representing a key handle. */ typedef uint16_t psa_key_handle_t; +/* This implementation distinguishes *application key identifiers*, which + * are the key identifiers specified by the application, from + * *key file identifiers*, which are the key identifiers that the library + * sees internally. The two types can be different if there is a remote + * call layer between the application and the library which supports + * multiple client applications that do not have access to each others' + * keys. The point of having different types is that the key file + * identifier may encode not only the key identifier specified by the + * application, but also the the identity of the application. + * + * Note that this is an internal concept of the library and the remote + * call layer. The application itself never sees anything other than + * #psa_app_key_id_t with its standard definition. + */ + +/* The application key identifier is always what the application sees as + * #psa_key_id_t. */ +typedef uint32_t psa_app_key_id_t; + +#if defined(MBEDTLS_PSA_CRYPTO_KEY_FILE_ID_ENCODES_OWNER) + +#if defined(PSA_CRYPTO_SECURE) +/* Building for the PSA Crypto service on a PSA platform. */ +/* A key owner is a PSA partition identifier. */ +typedef int32_t psa_key_owner_id_t; +#endif + +typedef struct +{ + uint32_t key_id; + psa_key_owner_id_t owner; +} psa_key_file_id_t; +#define PSA_KEY_FILE_GET_KEY_ID( file_id ) ( ( file_id ).key_id ) + +/* Since crypto.h is used as part of the PSA Cryptography API specification, + * it must use standard types for things like the argument of psa_open_key(). + * If it wasn't for that constraint, psa_open_key() would take a + * `psa_key_file_id_t` argument. As a workaround, make `psa_key_id_t` an + * alias for `psa_key_file_id_t` when building for a multi-client service. */ +typedef psa_key_file_id_t psa_key_id_t; + +#else /* !MBEDTLS_PSA_CRYPTO_KEY_FILE_ID_ENCODES_OWNER */ + +/* By default, a key file identifier is just the application key identifier. */ +typedef psa_app_key_id_t psa_key_file_id_t; +#define PSA_KEY_FILE_GET_KEY_ID( id ) ( id ) + +#endif /* !MBEDTLS_PSA_CRYPTO_KEY_FILE_ID_ENCODES_OWNER */ + #endif /* PSA_CRYPTO_PLATFORM_H */ diff --git a/api-specs/include/psa/crypto_sizes.h b/api-specs/include/psa/crypto_sizes.h index 046b5246..7967a290 100644 --- a/api-specs/include/psa/crypto_sizes.h +++ b/api-specs/include/psa/crypto_sizes.h @@ -268,27 +268,6 @@ (plaintext_length) + PSA_AEAD_TAG_LENGTH(alg) : \ 0) -/** The maximum size of the output of psa_aead_finish(), in bytes. - * - * If the size of the ciphertext buffer is at least this large, it is - * guaranteed that psa_aead_finish() will not fail due to an - * insufficient buffer size. Depending on the algorithm, the actual size of - * the ciphertext may be smaller. - * - * \param alg An AEAD algorithm - * (\c PSA_ALG_XXX value such that - * #PSA_ALG_IS_AEAD(alg) is true). - * - * \return The maximum trailing ciphertext size for the - * specified algorithm. - * If the AEAD algorithm is not recognized, return 0. - * An implementation may return either 0 or a - * correct size for an AEAD algorithm that it - * recognizes, but does not support. - */ -#define PSA_AEAD_FINISH_OUTPUT_SIZE(alg, plaintext_length) \ - ((size_t)0) - /** The maximum size of the output of psa_aead_decrypt(), in bytes. * * If the size of the plaintext buffer is at least this large, it is @@ -313,9 +292,9 @@ (plaintext_length) - PSA_AEAD_TAG_LENGTH(alg) : \ 0) -#define PSA_RSA_MINIMUM_PADDING_SIZE(alg) \ - (PSA_ALG_IS_RSA_OAEP(alg) ? \ - 2 * PSA_HASH_FINAL_SIZE(PSA_ALG_RSA_OAEP_GET_HASH(alg)) + 1 : \ +#define PSA_RSA_MINIMUM_PADDING_SIZE(alg) \ + (PSA_ALG_IS_RSA_OAEP(alg) ? \ + 2 * PSA_HASH_SIZE(PSA_ALG_RSA_OAEP_GET_HASH(alg)) + 1 : \ 11 /*PKCS#1v1.5*/) /** @@ -438,25 +417,16 @@ /* Maximum size of the export encoding of an RSA public key. * Assumes that the public exponent is less than 2^32. * - * SubjectPublicKeyInfo ::= SEQUENCE { - * algorithm AlgorithmIdentifier, - * subjectPublicKey BIT STRING } -- contains RSAPublicKey - * AlgorithmIdentifier ::= SEQUENCE { - * algorithm OBJECT IDENTIFIER, - * parameters NULL } * RSAPublicKey ::= SEQUENCE { * modulus INTEGER, -- n * publicExponent INTEGER } -- e * - * - 3 * 4 bytes of SEQUENCE overhead; - * - 1 + 1 + 9 bytes of algorithm (RSA OID); - * - 2 bytes of NULL; - * - 4 bytes of BIT STRING overhead; + * - 4 bytes of SEQUENCE overhead; * - n : INTEGER; * - 7 bytes for the public exponent. */ #define PSA_KEY_EXPORT_RSA_PUBLIC_KEY_MAX_SIZE(key_bits) \ - (PSA_KEY_EXPORT_ASN1_INTEGER_MAX_SIZE(key_bits) + 36) + (PSA_KEY_EXPORT_ASN1_INTEGER_MAX_SIZE(key_bits) + 11) /* Maximum size of the export encoding of an RSA key pair. * Assumes thatthe public exponent is less than 2^32 and that the size @@ -523,26 +493,16 @@ /* Maximum size of the export encoding of an ECC public key. * - * SubjectPublicKeyInfo ::= SEQUENCE { - * algorithm AlgorithmIdentifier, - * subjectPublicKey BIT STRING } -- contains ECPoint - * AlgorithmIdentifier ::= SEQUENCE { - * algorithm OBJECT IDENTIFIER, - * parameters OBJECT IDENTIFIER } -- namedCurve - * ECPoint ::= ... - * -- first 8 bits: 0x04; - * -- then x_P as a `ceiling(m/8)`-byte string, big endian; - * -- then y_P as a `ceiling(m/8)`-byte string, big endian; - * -- where `m` is the bit size associated with the curve. - * - * - 2 * 4 bytes of SEQUENCE overhead; - * - 1 + 1 + 7 bytes of algorithm (id-ecPublicKey OID); - * - 1 + 1 + 12 bytes of namedCurve OID; - * - 4 bytes of BIT STRING overhead; - * - 1 byte + 2 * point size in ECPoint. + * The representation of an ECC public key is: + * - The byte 0x04; + * - `x_P` as a `ceiling(m/8)`-byte string, big-endian; + * - `y_P` as a `ceiling(m/8)`-byte string, big-endian; + * - where m is the bit size associated with the curve. + * + * - 1 byte + 2 * point size. */ #define PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(key_bits) \ - (2 * PSA_BITS_TO_BYTES(key_bits) + 36) + (2 * PSA_BITS_TO_BYTES(key_bits) + 1) /* Maximum size of the export encoding of an ECC key pair. * diff --git a/api-specs/include/psa/crypto_struct.h b/api-specs/include/psa/crypto_struct.h index df09fcb0..18b37ef2 100644 --- a/api-specs/include/psa/crypto_struct.h +++ b/api-specs/include/psa/crypto_struct.h @@ -1,11 +1,7 @@ /** * \file psa/crypto_struct.h * - * \brief PSA cryptography module: structured type implementations - * - * This file contains a list of structures that each implementation - * of the PSA Crypto API must define, as well as sample definitions - * for initializers. + * \brief PSA cryptography module: Mbed TLS structured type implementations * * \note This file may not be included directly. Applications must * include psa/crypto.h. @@ -39,40 +35,189 @@ #ifndef PSA_CRYPTO_STRUCT_H #define PSA_CRYPTO_STRUCT_H -struct psa_hash_operation_s; -#define PSA_HASH_OPERATION_INIT {0} +/* Include the Mbed TLS configuration file, the way Mbed TLS does it + * in each of its header files. */ +#if !defined(MBEDTLS_CONFIG_FILE) +#include "../mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#include "mbedtls/cipher.h" +#include "mbedtls/cmac.h" +#include "mbedtls/gcm.h" +#include "mbedtls/md.h" +#include "mbedtls/md2.h" +#include "mbedtls/md4.h" +#include "mbedtls/md5.h" +#include "mbedtls/ripemd160.h" +#include "mbedtls/sha1.h" +#include "mbedtls/sha256.h" +#include "mbedtls/sha512.h" + +struct psa_hash_operation_s +{ + psa_algorithm_t alg; + union + { + unsigned dummy; /* Make the union non-empty even with no supported algorithms. */ +#if defined(MBEDTLS_MD2_C) + mbedtls_md2_context md2; +#endif +#if defined(MBEDTLS_MD4_C) + mbedtls_md4_context md4; +#endif +#if defined(MBEDTLS_MD5_C) + mbedtls_md5_context md5; +#endif +#if defined(MBEDTLS_RIPEMD160_C) + mbedtls_ripemd160_context ripemd160; +#endif +#if defined(MBEDTLS_SHA1_C) + mbedtls_sha1_context sha1; +#endif +#if defined(MBEDTLS_SHA256_C) + mbedtls_sha256_context sha256; +#endif +#if defined(MBEDTLS_SHA512_C) + mbedtls_sha512_context sha512; +#endif + } ctx; +}; + +#define PSA_HASH_OPERATION_INIT {0, {0}} static inline struct psa_hash_operation_s psa_hash_operation_init( void ) { const struct psa_hash_operation_s v = PSA_HASH_OPERATION_INIT; return( v ); } -struct psa_mac_operation_s; -#define PSA_MAC_OPERATION_INIT {0} +#if defined(MBEDTLS_MD_C) +typedef struct +{ + /** The hash context. */ + struct psa_hash_operation_s hash_ctx; + /** The HMAC part of the context. */ + uint8_t opad[PSA_HMAC_MAX_HASH_BLOCK_SIZE]; +} psa_hmac_internal_data; +#endif /* MBEDTLS_MD_C */ + +struct psa_mac_operation_s +{ + psa_algorithm_t alg; + unsigned int key_set : 1; + unsigned int iv_required : 1; + unsigned int iv_set : 1; + unsigned int has_input : 1; + unsigned int is_sign : 1; + uint8_t mac_size; + union + { + unsigned dummy; /* Make the union non-empty even with no supported algorithms. */ +#if defined(MBEDTLS_MD_C) + psa_hmac_internal_data hmac; +#endif +#if defined(MBEDTLS_CMAC_C) + mbedtls_cipher_context_t cmac; +#endif + } ctx; +}; + +#define PSA_MAC_OPERATION_INIT {0, 0, 0, 0, 0, 0, 0, {0}} static inline struct psa_mac_operation_s psa_mac_operation_init( void ) { const struct psa_mac_operation_s v = PSA_MAC_OPERATION_INIT; return( v ); } -struct psa_cipher_operation_s; -#define PSA_CIPHER_OPERATION_INIT {0} +struct psa_cipher_operation_s +{ + psa_algorithm_t alg; + unsigned int key_set : 1; + unsigned int iv_required : 1; + unsigned int iv_set : 1; + uint8_t iv_size; + uint8_t block_size; + union + { + unsigned dummy; /* Enable easier initializing of the union. */ + mbedtls_cipher_context_t cipher; + } ctx; +}; + +#define PSA_CIPHER_OPERATION_INIT {0, 0, 0, 0, 0, 0, {0}} static inline struct psa_cipher_operation_s psa_cipher_operation_init( void ) { const struct psa_cipher_operation_s v = PSA_CIPHER_OPERATION_INIT; return( v ); } -struct psa_aead_operation_s; -#define PSA_AEAD_OPERATION_INIT {0} -static inline struct psa_aead_operation_s psa_aead_operation_init( void ) +#if defined(MBEDTLS_MD_C) +typedef struct { - const struct psa_aead_operation_s v = PSA_AEAD_OPERATION_INIT; - return( v ); -} + uint8_t *info; + size_t info_length; + psa_hmac_internal_data hmac; + uint8_t prk[PSA_HASH_MAX_SIZE]; + uint8_t output_block[PSA_HASH_MAX_SIZE]; +#if PSA_HASH_MAX_SIZE > 0xff +#error "PSA_HASH_MAX_SIZE does not fit in uint8_t" +#endif + uint8_t offset_in_block; + uint8_t block_number; +} psa_hkdf_generator_t; +#endif /* MBEDTLS_MD_C */ + +#if defined(MBEDTLS_MD_C) +typedef struct psa_tls12_prf_generator_s +{ + /* The TLS 1.2 PRF uses the key for each HMAC iteration, + * hence we must store it for the lifetime of the generator. + * This is different from HKDF, where the key is only used + * in the extraction phase, but not during expansion. */ + unsigned char *key; + size_t key_len; + + /* `A(i) + seed` in the notation of RFC 5246, Sect. 5 */ + uint8_t *Ai_with_seed; + size_t Ai_with_seed_len; + + /* `HMAC_hash( prk, A(i) + seed )` in the notation of RFC 5246, Sect. 5. */ + uint8_t output_block[PSA_HASH_MAX_SIZE]; + +#if PSA_HASH_MAX_SIZE > 0xff +#error "PSA_HASH_MAX_SIZE does not fit in uint8_t" +#endif -struct psa_crypto_generator_s; -#define PSA_CRYPTO_GENERATOR_INIT {0} + /* Indicates how many bytes in the current HMAC block have + * already been read by the user. */ + uint8_t offset_in_block; + + /* The 1-based number of the block. */ + uint8_t block_number; + +} psa_tls12_prf_generator_t; +#endif /* MBEDTLS_MD_C */ + +struct psa_crypto_generator_s +{ + psa_algorithm_t alg; + size_t capacity; + union + { + struct + { + uint8_t *data; + size_t size; + } buffer; +#if defined(MBEDTLS_MD_C) + psa_hkdf_generator_t hkdf; + psa_tls12_prf_generator_t tls12_prf; +#endif + } ctx; +}; + +#define PSA_CRYPTO_GENERATOR_INIT {0, 0, {{0, 0}}} static inline struct psa_crypto_generator_s psa_crypto_generator_init( void ) { const struct psa_crypto_generator_s v = PSA_CRYPTO_GENERATOR_INIT; @@ -84,6 +229,7 @@ struct psa_key_policy_s psa_key_usage_t usage; psa_algorithm_t alg; }; + #define PSA_KEY_POLICY_INIT {0, 0} static inline struct psa_key_policy_s psa_key_policy_init( void ) { diff --git a/api-specs/include/psa/crypto_types.h b/api-specs/include/psa/crypto_types.h index 99d73f61..b8717e35 100644 --- a/api-specs/include/psa/crypto_types.h +++ b/api-specs/include/psa/crypto_types.h @@ -47,8 +47,13 @@ * This is either #PSA_SUCCESS (which is zero), indicating success, * or a nonzero value indicating that an error occurred. Errors are * encoded as one of the \c PSA_ERROR_xxx values defined here. + * If #PSA_SUCCESS is already defined, it means that #psa_status_t + * is also defined in an external header, so prevent its multiple + * definition. */ +#ifndef PSA_SUCCESS typedef int32_t psa_status_t; +#endif /**@}*/ @@ -85,7 +90,14 @@ typedef uint32_t psa_key_lifetime_t; /** Encoding of identifiers of persistent keys. */ +/* Implementation-specific quirk: The Mbed Crypto library can be built as + * part of a multi-client service that exposes the PSA Crypto API in each + * client and encodes the client identity in the key id argument of functions + * such as psa_open_key(). In this build configuration, we define + * psa_key_id_t in crypto_platform.h instead of here. */ +#if !defined(MBEDTLS_PSA_CRYPTO_KEY_FILE_ID_ENCODES_OWNER) typedef uint32_t psa_key_id_t; +#endif /**@}*/ @@ -98,13 +110,4 @@ typedef uint32_t psa_key_usage_t; /**@}*/ -/** \defgroup derivation Key derivation - * @{ - */ - -/** \brief Encoding of the step of a key derivation. */ -typedef uint16_t psa_key_derivation_step_t; - -/**@}*/ - #endif /* PSA_CRYPTO_TYPES_H */ diff --git a/api-specs/include/psa/crypto_values.h b/api-specs/include/psa/crypto_values.h index 88eaff65..2ce2de65 100644 --- a/api-specs/include/psa/crypto_values.h +++ b/api-specs/include/psa/crypto_values.h @@ -40,25 +40,17 @@ * @{ */ -#if !defined(PSA_SUCCESS) -/* If PSA_SUCCESS is defined, assume that PSA crypto is being used - * together with PSA IPC, which also defines the identifier - * PSA_SUCCESS. We must not define PSA_SUCCESS ourselves in that case; - * the other error code names don't clash. This is a temporary hack - * until we unify error reporting in PSA IPC and PSA crypto. - * - * Note that psa_defs.h must be included before this header! - */ +/* PSA error codes */ + /** The action was completed successfully. */ #define PSA_SUCCESS ((psa_status_t)0) -#endif /* !defined(PSA_SUCCESS) */ /** An error occurred that does not correspond to any defined * failure cause. * * Implementations may use this error code if none of the other standard * error codes are applicable. */ -#define PSA_ERROR_UNKNOWN_ERROR ((psa_status_t)1) +#define PSA_ERROR_GENERIC_ERROR ((psa_status_t)-132) /** The requested operation or a parameter is not supported * by this implementation. @@ -67,7 +59,7 @@ * parameter such as a key type, algorithm, etc. is not recognized. * If a combination of parameters is recognized and identified as * not valid, return #PSA_ERROR_INVALID_ARGUMENT instead. */ -#define PSA_ERROR_NOT_SUPPORTED ((psa_status_t)2) +#define PSA_ERROR_NOT_SUPPORTED ((psa_status_t)-134) /** The requested action is denied by a policy. * @@ -80,7 +72,7 @@ * not valid or not supported, it is unspecified whether the function * returns #PSA_ERROR_NOT_PERMITTED, #PSA_ERROR_NOT_SUPPORTED or * #PSA_ERROR_INVALID_ARGUMENT. */ -#define PSA_ERROR_NOT_PERMITTED ((psa_status_t)3) +#define PSA_ERROR_NOT_PERMITTED ((psa_status_t)-133) /** An output buffer is too small. * @@ -92,23 +84,19 @@ * buffer would succeed. However implementations may return this * error if a function has invalid or unsupported parameters in addition * to the parameters that determine the necessary output buffer size. */ -#define PSA_ERROR_BUFFER_TOO_SMALL ((psa_status_t)4) +#define PSA_ERROR_BUFFER_TOO_SMALL ((psa_status_t)-138) -/** A slot is occupied, but must be empty to carry out the - * requested action. +/** Asking for an item that already exists * - * If a handle is invalid, it does not designate an occupied slot. - * The error for an invalid handle is #PSA_ERROR_INVALID_HANDLE. - */ -#define PSA_ERROR_OCCUPIED_SLOT ((psa_status_t)5) + * Implementations should return this error, when attempting + * to write an item (like a key) that already exists. */ +#define PSA_ERROR_ALREADY_EXISTS ((psa_status_t)-139) -/** A slot is empty, but must be occupied to carry out the - * requested action. +/** Asking for an item that doesn't exist * - * If a handle is invalid, it does not designate an empty slot. - * The error for an invalid handle is #PSA_ERROR_INVALID_HANDLE. - */ -#define PSA_ERROR_EMPTY_SLOT ((psa_status_t)6) + * Implementations should return this error, if a requested item (like + * a key) does not exist. */ +#define PSA_ERROR_DOES_NOT_EXIST ((psa_status_t)-140) /** The requested action cannot be performed in the current state. * @@ -118,9 +106,9 @@ * * Implementations shall not return this error code to indicate * that a key slot is occupied when it needs to be free or vice versa, - * but shall return #PSA_ERROR_OCCUPIED_SLOT or #PSA_ERROR_EMPTY_SLOT + * but shall return #PSA_ERROR_ALREADY_EXISTS or #PSA_ERROR_DOES_NOT_EXIST * as applicable. */ -#define PSA_ERROR_BAD_STATE ((psa_status_t)7) +#define PSA_ERROR_BAD_STATE ((psa_status_t)-137) /** The parameters passed to the function are invalid. * @@ -129,20 +117,20 @@ * * Implementations shall not return this error code to indicate * that a key slot is occupied when it needs to be free or vice versa, - * but shall return #PSA_ERROR_OCCUPIED_SLOT or #PSA_ERROR_EMPTY_SLOT + * but shall return #PSA_ERROR_ALREADY_EXISTS or #PSA_ERROR_DOES_NOT_EXIST * as applicable. * * Implementation shall not return this error code to indicate that a * key handle is invalid, but shall return #PSA_ERROR_INVALID_HANDLE * instead. */ -#define PSA_ERROR_INVALID_ARGUMENT ((psa_status_t)8) +#define PSA_ERROR_INVALID_ARGUMENT ((psa_status_t)-135) /** There is not enough runtime memory. * * If the action is carried out across multiple security realms, this * error can refer to available memory in any of the security realms. */ -#define PSA_ERROR_INSUFFICIENT_MEMORY ((psa_status_t)9) +#define PSA_ERROR_INSUFFICIENT_MEMORY ((psa_status_t)-141) /** There is not enough persistent storage. * @@ -151,7 +139,7 @@ * many functions that do not otherwise access storage may return this * error code if the implementation requires a mandatory log entry for * the requested action and the log storage space is full. */ -#define PSA_ERROR_INSUFFICIENT_STORAGE ((psa_status_t)10) +#define PSA_ERROR_INSUFFICIENT_STORAGE ((psa_status_t)-142) /** There was a communication failure inside the implementation. * @@ -168,7 +156,7 @@ * cryptoprocessor but there was a breakdown of communication before * the cryptoprocessor could report the status to the application. */ -#define PSA_ERROR_COMMUNICATION_FAILURE ((psa_status_t)11) +#define PSA_ERROR_COMMUNICATION_FAILURE ((psa_status_t)-145) /** There was a storage failure that may have led to data loss. * @@ -193,13 +181,13 @@ * permanent storage corruption. However application writers should * keep in mind that transient errors while reading the storage may be * reported using this error code. */ -#define PSA_ERROR_STORAGE_FAILURE ((psa_status_t)12) +#define PSA_ERROR_STORAGE_FAILURE ((psa_status_t)-146) /** A hardware failure was detected. * * A hardware failure may be transient or permanent depending on the * cause. */ -#define PSA_ERROR_HARDWARE_FAILURE ((psa_status_t)13) +#define PSA_ERROR_HARDWARE_FAILURE ((psa_status_t)-147) /** A tampering attempt was detected. * @@ -230,7 +218,7 @@ * This error indicates an attack against the application. Implementations * shall not return this error code as a consequence of the behavior of * the application itself. */ -#define PSA_ERROR_TAMPERING_DETECTED ((psa_status_t)14) +#define PSA_ERROR_TAMPERING_DETECTED ((psa_status_t)-151) /** There is not enough entropy to generate random data needed * for the requested action. @@ -249,7 +237,7 @@ * secure pseudorandom generator (PRNG). However implementations may return * this error at any time if a policy requires the PRNG to be reseeded * during normal operation. */ -#define PSA_ERROR_INSUFFICIENT_ENTROPY ((psa_status_t)15) +#define PSA_ERROR_INSUFFICIENT_ENTROPY ((psa_status_t)-148) /** The signature, MAC or hash is incorrect. * @@ -259,7 +247,7 @@ * * If the value to verify has an invalid size, implementations may return * either #PSA_ERROR_INVALID_ARGUMENT or #PSA_ERROR_INVALID_SIGNATURE. */ -#define PSA_ERROR_INVALID_SIGNATURE ((psa_status_t)16) +#define PSA_ERROR_INVALID_SIGNATURE ((psa_status_t)-149) /** The decrypted padding is incorrect. * @@ -275,17 +263,15 @@ * as close as possible to indistinguishable to an external observer. * In particular, the timing of a decryption operation should not * depend on the validity of the padding. */ -#define PSA_ERROR_INVALID_PADDING ((psa_status_t)17) +#define PSA_ERROR_INVALID_PADDING ((psa_status_t)-150) -/** The generator has insufficient capacity left. - * - * Once a function returns this error, attempts to read from the - * generator will always return this error. */ -#define PSA_ERROR_INSUFFICIENT_CAPACITY ((psa_status_t)18) +/** Return this error when there's insufficient data when attempting + * to read from a resource. */ +#define PSA_ERROR_INSUFFICIENT_DATA ((psa_status_t)-143) /** The key handle is not valid. */ -#define PSA_ERROR_INVALID_HANDLE ((psa_status_t)19) +#define PSA_ERROR_INVALID_HANDLE ((psa_status_t)-136) /**@}*/ @@ -497,15 +483,6 @@ #define PSA_ECC_CURVE_CURVE25519 ((psa_ecc_curve_t) 0x001d) #define PSA_ECC_CURVE_CURVE448 ((psa_ecc_curve_t) 0x001e) -/** Diffie-Hellman key exchange public key. */ -#define PSA_KEY_TYPE_DH_PUBLIC_KEY ((psa_key_type_t)0x60040000) -/** Diffie-Hellman key exchange key pair (private and public key). */ -#define PSA_KEY_TYPE_DH_KEYPAIR ((psa_key_type_t)0x70040000) -/** Whether a key type is a Diffie-Hellman key exchange key (pair or - * public-only). */ -#define PSA_KEY_TYPE_IS_DH(type) \ - (PSA_KEY_TYPE_PUBLIC_KEY_OF_KEYPAIR(type) == PSA_KEY_TYPE_DH_PUBLIC_KEY) - /** The block size of a block cipher. * * \param type A cipher key type (value of type #psa_key_type_t). @@ -540,8 +517,9 @@ #define PSA_ALG_CATEGORY_AEAD ((psa_algorithm_t)0x06000000) #define PSA_ALG_CATEGORY_SIGN ((psa_algorithm_t)0x10000000) #define PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION ((psa_algorithm_t)0x12000000) -#define PSA_ALG_CATEGORY_KEY_DERIVATION ((psa_algorithm_t)0x20000000) -#define PSA_ALG_CATEGORY_KEY_AGREEMENT ((psa_algorithm_t)0x30000000) +#define PSA_ALG_CATEGORY_KEY_AGREEMENT ((psa_algorithm_t)0x22000000) +#define PSA_ALG_CATEGORY_KEY_DERIVATION ((psa_algorithm_t)0x30000000) +#define PSA_ALG_CATEGORY_KEY_SELECTION ((psa_algorithm_t)0x31000000) #define PSA_ALG_IS_VENDOR_DEFINED(alg) \ (((alg) & PSA_ALG_VENDOR_FLAG) != 0) @@ -676,15 +654,18 @@ /** SHA3-512 */ #define PSA_ALG_SHA3_512 ((psa_algorithm_t)0x01000013) -/** Allow any hash algorithm. +/** In a hash-and-sign algorithm policy, allow any hash algorithm. + * + * This value may be used to form the algorithm usage field of a policy + * for a signature algorithm that is parametrized by a hash. The key + * may then be used to perform operations using the same signature + * algorithm parametrized with any supported hash. * - * This value may only be used to form the algorithm usage field of a policy - * for a signature algorithm that is parametrized by a hash. That is, - * suppose that `PSA_xxx_SIGNATURE` is one of the following macros: + * That is, suppose that `PSA_xxx_SIGNATURE` is one of the following macros: * - #PSA_ALG_RSA_PKCS1V15_SIGN, #PSA_ALG_RSA_PSS, * - #PSA_ALG_DSA, #PSA_ALG_DETERMINISTIC_DSA, * - #PSA_ALG_ECDSA, #PSA_ALG_DETERMINISTIC_ECDSA. - * Then you may create a key as follows: + * Then you may create and use a key as follows: * - Set the key usage field using #PSA_ALG_ANY_HASH, for example: * ``` * psa_key_policy_set_usage(&policy, @@ -771,7 +752,7 @@ * algorithm is considered identical to the untruncated algorithm * for policy comparison purposes. * - * \param alg A MAC algorithm identifier (value of type + * \param mac_alg A MAC algorithm identifier (value of type * #psa_algorithm_t such that #PSA_ALG_IS_MAC(\p alg) * is true). This may be a truncated or untruncated * MAC algorithm. @@ -787,14 +768,14 @@ * MAC algorithm or if \p mac_length is too small or * too large for the specified MAC algorithm. */ -#define PSA_ALG_TRUNCATED_MAC(alg, mac_length) \ - (((alg) & ~PSA_ALG_MAC_TRUNCATION_MASK) | \ +#define PSA_ALG_TRUNCATED_MAC(mac_alg, mac_length) \ + (((mac_alg) & ~PSA_ALG_MAC_TRUNCATION_MASK) | \ ((mac_length) << PSA_MAC_TRUNCATION_OFFSET & PSA_ALG_MAC_TRUNCATION_MASK)) /** Macro to build the base MAC algorithm corresponding to a truncated * MAC algorithm. * - * \param alg A MAC algorithm identifier (value of type + * \param mac_alg A MAC algorithm identifier (value of type * #psa_algorithm_t such that #PSA_ALG_IS_MAC(\p alg) * is true). This may be a truncated or untruncated * MAC algorithm. @@ -803,12 +784,12 @@ * \return Unspecified if \p alg is not a supported * MAC algorithm. */ -#define PSA_ALG_FULL_LENGTH_MAC(alg) \ - ((alg) & ~PSA_ALG_MAC_TRUNCATION_MASK) +#define PSA_ALG_FULL_LENGTH_MAC(mac_alg) \ + ((mac_alg) & ~PSA_ALG_MAC_TRUNCATION_MASK) /** Length to which a MAC algorithm is truncated. * - * \param alg A MAC algorithm identifier (value of type + * \param mac_alg A MAC algorithm identifier (value of type * #psa_algorithm_t such that #PSA_ALG_IS_MAC(\p alg) * is true). * @@ -817,8 +798,8 @@ * \return Unspecified if \p alg is not a supported * MAC algorithm. */ -#define PSA_MAC_TRUNCATED_LENGTH(alg) \ - (((alg) & PSA_ALG_MAC_TRUNCATION_MASK) >> PSA_MAC_TRUNCATION_OFFSET) +#define PSA_MAC_TRUNCATED_LENGTH(mac_alg) \ + (((mac_alg) & PSA_ALG_MAC_TRUNCATION_MASK) >> PSA_MAC_TRUNCATION_OFFSET) #define PSA_ALG_CIPHER_MAC_BASE ((psa_algorithm_t)0x02c00000) #define PSA_ALG_CBC_MAC ((psa_algorithm_t)0x02c00001) @@ -915,7 +896,7 @@ * Depending on the algorithm, the tag length may affect the calculation * of the ciphertext. * - * \param alg A AEAD algorithm identifier (value of type + * \param aead_alg An AEAD algorithm identifier (value of type * #psa_algorithm_t such that #PSA_ALG_IS_AEAD(\p alg) * is true). * \param tag_length Desired length of the authentication tag in bytes. @@ -926,26 +907,26 @@ * AEAD algorithm or if \p tag_length is not valid * for the specified AEAD algorithm. */ -#define PSA_ALG_AEAD_WITH_TAG_LENGTH(alg, tag_length) \ - (((alg) & ~PSA_ALG_AEAD_TAG_LENGTH_MASK) | \ +#define PSA_ALG_AEAD_WITH_TAG_LENGTH(aead_alg, tag_length) \ + (((aead_alg) & ~PSA_ALG_AEAD_TAG_LENGTH_MASK) | \ ((tag_length) << PSA_AEAD_TAG_LENGTH_OFFSET & \ PSA_ALG_AEAD_TAG_LENGTH_MASK)) /** Calculate the corresponding AEAD algorithm with the default tag length. * - * \param alg An AEAD algorithm (\c PSA_ALG_XXX value such that - * #PSA_ALG_IS_AEAD(\p alg) is true). + * \param aead_alg An AEAD algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_AEAD(\p alg) is true). * - * \return The corresponding AEAD algorithm with the default tag length - * for that algorithm. + * \return The corresponding AEAD algorithm with the default + * tag length for that algorithm. */ -#define PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH(alg) \ +#define PSA_ALG_AEAD_WITH_DEFAULT_TAG_LENGTH(aead_alg) \ ( \ - PSA__ALG_AEAD_WITH_DEFAULT_TAG_LENGTH__CASE(alg, PSA_ALG_CCM) \ - PSA__ALG_AEAD_WITH_DEFAULT_TAG_LENGTH__CASE(alg, PSA_ALG_GCM) \ + PSA__ALG_AEAD_WITH_DEFAULT_TAG_LENGTH__CASE(aead_alg, PSA_ALG_CCM) \ + PSA__ALG_AEAD_WITH_DEFAULT_TAG_LENGTH__CASE(aead_alg, PSA_ALG_GCM) \ 0) -#define PSA__ALG_AEAD_WITH_DEFAULT_TAG_LENGTH__CASE(alg, ref) \ - PSA_ALG_AEAD_WITH_TAG_LENGTH(alg, 0) == \ +#define PSA__ALG_AEAD_WITH_DEFAULT_TAG_LENGTH__CASE(aead_alg, ref) \ + PSA_ALG_AEAD_WITH_TAG_LENGTH(aead_alg, 0) == \ PSA_ALG_AEAD_WITH_TAG_LENGTH(ref, 0) ? \ ref : @@ -1169,20 +1150,11 @@ ((alg) & PSA_ALG_HASH_MASK) | PSA_ALG_CATEGORY_HASH : \ 0) -#define PSA_ALG_HKDF_BASE ((psa_algorithm_t)0x20000100) +#define PSA_ALG_HKDF_BASE ((psa_algorithm_t)0x30000100) /** Macro to build an HKDF algorithm. * * For example, `PSA_ALG_HKDF(PSA_ALG_SHA256)` is HKDF using HMAC-SHA-256. * - * This key derivation algorithm uses the following inputs: - * - #PSA_KDF_STEP_SALT is the salt used in the "extract" step. - * It is optional; if omitted, the derivation uses an empty salt. - * - #PSA_KDF_STEP_SECRET is the secret key used in the "extract" step. - * - #PSA_KDF_STEP_INFO is the info string used in the "expand" step. - * You must pass #PSA_KDF_STEP_SALT before #PSA_KDF_STEP_SECRET. - * You may pass #PSA_KDF_STEP_INFO at any time after steup and before - * starting to generate output. - * * \param hash_alg A hash algorithm (\c PSA_ALG_XXX value such that * #PSA_ALG_IS_HASH(\p hash_alg) is true). * @@ -1208,7 +1180,7 @@ #define PSA_ALG_HKDF_GET_HASH(hkdf_alg) \ (PSA_ALG_CATEGORY_HASH | ((hkdf_alg) & PSA_ALG_HASH_MASK)) -#define PSA_ALG_TLS12_PRF_BASE ((psa_algorithm_t)0x20000200) +#define PSA_ALG_TLS12_PRF_BASE ((psa_algorithm_t)0x30000200) /** Macro to build a TLS-1.2 PRF algorithm. * * TLS 1.2 uses a custom pseudorandom function (PRF) for key schedule, @@ -1247,7 +1219,7 @@ #define PSA_ALG_TLS12_PRF_GET_HASH(hkdf_alg) \ (PSA_ALG_CATEGORY_HASH | ((hkdf_alg) & PSA_ALG_HASH_MASK)) -#define PSA_ALG_TLS12_PSK_TO_MS_BASE ((psa_algorithm_t)0x20000300) +#define PSA_ALG_TLS12_PSK_TO_MS_BASE ((psa_algorithm_t)0x30000300) /** Macro to build a TLS-1.2 PSK-to-MasterSecret algorithm. * * In a pure-PSK handshake in TLS 1.2, the master secret is derived @@ -1287,48 +1259,51 @@ #define PSA_ALG_TLS12_PSK_TO_MS_GET_HASH(hkdf_alg) \ (PSA_ALG_CATEGORY_HASH | ((hkdf_alg) & PSA_ALG_HASH_MASK)) -#define PSA_ALG_KEY_DERIVATION_MASK ((psa_algorithm_t)0x080fffff) -#define PSA_ALG_KEY_AGREEMENT_MASK ((psa_algorithm_t)0x10f00000) +#define PSA_ALG_KEY_DERIVATION_MASK ((psa_algorithm_t)0x010fffff) -/** Macro to build a combined algorithm that chains a key agreement with - * a key derivation. +/** Use a shared secret as is. * - * \param ka_alg A key agreement algorithm (\c PSA_ALG_XXX value such - * that #PSA_ALG_IS_KEY_AGREEMENT(\p ka_alg) is true). - * \param kdf_alg A key derivation algorithm (\c PSA_ALG_XXX value such - * that #PSA_ALG_IS_KEY_DERIVATION(\p kdf_alg) is true). + * Specify this algorithm as the selection component of a key agreement + * to use the raw result of the key agreement as key material. * - * \return The corresponding key agreement and derivation - * algorithm. - * \return Unspecified if \p ka_alg is not a supported - * key agreement algorithm or \p kdf_alg is not a - * supported key derivation algorithm. + * \warning The raw result of a key agreement algorithm such as finite-field + * Diffie-Hellman or elliptic curve Diffie-Hellman has biases and should + * not be used directly as key material. It can however be used as the secret + * input in a key derivation algorithm. */ -#define PSA_ALG_KEY_AGREEMENT(ka_alg, kdf_alg) \ - ((ka_alg) | (kdf_alg)) +#define PSA_ALG_SELECT_RAW ((psa_algorithm_t)0x31000001) #define PSA_ALG_KEY_AGREEMENT_GET_KDF(alg) \ (((alg) & PSA_ALG_KEY_DERIVATION_MASK) | PSA_ALG_CATEGORY_KEY_DERIVATION) -#define PSA_ALG_KEY_AGREEMENT_GET_BASE(alg) \ - (((alg) & PSA_ALG_KEY_AGREEMENT_MASK) | PSA_ALG_CATEGORY_KEY_AGREEMENT) - -#define PSA_ALG_IS_RAW_KEY_AGREEMENT(alg) \ - (PSA_ALG_KEY_AGREEMENT_GET_KDF(alg) == PSA_ALG_CATEGORY_KEY_DERIVATION) +#define PSA_ALG_KEY_AGREEMENT_GET_BASE(alg) \ + ((alg) & ~PSA_ALG_KEY_DERIVATION_MASK) -#define PSA_ALG_IS_KEY_DERIVATION_OR_AGREEMENT(alg) \ - ((PSA_ALG_IS_KEY_DERIVATION(alg) || PSA_ALG_IS_KEY_AGREEMENT(alg))) - -/** The finite-field Diffie-Hellman (DH) key agreement algorithm. +#define PSA_ALG_FFDH_BASE ((psa_algorithm_t)0x22100000) +/** The Diffie-Hellman key agreement algorithm. + * + * This algorithm combines the finite-field Diffie-Hellman (DH) key + * agreement, also known as Diffie-Hellman-Merkle (DHM) key agreement, + * to produce a shared secret from a private key and the peer's + * public key, with a key selection or key derivation algorithm to produce + * one or more shared keys and other shared cryptographic material. * * The shared secret produced by key agreement and passed as input to the * derivation or selection algorithm \p kdf_alg is the shared secret * `g^{ab}` in big-endian format. * It is `ceiling(m / 8)` bytes long where `m` is the size of the prime `p` * in bits. + * + * \param kdf_alg A key derivation algorithm (\c PSA_ALG_XXX value such + * that #PSA_ALG_IS_KEY_DERIVATION(\p hash_alg) is true) + * or a key selection algorithm (\c PSA_ALG_XXX value such + * that #PSA_ALG_IS_KEY_SELECTION(\p hash_alg) is true). + * + * \return The Diffie-Hellman algorithm with the specified + * selection or derivation algorithm. */ -#define PSA_ALG_FFDH ((psa_algorithm_t)0x30100000) - +#define PSA_ALG_FFDH(kdf_alg) \ + (PSA_ALG_FFDH_BASE | ((kdf_alg) & PSA_ALG_KEY_DERIVATION_MASK)) /** Whether the specified algorithm is a finite field Diffie-Hellman algorithm. * * This includes every supported key selection or key agreement algorithm @@ -1341,11 +1316,18 @@ * key agreement algorithm identifier. */ #define PSA_ALG_IS_FFDH(alg) \ - (PSA_ALG_KEY_AGREEMENT_GET_BASE(alg) == PSA_ALG_FFDH) + (PSA_ALG_KEY_AGREEMENT_GET_BASE(alg) == PSA_ALG_FFDH_BASE) +#define PSA_ALG_ECDH_BASE ((psa_algorithm_t)0x22200000) /** The elliptic curve Diffie-Hellman (ECDH) key agreement algorithm. * - * The shared secret produced by key agreement is the x-coordinate of + * This algorithm combines the elliptic curve Diffie-Hellman key + * agreement to produce a shared secret from a private key and the peer's + * public key, with a key selection or key derivation algorithm to produce + * one or more shared keys and other shared cryptographic material. + * + * The shared secret produced by key agreement and passed as input to the + * derivation or selection algorithm \p kdf_alg is the x-coordinate of * the shared secret point. It is always `ceiling(m / 8)` bytes long where * `m` is the bit size associated with the curve, i.e. the bit size of the * order of the curve's coordinate field. When `m` is not a multiple of 8, @@ -1367,9 +1349,17 @@ * the shared secret is the x-coordinate of `d_A Q_B = d_B Q_A` * in big-endian byte order. * The bit size is `m` for the field `F_{2^m}`. + * + * \param kdf_alg A key derivation algorithm (\c PSA_ALG_XXX value such + * that #PSA_ALG_IS_KEY_DERIVATION(\p hash_alg) is true) + * or a selection algorithm (\c PSA_ALG_XXX value such + * that #PSA_ALG_IS_KEY_SELECTION(\p hash_alg) is true). + * + * \return The Diffie-Hellman algorithm with the specified + * selection or derivation algorithm. */ -#define PSA_ALG_ECDH ((psa_algorithm_t)0x30200000) - +#define PSA_ALG_ECDH(kdf_alg) \ + (PSA_ALG_ECDH_BASE | ((kdf_alg) & PSA_ALG_KEY_DERIVATION_MASK)) /** Whether the specified algorithm is an elliptic curve Diffie-Hellman * algorithm. * @@ -1384,25 +1374,7 @@ * key agreement algorithm identifier. */ #define PSA_ALG_IS_ECDH(alg) \ - (PSA_ALG_KEY_AGREEMENT_GET_BASE(alg) == PSA_ALG_ECDH) - -/** Whether the specified algorithm encoding is a wildcard. - * - * Wildcard values may only be used to set the usage algorithm field in - * a policy, not to perform an operation. - * - * \param alg An algorithm identifier (value of type #psa_algorithm_t). - * - * \return 1 if \c alg is a wildcard algorithm encoding. - * \return 0 if \c alg is a non-wildcard algorithm encoding (suitable for - * an operation). - * \return This macro may return either 0 or 1 if \c alg is not a supported - * algorithm identifier. - */ -#define PSA_ALG_IS_WILDCARD(alg) \ - (PSA_ALG_IS_HASH_AND_SIGN(alg) ? \ - PSA_ALG_SIGN_GET_HASH(alg) == PSA_ALG_ANY_HASH : \ - (alg) == PSA_ALG_ANY_HASH) + (PSA_ALG_KEY_AGREEMENT_GET_BASE(alg) == PSA_ALG_ECDH_BASE) /** Whether the specified algorithm encoding is a wildcard. * @@ -1514,34 +1486,4 @@ /**@}*/ -/** \defgroup derivation Key derivation - * @{ - */ - -/** A secret input for key derivation. - * - * This must be a key of type #PSA_KEY_TYPE_DERIVE. - */ -#define PSA_KDF_STEP_SECRET ((psa_key_derivation_step_t)0x0101) - -/** A label for key derivation. - * - * This must be a direct input. - */ -#define PSA_KDF_STEP_LABEL ((psa_key_derivation_step_t)0x0201) - -/** A salt for key derivation. - * - * This must be a direct input. - */ -#define PSA_KDF_STEP_SALT ((psa_key_derivation_step_t)0x0202) - -/** An information string for key derivation. - * - * This must be a direct input. - */ -#define PSA_KDF_STEP_INFO ((psa_key_derivation_step_t)0x0203) - -/**@}*/ - #endif /* PSA_CRYPTO_VALUES_H */ diff --git a/api-tests/README.md b/api-tests/README.md index 98f1bcd5..cb664216 100644 --- a/api-tests/README.md +++ b/api-tests/README.md @@ -1,38 +1,37 @@ -# PSA APIs : Architecture Test Suite +# PSA APIs Architecture Test Suite ## Introduction -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. One of the PSA goals is to make IoT security easier and quicker for everyone. This means having reliable, consistent APIs and useful built-in security functions for device manufacturers and the developer community. These PSA APIs provides a consistent developer experience hiding the underlying complexity of the security system. +Arm *Platform Security Architecture* (PSA) is a holistic set of threat models, security analyses, 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. One of the PSA goals is to make IoT security easier and quicker for everyone. This means having reliable, consistent APIs and useful built-in security functions for device manufacturers and the developer community. These PSA APIs provides a consistent developer experience, hiding the underlying complexity of the security system. -For more information, visit the PSA webpage [here](https://developer.arm.com/products/architecture/platform-security-architecture) +For more information, visit the [PSA Webpage](https://developer.arm.com/products/architecture/platform-security-architecture). ## Architecture test suite -The current implementation of the Architecture test suite contains tests for following PSA APIs specifications. - -The tests are available as open source. The tests and the corresponding abstraction layers are available with an Apache v2 license allowing for external contribution. +The current implementation of the architecture test suite contains tests for PSA Firmware Framework and PSA Developer APIs specifications. The tests are available as open source. The tests and the corresponding abstraction layers are available with an Apache v2.0 license, allowing external contribution. ### PSA Firmware Framework (PSA-FF) -The test suite for this specification is located in the ff directory of this repository. See [PSA Firmware Framework Readme](ff/README.md) file for more details. +The test suite for this specification is located in the **ff** directory of this repository. See the [PSA Firmware Framework README](ff/README.md) file for more details. ### PSA Developer APIs -The test suite for this specification is located in the dev_apis directory of this repository. See [PSA Developer APIs Readme](dev_apis/README.md) file for more details. +The test suite for this specification is located in the **dev_apis** directory of this repository. See the [PSA Developer APIs README](dev_apis/README.md) file for more details. ## Release Update - - Release Version - 0.8 - - Code Quality: Please use this opportunity to suggest enhancements and point out errors. - - Current release contains following tests: - 1. Developer APIs test list: + - Release Version: 0.9 + - Code quality: Arm welcomes suggestions for enhancements and error corrections. + - This release contains following tests:
+ +1. Developer APIs test list: | Test Category | Specification Version | Header File | |--------------------------|--------------------------------------|-----------------------------------------------------------------------------------| -| Crypto | PSA Crypto API 1.0 Beta-1 | [crypto.h](../api-specs/include/psa/crypto.h) | -| Protected Storage | PSA Protected Storage API 1.0 | [protected_storage.h](../api-specs/include/psa/protected_storage.h) | -| Internal Trusted Storage | PSA Internal Trusted Storage API 1.0 | [internal_trusted_storage.h](../api-specs/include/psa/internal_trusted_storage.h) | -| Initial Attestation | PSA Initial Attestation API 1.0 Beta-0 | [initial_attestation.h](../api-specs/include/psa/initial_attestation.h) | +| Crypto | PSA Crypto API 1.0-Beta([mbedcrypto-1.0.0](https://github.com/ARMmbed/mbed-crypto/tree/mbedcrypto-1.0.0)) | [crypto.h](../api-specs/include/psa/crypto.h) | +| Protected Storage | PSA Protected Storage API 1.0-Beta2 | [protected_storage.h](../api-specs/include/psa/protected_storage.h) | +| Internal Trusted Storage | PSA Internal Trusted Storage API 1.0-Beta2 | [internal_trusted_storage.h](../api-specs/include/psa/internal_trusted_storage.h) | +| Initial Attestation | PSA Initial Attestation API 1.0-Beta0 | [initial_attestation.h](../api-specs/include/psa/initial_attestation.h) | -2. PSA-FF **IPC tests** that are written for version 1.0-Beta-0 of the PSA FF specification. +2. PSA-FF tests that are written for version 1.0-Beta1 of the PSA FF specification. ## License diff --git a/api-tests/dev_apis/README.md b/api-tests/dev_apis/README.md index 4ada39ed..518fe60e 100644 --- a/api-tests/dev_apis/README.md +++ b/api-tests/dev_apis/README.md @@ -1,34 +1,26 @@ -# PSA Developer APIs : Architecture Test Suite +# PSA Developer APIs Architecture Test Suite -## Introduction +## PSA Developer APIs -### PSA Developer APIs +PSA defines security service APIs for application developers. Some of these services are Crypto, Attestation, and Secure storage. For more information on API specifications, refer to the [PSA Developer APIs Specifications](../../api-specs/). -PSA defines security service APIs for application developers. Some of these services are Crypto Services, Attestation Services, and Secure Storage Services. For more information on API specification, refer the [PSA Developer facing APIs specifications](../../api-specs/) +## Architecture test suite -### Architecture Test Suite +The architecture test suite is a set of examples of the invariant behaviors that are specified in the PSA Developer APIs specifications. Use this suite to verify whether these behaviors are implemented correctly in your system. This suite contains self-checking and portable C-based tests with directed stimulus. These tests are available as open source. The tests and the corresponding abstraction layers are available with an Apache v2.0 license, allowing external contribution. -The Architecture Test Suite is a set of examples of the invariant behaviours that are specified by the PSA Developer APIs Specifications. Use this suite to verify that these behaviours are implemented correctly in your system. +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. -The Architecture Test Suite contains the tests that are self-checking, portable C-based tests with directed stimulus. +For more information on the architecture test suite framework and methodology to run the tests, refer to the [Validation Methodology](../docs/Arm_PSA_APIs_Arch_Test_Validation_Methodology.pdf) document. -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. 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. +## Test scenarios -For more information on Architecture Test Suite specification, refer the [Validation Methodology](../docs/Arm_PSA_APIs_Arch_Test_Validation_Methodology.pdf) document. +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/) that is present in the **docs/** folder. -## Tests 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 - -## 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 - -Please make sure you have all required software installed as explained in the [software requirements](../docs/sw_requirements.md). +Follow the instructions in the subsequent sections to get a copy of the source code on your local machine and build the tests. Make sure you have all required software installed as explained in the [Software Requirements Document](../docs/sw_requirements.md). ### Porting steps @@ -36,49 +28,51 @@ Refer to the [PSA Developer APIs Test Suite Porting Guide](../docs/porting_guide ### Build steps -To build test suite for a given platform, execute the following commands: +To build the test suite for your target platform, execute the following commands: ``` cd api-tests -./tools/scripts/setup.sh --target --cpu_arch --suite --build --include +./tools/scripts/setup.sh --target --cpu_arch --suite --build --include --archive_tests ```
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 test binaries should be compiled. 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. Note- You must provide Developer APIs header file implementation to Test Suite build system using this option. For example - To compiler Crypto tests, include path must point to path where "psa/crypto.h" is located in your build system.
-- is an output directory to keep build files. +- 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 the tests should be compiled. For example, Armv7M, Armv8M-Baseline and Armv8M-Mainline Architecture.
+- is the suite name that is the same as the suite name available in **dev_apis/** directory.
+- is a directory to store build output files.
+- is an additional directory to be included into the compiler search path.
+Note: You must provide Developer APIs header file implementation to the test suite build system using this option. For example, to compile Crypto tests, the include path must point to the path where **psa/crypto.h** is located in your build system.
+- Use **--archive_tests** option to create a combined test archive (**test_combine.a**) file by combining available test object files. Absence of this option creates a combined test binary (**test_elf_combine.bin**) by combining the available test ELF files. -Refer ./tools/scripts/setup.sh --help to know more about options. +For details about options, refer to **./tools/scripts/setup.sh --help**. -*To compile crypto tests for tgt_dev_apis_mbedos_fvp_mps2_m4 platform* +To compile Crypto tests for **tgt_dev_apis_mbedos_fvp_mps2_m4** platform, execute the following commands: ``` cd api-tests -./tools/scripts/setup.sh --target tgt_dev_apis_mbedos_fvp_mps2_m4 --cpu_arch armv7m --suite crypto --build BUILD_CRYPTO --include +./tools/scripts/setup.sh --target tgt_dev_apis_mbedos_fvp_mps2_m4 --cpu_arch armv7m --suite crypto --build BUILD_CRYPTO --include --archive_tests ``` ### Build output -Test suite build generates following binaries:
+Building the test suite generates the following NSPE binaries:
+- **/BUILD/val/val_nspe.a** +- **/BUILD/platform/pal_nspe.a** +- **/BUILD/dev_apis//test_combine.a** -NSPE libraries:
-1. /BUILD/val/val_nspe.a -2. /BUILD/platform/pal_nspe.a -3. /BUILD/dev_apis//test_elf_combine.bin +### Integrating the libraries into your target platform -### Binaries integration into your platform +1. Integrate **val_nspe.a**, **pal_nspe.a**, and **test_combine.a** libraries with your Non-secure OS so that these libraries get access to the PSA Developer APIs. For example, Crypto tests require access to PSA Crypto APIs. This forms an NSPE binary. +2. Load the NSPE binary to the Non-secure memory. +3. Build your SPE binary and load into the Secure memory. -1. Integrate val_nspe.a and pal_nspe.a libraries with your non-secure OS so that these libraries get access to PSA Developer APIs. For example, Crypto tests would require to get access to PSA Crypto APIs. This will form a NSPE binary. -2. Load NSPE binary and test_elf_combine.bin to NS memory -3. Build your SPE binary and load into S memory +## Test suite execution +The following steps describe the execution flow before the test execution:
-## 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.
-3. Upon booting firmware and non-secure OS, the SUT - boot software would give control to the test suite entry point- *void val_entry(void);* as an non-secure application entry point.
+1. The target platform must load the above binaries into appropriate memory.
+3. Upon booting firmware and Non-secure OS, the SUT boot software gives control to the test suite entry point void **val_entry(void);** as an Non-secure application entry point.
4. The tests are executed sequentially in a loop in the test_dispatcher function.
+For details on test suite integration, refer to the **Integrating the test suite with the SUT** section of [Validation Methodology](../docs/Arm_PSA_APIs_Arch_Test_Validation_Methodology.pdf). + ## License Arm PSA test suite is distributed under Apache v2.0 License. @@ -94,4 +88,3 @@ Arm PSA test suite is distributed under Apache v2.0 License. -------------- *Copyright (c) 2018-2019, Arm Limited and Contributors. All rights reserved.* - diff --git a/api-tests/dev_apis/crypto/test_c002/test_c002.c b/api-tests/dev_apis/crypto/test_c002/test_c002.c index 19e1c44f..e5c15a5e 100644 --- a/api-tests/dev_apis/crypto/test_c002/test_c002.c +++ b/api-tests/dev_apis/crypto/test_c002/test_c002.c @@ -139,6 +139,10 @@ int32_t psa_import_key_test(security_t caller) { return VAL_STATUS_INVALID; } + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(13)); } return VAL_STATUS_SUCCESS; @@ -185,7 +189,7 @@ int32_t psa_import_key_negative_test(security_t caller) /* Import the key data into the occupied key slot */ status = val->crypto_function(VAL_CRYPTO_IMPORT_KEY, check2[i].key_handle, check2[i].key_type, check2[i].key_data, check2[i].key_length); - TEST_ASSERT_EQUAL(status, PSA_ERROR_OCCUPIED_SLOT, TEST_CHECKPOINT_NUM(5)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_ALREADY_EXISTS, TEST_CHECKPOINT_NUM(5)); val->print(PRINT_TEST, "[Check %d] Test psa_import_key with zero as key handle\n", g_test_count++); diff --git a/api-tests/dev_apis/crypto/test_c002/test_data.h b/api-tests/dev_apis/crypto/test_c002/test_data.h index aaa6840b..0b2faea1 100644 --- a/api-tests/dev_apis/crypto/test_c002/test_data.h +++ b/api-tests/dev_apis/crypto/test_c002/test_data.h @@ -166,7 +166,7 @@ static test_data check1[] = { #ifdef ARCH_TEST_AES_128 {"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}, + 0x5F, 0xC9, 0x77}, AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_CTR, BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_SUCCESS }, @@ -175,7 +175,7 @@ static test_data check1[] = { #ifdef ARCH_TEST_AES_192 {"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}, + 0x40, 0xAC, 0xA3, 0x90, 0x77, 0x83, 0x52, 0x31, 0x74, 0xF9, 0x05}, AES_24B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_CTR, BYTES_TO_BITS(AES_24B_KEY_SIZE), AES_24B_KEY_SIZE, PSA_SUCCESS }, @@ -269,7 +269,7 @@ static test_data check1[] = { {"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}, + 0x40, 0xAC, 0xA3, 0x90, 0x77}, AES_18B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_CTR, BYTES_TO_BITS(AES_18B_KEY_SIZE), AES_18B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT }, @@ -277,7 +277,7 @@ 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}, + 0x40, 0xAC, 0xA3, 0x90, 0x77, 0x83, 0x52, 0x31, 0x74, 0xF9, 0x05}, AES_24B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_CTR, BYTES_TO_BITS(AES_24B_KEY_SIZE), AES_24B_KEY_SIZE, PSA_ERROR_NOT_SUPPORTED, }, @@ -289,9 +289,9 @@ static test_data check2[] = { #ifdef ARCH_TEST_AES_128 {"Test psa_import_key negative cases\n", 1, PSA_KEY_TYPE_AES, {0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, - 0x5F, 0xC9}, + 0x5F, 0xC9, 0x77}, AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_CTR, -BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_OCCUPIED_SLOT +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_ALREADY_EXISTS }, #endif #endif diff --git a/api-tests/dev_apis/crypto/test_c003/test_c003.c b/api-tests/dev_apis/crypto/test_c003/test_c003.c index e25d80dd..1968cb15 100644 --- a/api-tests/dev_apis/crypto/test_c003/test_c003.c +++ b/api-tests/dev_apis/crypto/test_c003/test_c003.c @@ -112,8 +112,8 @@ int32_t psa_export_key_test(security_t caller) &key_type, &bits); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(6)); + /* Make sure the metada matches with the given data */ TEST_ASSERT_EQUAL(key_type, check1[i].key_type, TEST_CHECKPOINT_NUM(7)); - TEST_ASSERT_EQUAL(bits, check1[i].expected_bit_length, TEST_CHECKPOINT_NUM(8)); /* Export a key in binary format */ @@ -124,6 +124,7 @@ int32_t psa_export_key_test(security_t caller) if (check1[i].expected_status != PSA_SUCCESS) continue; + /* Check if the key length matches with the given length */ TEST_ASSERT_EQUAL(length, check1[i].expected_key_length, TEST_CHECKPOINT_NUM(10)); /* Check if original key data matches with the exported data */ @@ -177,7 +178,7 @@ int32_t psa_export_key_negative_test(security_t caller) /* Export a key in binary format */ status = val->crypto_function(VAL_CRYPTO_EXPORT_KEY, check2[i].key_handle, data, check2[i].key_length, &length); - TEST_ASSERT_EQUAL(status, PSA_ERROR_EMPTY_SLOT, TEST_CHECKPOINT_NUM(5)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_DOES_NOT_EXIST, TEST_CHECKPOINT_NUM(5)); val->print(PRINT_TEST, "[Check %d] Test psa_export_key with zero as key handle\n", g_test_count++); diff --git a/api-tests/dev_apis/crypto/test_c003/test_data.h b/api-tests/dev_apis/crypto/test_c003/test_data.h index c5832c7d..dfe0a0b6 100644 --- a/api-tests/dev_apis/crypto/test_c003/test_data.h +++ b/api-tests/dev_apis/crypto/test_c003/test_data.h @@ -165,7 +165,7 @@ static test_data check1[] = { #ifdef ARCH_TEST_AES_128 {"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}, + 0x5F, 0xC9, 0x77}, AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_CTR, BUFFER_SIZE, BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_SUCCESS }, @@ -174,7 +174,7 @@ static test_data check1[] = { #ifdef ARCH_TEST_AES_192 {"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}, + 0x40, 0xAC, 0xA3, 0x90, 0x77, 0x83, 0x52, 0x31, 0x74, 0xF9, 0x05}, AES_24B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_CTR, BUFFER_SIZE, BYTES_TO_BITS(AES_24B_KEY_SIZE), AES_24B_KEY_SIZE, PSA_SUCCESS }, @@ -260,14 +260,14 @@ static test_data check1[] = { #ifdef ARCH_TEST_AES_128 {"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}, + 0x5F, 0xC9, 0x05}, AES_16B_KEY_SIZE, PSA_KEY_USAGE_VERIFY, PSA_ALG_CTR, BUFFER_SIZE, BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_NOT_PERMITTED }, {"Test psa_export_key with less buffer size\n", 12, PSA_KEY_TYPE_AES, {0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, - 0x5F, 0xC9}, + 0x5F, 0xC9, 0x05}, AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_CTR, 14, BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_BUFFER_TOO_SMALL }, @@ -280,7 +280,7 @@ static test_data check2[] = { #ifdef ARCH_TEST_AES_128 {"Test psa_export_key negative case\n", 13, PSA_KEY_TYPE_AES, {0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, - 0x5F, 0xC9}, + 0x5F, 0xC9, 0x05}, AES_16B_KEY_SIZE, PSA_KEY_USAGE_EXPORT, PSA_ALG_CTR, BUFFER_SIZE, BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_SUCCESS }, diff --git a/api-tests/dev_apis/crypto/test_c004/test_c004.c b/api-tests/dev_apis/crypto/test_c004/test_c004.c index fceade54..d99c4837 100644 --- a/api-tests/dev_apis/crypto/test_c004/test_c004.c +++ b/api-tests/dev_apis/crypto/test_c004/test_c004.c @@ -185,7 +185,7 @@ int32_t test_psa_export_public_key_handle(security_t caller) /* Export a key in binary format */ status = val->crypto_function(VAL_CRYPTO_EXPORT_PUBLIC_KEY, check2[i].key_handle, data, check2[i].key_length, &length); - TEST_ASSERT_EQUAL(status, PSA_ERROR_EMPTY_SLOT, TEST_CHECKPOINT_NUM(5)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_DOES_NOT_EXIST, TEST_CHECKPOINT_NUM(5)); val->print(PRINT_TEST, "[Check %d] Test psa_export_key with zero as key handle\n", g_test_count++); diff --git a/api-tests/dev_apis/crypto/test_c005/test_c005.c b/api-tests/dev_apis/crypto/test_c005/test_c005.c index f5881929..1af6b369 100644 --- a/api-tests/dev_apis/crypto/test_c005/test_c005.c +++ b/api-tests/dev_apis/crypto/test_c005/test_c005.c @@ -115,8 +115,8 @@ int32_t psa_destroy_key_test(security_t caller) PSA_SUCCESS, TEST_CHECKPOINT_NUM(6)); + /* Check that metadata matches with given data */ TEST_ASSERT_EQUAL(key_type, check1[i].key_type, TEST_CHECKPOINT_NUM(7)); - TEST_ASSERT_EQUAL(bits, check1[i].expected_bit_length, TEST_CHECKPOINT_NUM(8)); /* Destroy a key and restore the slot to its default state */ diff --git a/api-tests/dev_apis/crypto/test_c006/test_c006.c b/api-tests/dev_apis/crypto/test_c006/test_c006.c index ca6d5258..a18aaa79 100644 --- a/api-tests/dev_apis/crypto/test_c006/test_c006.c +++ b/api-tests/dev_apis/crypto/test_c006/test_c006.c @@ -113,8 +113,8 @@ int32_t psa_get_key_information_test(security_t caller) if (check1[i].expected_status != PSA_SUCCESS) continue; + /* Check that it matches with given data */ TEST_ASSERT_EQUAL(key_type, check1[i].key_type, TEST_CHECKPOINT_NUM(7)); - TEST_ASSERT_EQUAL(bits, check1[i].expected_bit_length, TEST_CHECKPOINT_NUM(8)); } @@ -159,7 +159,7 @@ int32_t psa_get_key_information_invalid_test(security_t caller) /* Get basic metadata about a key */ status = val->crypto_function(VAL_CRYPTO_GET_KEY_INFORMATION, check2[i].key_handle, &check2[i].key_type, &check2[i].key_length); - TEST_ASSERT_EQUAL(status, PSA_ERROR_EMPTY_SLOT, TEST_CHECKPOINT_NUM(6)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_DOES_NOT_EXIST, TEST_CHECKPOINT_NUM(6)); val->print(PRINT_TEST, "[Check %d] Test psa_get_key_information with destroyed" " key handle\n", g_test_count++); diff --git a/api-tests/dev_apis/crypto/test_c006/test_data.h b/api-tests/dev_apis/crypto/test_c006/test_data.h index e4ae0774..ccc52e2c 100644 --- a/api-tests/dev_apis/crypto/test_c006/test_data.h +++ b/api-tests/dev_apis/crypto/test_c006/test_data.h @@ -255,10 +255,9 @@ static test_data check1[] = { 224, 28, PSA_SUCCESS }, #endif -#endif +#endif }; - static test_data check2[] = { #ifdef ARCH_TEST_CIPER_MODE_CTR #ifdef ARCH_TEST_AES_128 diff --git a/api-tests/dev_apis/crypto/test_c007/test_c007.c b/api-tests/dev_apis/crypto/test_c007/test_c007.c index 82a77c27..9b3371fc 100644 --- a/api-tests/dev_apis/crypto/test_c007/test_c007.c +++ b/api-tests/dev_apis/crypto/test_c007/test_c007.c @@ -181,7 +181,7 @@ int32_t psa_set_key_policy_negative_test(security_t caller) /* Set the usage policy on a key slot */ status = val->crypto_function(VAL_CRYPTO_SET_KEY_POLICY, check2[i].key_handle, &policy); - TEST_ASSERT_EQUAL(status, PSA_ERROR_OCCUPIED_SLOT, TEST_CHECKPOINT_NUM(8)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_ALREADY_EXISTS, TEST_CHECKPOINT_NUM(8)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c007/test_data.h b/api-tests/dev_apis/crypto/test_c007/test_data.h index 183c3b56..75726963 100644 --- a/api-tests/dev_apis/crypto/test_c007/test_data.h +++ b/api-tests/dev_apis/crypto/test_c007/test_data.h @@ -277,7 +277,7 @@ static test_data check2[] = { {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_CTR, -BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_OCCUPIED_SLOT +BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_ALREADY_EXISTS }, #endif #endif diff --git a/api-tests/dev_apis/crypto/test_c008/test_c008.c b/api-tests/dev_apis/crypto/test_c008/test_c008.c index 05ce0f81..363d5c76 100644 --- a/api-tests/dev_apis/crypto/test_c008/test_c008.c +++ b/api-tests/dev_apis/crypto/test_c008/test_c008.c @@ -53,6 +53,8 @@ int32_t psa_get_key_policy_test(security_t caller) * usage of the key */ val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &policy); + memset(&expected_usage, 0, sizeof(psa_key_usage_t)); + memset(&expected_alg, 0, sizeof(psa_algorithm_t)); /* Setting up the watchdog timer for each check */ status = val->wd_reprogram_timer(WD_CRYPTO_TIMEOUT); diff --git a/api-tests/dev_apis/crypto/test_c009/test_c009.c b/api-tests/dev_apis/crypto/test_c009/test_c009.c index aa4af152..9b252b88 100644 --- a/api-tests/dev_apis/crypto/test_c009/test_c009.c +++ b/api-tests/dev_apis/crypto/test_c009/test_c009.c @@ -55,6 +55,9 @@ int32_t psa_allocate_key_test(security_t caller) status = val->crypto_function(VAL_CRYPTO_ALLOCATE_KEY, &check1[i].key_handle); TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(3)); + /* Destroy a key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(4)); } return VAL_STATUS_SUCCESS; @@ -62,7 +65,7 @@ int32_t psa_allocate_key_test(security_t caller) int32_t psa_allocate_key_negative_test(security_t caller) { - int32_t i, status; + int32_t i, j, status; psa_key_handle_t key_handle[MAX_KEYS]; /* Initialize the PSA crypto library*/ @@ -76,10 +79,18 @@ int32_t psa_allocate_key_negative_test(security_t caller) /* Allocate a key slot for a transient key */ status = val->crypto_function(VAL_CRYPTO_ALLOCATE_KEY, &key_handle[i]); if (status != PSA_SUCCESS) + { + TEST_ASSERT_EQUAL(status, PSA_ERROR_INSUFFICIENT_MEMORY, TEST_CHECKPOINT_NUM(2)); break; + } } - TEST_ASSERT_EQUAL(status, PSA_ERROR_INSUFFICIENT_MEMORY, TEST_CHECKPOINT_NUM(2)); + for (j = 0; j < i; j++) + { + /* Destroy a key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, key_handle[j]); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(3)); + } return VAL_STATUS_SUCCESS; } diff --git a/api-tests/dev_apis/crypto/test_c009/test_data.h b/api-tests/dev_apis/crypto/test_c009/test_data.h index 4a859864..fd4be3c2 100644 --- a/api-tests/dev_apis/crypto/test_c009/test_data.h +++ b/api-tests/dev_apis/crypto/test_c009/test_data.h @@ -20,67 +20,53 @@ typedef struct { char test_desc[75]; psa_key_handle_t key_handle; - psa_key_type_t key_type; - size_t key_length; psa_status_t expected_status; } test_data; static test_data check1[] = { #ifdef ARCH_TEST_AES_128 -{"Test psa_allocate_key 16 Byte AES\n", 1, PSA_KEY_TYPE_AES, - AES_16B_KEY_SIZE, PSA_SUCCESS +{"Test psa_allocate_key 16 Byte AES\n", 1, PSA_SUCCESS }, #endif #ifdef ARCH_TEST_AES_192 -{"Test psa_allocate_key 24 Byte AES\n", 2, PSA_KEY_TYPE_AES, - AES_24B_KEY_SIZE, PSA_SUCCESS +{"Test psa_allocate_key 24 Byte AES\n", 2, PSA_SUCCESS }, #endif #ifdef ARCH_TEST_AES_256 -{"Test psa_allocate_key 32 Byte AES\n", 3, PSA_KEY_TYPE_AES, - AES_32B_KEY_SIZE, PSA_SUCCESS +{"Test psa_allocate_key 32 Byte AES\n", 3, PSA_SUCCESS }, #endif #ifdef ARCH_TEST_RSA_2048 -{"Test psa_allocate_key 2048 RSA public key\n", 4, PSA_KEY_TYPE_RSA_PUBLIC_KEY, - 294, PSA_SUCCESS +{"Test psa_allocate_key 2048 RSA public key\n", 4, PSA_SUCCESS }, -{"Test psa_allocate_key with RSA 2048 keypair\n", 5, PSA_KEY_TYPE_RSA_KEYPAIR, - 1193, PSA_SUCCESS +{"Test psa_allocate_key with RSA 2048 keypair\n", 5, PSA_SUCCESS, }, #endif #ifdef ARCH_TEST_DES_1KEY -{"Test psa_allocate_key with DES 64 bit key\n", 6, PSA_KEY_TYPE_DES, - DES_8B_KEY_SIZE, PSA_SUCCESS +{"Test psa_allocate_key with DES 64 bit key\n", 6, PSA_SUCCESS, }, #endif #ifdef ARCH_TEST_DES_2KEY -{"Test psa_allocate_key with Triple DES 2-Key\n", 7, PSA_KEY_TYPE_DES, - DES3_2KEY_SIZE, PSA_SUCCESS +{"Test psa_allocate_key with Triple DES 2-Key\n", 7, PSA_SUCCESS, }, #endif #ifdef ARCH_TEST_DES_3KEY -{"Test psa_allocate_key with Triple DES 3-Key\n", 8, PSA_KEY_TYPE_DES, - DES3_3KEY_SIZE, PSA_SUCCESS +{"Test psa_allocate_key with Triple DES 3-Key\n", 8, PSA_SUCCESS, }, #endif #ifdef ARCH_TEST_ECC_CURVE_SECP192R1 -{"Test psa_allocate_key with EC Public key\n", 9, - PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE | PSA_ECC_CURVE_SECP192R1, - 75, PSA_SUCCESS +{"Test psa_allocate_key with EC Public key\n", 9, PSA_SUCCESS, }, -{"Test psa_allocate_key with EC keypair\n", 10, - PSA_KEY_TYPE_ECC_KEYPAIR_BASE | PSA_ECC_CURVE_SECP192R1, - 97, PSA_SUCCESS +{"Test psa_allocate_key with EC keypair\n", 10, PSA_SUCCESS }, #endif }; diff --git a/api-tests/dev_apis/crypto/test_c011/test_c011.c b/api-tests/dev_apis/crypto/test_c011/test_c011.c index 7e4b4dff..13176a2e 100644 --- a/api-tests/dev_apis/crypto/test_c011/test_c011.c +++ b/api-tests/dev_apis/crypto/test_c011/test_c011.c @@ -43,6 +43,8 @@ int32_t psa_hash_setup_test(security_t caller) { val->print(PRINT_TEST, "[Check %d] ", g_test_count++); val->print(PRINT_TEST, check1[i].test_desc, 0); + + /* Initialize the structure */ memset(&operation, 0, sizeof(operation)); /* Setting up the watchdog timer for each check */ @@ -53,9 +55,16 @@ int32_t psa_hash_setup_test(security_t caller) status = val->crypto_function(VAL_CRYPTO_HASH_SETUP, &operation, check1[i].alg); TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(3)); - /*Abort the hash operation */ - status = val->crypto_function(VAL_CRYPTO_HASH_ABORT, &operation); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(4)); + if (check1[i].expected_status == PSA_SUCCESS) + { + /* Start a multipart hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_SETUP, &operation, check1[i].alg); + TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(4)); + + /*Abort the hash operation */ + status = val->crypto_function(VAL_CRYPTO_HASH_ABORT, &operation); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(5)); + } } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c011/test_data.h b/api-tests/dev_apis/crypto/test_c011/test_data.h index fab7bd59..64b233b7 100644 --- a/api-tests/dev_apis/crypto/test_c011/test_data.h +++ b/api-tests/dev_apis/crypto/test_c011/test_data.h @@ -114,7 +114,16 @@ static test_data check1[] = { }, #endif +{"Test psa_hash_setup with Invalid hash algorithm\n", + PSA_HASH_ALG_INVALID, PSA_ERROR_NOT_SUPPORTED, +}, + {"Test psa_hash_setup with Invalid algorithm\n", PSA_ALG_INVALID, PSA_ERROR_INVALID_ARGUMENT, }, + +{"Test psa_hash_setup with CTR algorithm\n", + PSA_ALG_CTR, PSA_ERROR_INVALID_ARGUMENT, +}, + }; diff --git a/api-tests/dev_apis/crypto/test_c012/test_c012.c b/api-tests/dev_apis/crypto/test_c012/test_c012.c index 5a3181cb..a02f8ada 100644 --- a/api-tests/dev_apis/crypto/test_c012/test_c012.c +++ b/api-tests/dev_apis/crypto/test_c012/test_c012.c @@ -45,6 +45,8 @@ int32_t psa_hash_update_test(security_t caller) { val->print(PRINT_TEST, "[Check %d] ", g_test_count++); val->print(PRINT_TEST, check1[i].test_desc, 0); + + /* Initialize the hash structure */ memset(&operation, 0, sizeof(operation)); /* Setting up the watchdog timer for each check */ @@ -72,7 +74,7 @@ int32_t psa_hash_update_invalid_handle(security_t caller) { psa_hash_operation_t operation; uint8_t input[] = "Hello World"; - size_t input_length = sizeof(input)/sizeof(input[0]); + size_t input_length = sizeof(input); int32_t status; /* Initialize the PSA crypto library*/ @@ -81,6 +83,8 @@ int32_t psa_hash_update_invalid_handle(security_t caller) val->print(PRINT_TEST, "[Check %d] ", g_test_count++); val->print(PRINT_TEST, "Test psa_hash_update without hash setup\n", 0); + + /* Initialize the hash structure */ memset(&operation, 0, sizeof(operation)); /* Setting up the watchdog timer for each check */ @@ -102,13 +106,13 @@ int32_t psa_hash_update_with_completed_handle(security_t caller) { psa_hash_operation_t operation; uint8_t input[] = {0xbd}; - size_t input_length = sizeof(input)/sizeof(input[0]); + size_t input_length = sizeof(input); 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]); + size_t hash_length = sizeof(hash); int32_t status; /* Initialize the PSA crypto library*/ diff --git a/api-tests/dev_apis/crypto/test_c014/test_c014.c b/api-tests/dev_apis/crypto/test_c014/test_c014.c index 48e3c5ab..272e0e0f 100644 --- a/api-tests/dev_apis/crypto/test_c014/test_c014.c +++ b/api-tests/dev_apis/crypto/test_c014/test_c014.c @@ -38,7 +38,7 @@ int32_t psa_hash_finish_test(security_t caller) psa_hash_operation_t operation; const char *expected_hash; char hash[HASH_64B]; - size_t hash_length, hash_size = sizeof(hash)/sizeof(hash[0]); + size_t hash_length, hash_size = sizeof(hash); /* Initialize the PSA crypto library*/ status = val->crypto_function(VAL_CRYPTO_INIT); @@ -77,14 +77,10 @@ int32_t psa_hash_finish_test(security_t caller) if (check1[i].expected_status != PSA_SUCCESS) { - /*Abort the hash operation */ - status = val->crypto_function(VAL_CRYPTO_HASH_ABORT, &operation); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(6)); continue; } TEST_ASSERT_EQUAL(hash_length, PSA_HASH_SIZE(check1[i].alg), TEST_CHECKPOINT_NUM(7)); - TEST_ASSERT_MEMCMP(hash, expected_hash, hash_length, TEST_CHECKPOINT_NUM(8)); /*Abort the hash operation */ @@ -102,7 +98,7 @@ int32_t psa_hash_finish_inactive_operation_handle(security_t caller) 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]); + size_t hash_length, hash_size = sizeof(hash); int32_t status; /* Initialize the PSA crypto library*/ diff --git a/api-tests/dev_apis/crypto/test_c015/test_c015.c b/api-tests/dev_apis/crypto/test_c015/test_c015.c index 208d7b56..8f725f3e 100644 --- a/api-tests/dev_apis/crypto/test_c015/test_c015.c +++ b/api-tests/dev_apis/crypto/test_c015/test_c015.c @@ -74,7 +74,7 @@ int32_t psa_hash_abort_before_operation_finish(security_t caller) 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]); + size_t hash_length, hash_size = sizeof(hash); int32_t status; /* Initialize the PSA crypto library*/ diff --git a/api-tests/dev_apis/crypto/test_c016/test_c016.c b/api-tests/dev_apis/crypto/test_c016/test_c016.c index 5862ba56..9781827a 100644 --- a/api-tests/dev_apis/crypto/test_c016/test_c016.c +++ b/api-tests/dev_apis/crypto/test_c016/test_c016.c @@ -76,23 +76,33 @@ int32_t psa_generate_key_test(security_t caller) TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(5)); if (check1[i].expected_status != PSA_SUCCESS) + { + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(6)); + continue; + } /* Get basic metadata about a key */ status = val->crypto_function(VAL_CRYPTO_GET_KEY_INFORMATION, check1[i].key_handle, &key_type, &bits); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(6)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(7)); - TEST_ASSERT_EQUAL(key_type, check1[i].key_type, TEST_CHECKPOINT_NUM(7)); + TEST_ASSERT_EQUAL(key_type, check1[i].key_type, TEST_CHECKPOINT_NUM(8)); - TEST_ASSERT_EQUAL(bits, check1[i].expected_bit_length, TEST_CHECKPOINT_NUM(8)); + TEST_ASSERT_EQUAL(bits, check1[i].expected_bit_length, TEST_CHECKPOINT_NUM(9)); /* Export a key in binary format */ status = val->crypto_function(VAL_CRYPTO_EXPORT_KEY, check1[i].key_handle, data, BUFFER_SIZE, &length); - TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(9)); + TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(10)); + + TEST_ASSERT_EQUAL(length, check1[i].expected_key_length, TEST_CHECKPOINT_NUM(11)); - TEST_ASSERT_EQUAL(length, check1[i].expected_key_length, TEST_CHECKPOINT_NUM(10)); + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(12)); } return VAL_STATUS_SUCCESS; @@ -156,7 +166,7 @@ int32_t psa_generate_key_negative_test(security_t caller) /* Generate a key or key pair */ status = val->crypto_function(VAL_CRYPTO_GENERATE_KEY, check2[i].key_handle, check2[i].key_type, check2[i].bits, check2[i].extra, check2[i].extra_size); - TEST_ASSERT_EQUAL(status, PSA_ERROR_OCCUPIED_SLOT, TEST_CHECKPOINT_NUM(8)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_ALREADY_EXISTS, TEST_CHECKPOINT_NUM(8)); val->print(PRINT_TEST, "[Check %d] Test psa_generate_key with destroyed key handle\n", g_test_count++); diff --git a/api-tests/dev_apis/crypto/test_c017/test_c017.c b/api-tests/dev_apis/crypto/test_c017/test_c017.c index 10387b8d..8aeab9c9 100644 --- a/api-tests/dev_apis/crypto/test_c017/test_c017.c +++ b/api-tests/dev_apis/crypto/test_c017/test_c017.c @@ -28,13 +28,14 @@ client_test_t test_c017_crypto_list[] = { }; static int g_test_count = 1; +static uint8_t data[BUFFER_SIZE], changed[BUFFER_SIZE]; int32_t psa_generate_random_test(security_t caller) { - int num_checks = sizeof(check1)/sizeof(check1[0]); - uint32_t i, j, data_sum; - uint8_t data[BUFFER_SIZE] = {0}; - int32_t status; + int num_checks = sizeof(check1)/sizeof(check1[0]); + uint32_t i, j, run; + uint8_t trail[] = "don't overwrite me"; + int32_t status; /* Initialize the PSA crypto library*/ status = val->crypto_function(VAL_CRYPTO_INIT); @@ -45,29 +46,44 @@ int32_t psa_generate_random_test(security_t caller) val->print(PRINT_TEST, "[Check %d] ", g_test_count++); val->print(PRINT_TEST, check1[i].test_desc, 0); + memset(data, 0, sizeof(data)); + memcpy(data + check1[i].size, trail, sizeof(trail)); + /* Setting up the watchdog timer for each check */ status = val->wd_reprogram_timer(WD_CRYPTO_TIMEOUT); TEST_ASSERT_EQUAL(status, VAL_STATUS_SUCCESS, TEST_CHECKPOINT_NUM(2)); - /* Generate random bytes */ - status = val->crypto_function(VAL_CRYPTO_GENERATE_RANDOM, data, check1[i].size); - TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(3)); + /* Run several times, to ensure that every output byte will be + * nonzero at least once with overwhelming probability + * (2^(-8*number_of_runs)). + */ + for (run = 0; run < 10; run++) + { + memset(data, 0, check1[i].size); + + /* Generate random bytes */ + status = val->crypto_function(VAL_CRYPTO_GENERATE_RANDOM, data, check1[i].size); + TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(3)); - if (check1[i].expected_status != PSA_SUCCESS) - continue; + /* Check that no more than bytes have been overwritten */ + status = memcmp(data + check1[i].size, trail, sizeof(trail)); + TEST_ASSERT_EQUAL(status, 0, TEST_CHECKPOINT_NUM(4)); - data_sum = 0; - /* Check that if generated data are zero */ + for (j = 0; j < check1[i].size; j++) + { + if (data[j] != 0) + ++changed[j]; + } + } + + /* Check that every byte was changed to nonzero at least once. This + * validates that psa_generate_random is overwriting every byte of + * the output buffer. + */ for (j = 0; j < check1[i].size; j++) { - data_sum += data[j]; - data[j] = 0; + TEST_ASSERT_NOT_EQUAL(changed[j], 0, TEST_CHECKPOINT_NUM(5)); } - - if (check1[i].size != 0) - TEST_ASSERT_NOT_EQUAL(data_sum, 0, TEST_CHECKPOINT_NUM(4)); - else - TEST_ASSERT_EQUAL(data_sum, 0, TEST_CHECKPOINT_NUM(5)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c017/test_data.h b/api-tests/dev_apis/crypto/test_c017/test_data.h index 4e0c46cf..53a7c24f 100644 --- a/api-tests/dev_apis/crypto/test_c017/test_data.h +++ b/api-tests/dev_apis/crypto/test_c017/test_data.h @@ -50,7 +50,4 @@ static test_data check1[] = { {"Test psa_generate_random to get 1000 Byte data\n", 100, PSA_SUCCESS }, - -{"Test psa_generate_random to get 1024 Byte data\n", 1024, PSA_SUCCESS -}, }; diff --git a/api-tests/dev_apis/crypto/test_c018/test_c018.c b/api-tests/dev_apis/crypto/test_c018/test_c018.c index 971c6de3..0bdf91a7 100644 --- a/api-tests/dev_apis/crypto/test_c018/test_c018.c +++ b/api-tests/dev_apis/crypto/test_c018/test_c018.c @@ -24,6 +24,7 @@ client_test_t test_c018_crypto_list[] = { NULL, psa_generator_read_test, + psa_generator_read_negative_test, NULL, }; @@ -95,6 +96,11 @@ int32_t psa_generator_read_test(security_t caller) /* Abort a generator */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_ABORT, &generator); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(8)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(9)); + continue; } @@ -106,7 +112,7 @@ int32_t psa_generator_read_test(security_t caller) } memset(data, 0, sizeof(data)); - TEST_ASSERT_NOT_EQUAL(data_sum, 0, TEST_CHECKPOINT_NUM(9)); + TEST_ASSERT_NOT_EQUAL(data_sum, 0, TEST_CHECKPOINT_NUM(10)); remaining_size = check1[i].capacity - check1[i].size; if (remaining_size > 0) @@ -114,7 +120,7 @@ int32_t psa_generator_read_test(security_t caller) /* Read some data from a generator */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_READ, &generator, data, remaining_size); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(11)); data_sum = 0; /* Check that if generated data are zero */ @@ -124,23 +130,52 @@ int32_t psa_generator_read_test(security_t caller) } memset(data, 0, sizeof(data)); - TEST_ASSERT_NOT_EQUAL(data_sum, 0, TEST_CHECKPOINT_NUM(11)); + TEST_ASSERT_NOT_EQUAL(data_sum, 0, TEST_CHECKPOINT_NUM(12)); /* Read some data from a generator */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_READ, &generator, data, check1[i].size); - TEST_ASSERT_EQUAL(status, PSA_ERROR_INSUFFICIENT_CAPACITY, TEST_CHECKPOINT_NUM(12)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_INSUFFICIENT_DATA, TEST_CHECKPOINT_NUM(13)); } - /* Read data using invalid generator handle */ - status = val->crypto_function(VAL_CRYPTO_GENERATOR_READ, &invalid_generator, - data, 1); - TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(13)); - /* Abort a generator */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_ABORT, &generator); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(14)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(15)); + } + + return VAL_STATUS_SUCCESS; +} + +int32_t psa_generator_read_negative_test(security_t caller) +{ + uint32_t i; + psa_crypto_generator_t generator[] = {psa_crypto_generator_init(), + PSA_CRYPTO_GENERATOR_INIT, {0} }; + uint32_t generator_count = sizeof(generator)/sizeof(generator[0]); + int32_t status; + + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, "Test psa_generator_read without setup\n", 0); + + /* Initialize the PSA crypto library*/ + status = val->crypto_function(VAL_CRYPTO_INIT); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(1)); + + memset(data, 0, sizeof(data)); + + for (i = 0; i < generator_count; i++) + { + status = val->crypto_function(VAL_CRYPTO_GENERATOR_READ, &generator[i], data, 1); + TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(2)); + + status = val->crypto_function(VAL_CRYPTO_GENERATOR_ABORT, &generator[i]); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(3)); } return VAL_STATUS_SUCCESS; } + diff --git a/api-tests/dev_apis/crypto/test_c018/test_c018.h b/api-tests/dev_apis/crypto/test_c018/test_c018.h index 34f72c8d..bd196511 100644 --- a/api-tests/dev_apis/crypto/test_c018/test_c018.h +++ b/api-tests/dev_apis/crypto/test_c018/test_c018.h @@ -27,4 +27,5 @@ extern psa_api_t *psa; extern client_test_t test_c018_crypto_list[]; int32_t psa_generator_read_test(security_t caller); +int32_t psa_generator_read_negative_test(security_t caller); #endif /* _TEST_C018_CLIENT_TESTS_H_ */ diff --git a/api-tests/dev_apis/crypto/test_c018/test_data.h b/api-tests/dev_apis/crypto/test_c018/test_data.h index 9506e173..f2c136e1 100644 --- a/api-tests/dev_apis/crypto/test_c018/test_data.h +++ b/api-tests/dev_apis/crypto/test_c018/test_data.h @@ -76,7 +76,7 @@ static test_data check1[] = { {0x70, 0x24, 0x55, 0x0C, 0x14, 0x9D, 0xED, 0x29}, DES_8B_KEY_SIZE, PSA_KEY_USAGE_DERIVE, PSA_ALG_HKDF(PSA_ALG_SHA_1), {0}, 0, {0}, 0, 64, - 70, PSA_ERROR_INSUFFICIENT_CAPACITY + 70, PSA_ERROR_INSUFFICIENT_DATA }, {"Test psa_generator_read to request maximum capacity\n", 4, PSA_KEY_TYPE_DERIVE, @@ -90,7 +90,7 @@ static test_data check1[] = { {0x70, 0x24, 0x55, 0x0C, 0x14, 0x9D, 0xED, 0x29}, DES_8B_KEY_SIZE, PSA_KEY_USAGE_DERIVE, PSA_ALG_HKDF(PSA_ALG_SHA_1), {0}, 0, {0}, 0, (255 * 20), - ((255 * 20) + 1), PSA_ERROR_INSUFFICIENT_CAPACITY + ((255 * 20) + 1), PSA_ERROR_INSUFFICIENT_DATA }, #endif #endif diff --git a/api-tests/dev_apis/crypto/test_c019/test_c019.c b/api-tests/dev_apis/crypto/test_c019/test_c019.c index c110892c..3f0a670c 100644 --- a/api-tests/dev_apis/crypto/test_c019/test_c019.c +++ b/api-tests/dev_apis/crypto/test_c019/test_c019.c @@ -24,6 +24,7 @@ client_test_t test_c019_crypto_list[] = { NULL, psa_get_generator_capacity_test, + psa_get_generator_capacity_negative_test, NULL, }; @@ -94,27 +95,66 @@ int32_t psa_get_generator_capacity_test(security_t caller) /* Abort a generator */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_ABORT, &generator); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(8)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(9)); + continue; } - TEST_ASSERT_EQUAL(capacity, check1[i].capacity, TEST_CHECKPOINT_NUM(9)); + TEST_ASSERT_EQUAL(capacity, check1[i].capacity, TEST_CHECKPOINT_NUM(10)); /* Generate random bytes */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_READ, &generator, data, check1[i].size); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(11)); remaining_size = check1[i].capacity - check1[i].size; /* Retrieve the current capacity of a generator */ status = val->crypto_function(VAL_CRYPTO_GET_GENERATOR_CAPACITY, &generator, &capacity); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(11)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(12)); - TEST_ASSERT_EQUAL(capacity, remaining_size, TEST_CHECKPOINT_NUM(12)); + TEST_ASSERT_EQUAL(capacity, remaining_size, TEST_CHECKPOINT_NUM(13)); /* Abort a generator */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_ABORT, &generator); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(13)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(14)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(15)); + } + + return VAL_STATUS_SUCCESS; +} + +int32_t psa_get_generator_capacity_negative_test(security_t caller) +{ + uint32_t i; + size_t capacity; + psa_crypto_generator_t generator[] = {psa_crypto_generator_init(), + PSA_CRYPTO_GENERATOR_INIT, {0} }; + uint32_t generator_count = sizeof(generator)/sizeof(generator[0]); + int32_t status; + + /* Initialize the PSA crypto library*/ + status = val->crypto_function(VAL_CRYPTO_INIT); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(1)); + + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, "Test psa_get_generator_capacity without setup\n", 0); + + for (i = 0; i < generator_count; i++) + { + /* Retrieve the current capacity of a generator */ + status = val->crypto_function(VAL_CRYPTO_GET_GENERATOR_CAPACITY, &generator[i], &capacity); + TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(2)); + + /* Abort a generator */ + status = val->crypto_function(VAL_CRYPTO_GENERATOR_ABORT, &generator[i]); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(3)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c019/test_c019.h b/api-tests/dev_apis/crypto/test_c019/test_c019.h index da29369c..d8924d3a 100644 --- a/api-tests/dev_apis/crypto/test_c019/test_c019.h +++ b/api-tests/dev_apis/crypto/test_c019/test_c019.h @@ -27,4 +27,5 @@ extern psa_api_t *psa; extern client_test_t test_c019_crypto_list[]; int32_t psa_get_generator_capacity_test(security_t caller); +int32_t psa_get_generator_capacity_negative_test(security_t caller); #endif /* _TEST_C019_CLIENT_TESTS_H_ */ diff --git a/api-tests/dev_apis/crypto/test_c020/test_c020.c b/api-tests/dev_apis/crypto/test_c020/test_c020.c index 700c3de0..0363d03f 100644 --- a/api-tests/dev_apis/crypto/test_c020/test_c020.c +++ b/api-tests/dev_apis/crypto/test_c020/test_c020.c @@ -78,12 +78,12 @@ int32_t psa_generator_import_key_test(security_t caller) /* Set the usage policy on a key slot */ status = val->crypto_function(VAL_CRYPTO_SET_KEY_POLICY, check1[i].key_handle[SLOT_1], &policy); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(3)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(4)); /* Import the key data into the key slot */ status = val->crypto_function(VAL_CRYPTO_IMPORT_KEY, check1[i].key_handle[SLOT_1], check1[i].key_type[SLOT_1], check1[i].key_data, check1[i].key_length); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(4)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(5)); /* Set up a key derivation operation. Using this function to initialize the generate as * XOR or PRNG generator initialization is not implemented. @@ -91,7 +91,7 @@ int32_t psa_generator_import_key_test(security_t caller) status = val->crypto_function(VAL_CRYPTO_KEY_DERIVATION, &generator, check1[i].key_handle[SLOT_1], check1[i].key_alg[SLOT_1], &salt, salt_length, &label, label_length, check1[i].capacity); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(5)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(6)); /* Initialize a key policy structure to a default that forbids all * usage of the key @@ -104,32 +104,40 @@ int32_t psa_generator_import_key_test(security_t caller) /* Allocate a key slot for a transient key */ status = val->crypto_function(VAL_CRYPTO_ALLOCATE_KEY, &check1[i].key_handle[SLOT_2]); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(3)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(7)); /* Set the usage policy on a key slot */ status = val->crypto_function(VAL_CRYPTO_SET_KEY_POLICY, check1[i].key_handle[SLOT_2], &policy); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(6)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(8)); /* Create a symmetric key from data read from a generator */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_IMPORT_KEY, check1[i].key_handle[SLOT_2], check1[i].key_type[SLOT_2], BYTES_TO_BITS(check1[i].size), &generator); - TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(7)); + TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(9)); if (check1[i].expected_status != PSA_SUCCESS) { /* Abort a generator */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_ABORT, &generator); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(8)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle[SLOT_1]); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(11)); + + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle[SLOT_2]); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(12)); + continue; } /* Export a key in binary format */ status = val->crypto_function(VAL_CRYPTO_EXPORT_KEY, check1[i].key_handle[SLOT_2], data, BUFFER_SIZE, &length); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(9)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(13)); - TEST_ASSERT_EQUAL(length, check1[i].size, TEST_CHECKPOINT_NUM(10)); + TEST_ASSERT_EQUAL(length, check1[i].size, TEST_CHECKPOINT_NUM(14)); data_sum = 0; /* Check that if generated data are zero */ @@ -139,7 +147,7 @@ int32_t psa_generator_import_key_test(security_t caller) } memset(data, 0, sizeof(data)); - TEST_ASSERT_NOT_EQUAL(data_sum, 0, TEST_CHECKPOINT_NUM(12)); + TEST_ASSERT_NOT_EQUAL(data_sum, 0, TEST_CHECKPOINT_NUM(15)); remaining_size = check1[i].capacity - check1[i].size; if (remaining_size > 0) @@ -156,25 +164,25 @@ int32_t psa_generator_import_key_test(security_t caller) /* Allocate a key slot for a transient key */ status = val->crypto_function(VAL_CRYPTO_ALLOCATE_KEY, &check1[i].key_handle[SLOT_3]); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(3)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(16)); /* Set the usage policy on a key slot */ status = val->crypto_function(VAL_CRYPTO_SET_KEY_POLICY, check1[i].key_handle[SLOT_3], &policy); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(12)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(17)); /* Create a symmetric key from data read from a generator */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_IMPORT_KEY, check1[i].key_handle[SLOT_3], check1[i].key_type[SLOT_2], BYTES_TO_BITS(check1[i].size), &generator); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(13)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(18)); /* Export a key in binary format */ status = val->crypto_function(VAL_CRYPTO_EXPORT_KEY, check1[i].key_handle[SLOT_3], data, BUFFER_SIZE, &length); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(14)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(19)); - TEST_ASSERT_EQUAL(length, remaining_size, TEST_CHECKPOINT_NUM(15)); + TEST_ASSERT_EQUAL(length, remaining_size, TEST_CHECKPOINT_NUM(20)); data_sum = 0; /* Check that if generated data are zero */ @@ -184,7 +192,7 @@ int32_t psa_generator_import_key_test(security_t caller) } memset(data, 0, sizeof(data)); - TEST_ASSERT_NOT_EQUAL(data_sum, 0, TEST_CHECKPOINT_NUM(16)); + TEST_ASSERT_NOT_EQUAL(data_sum, 0, TEST_CHECKPOINT_NUM(21)); /* Initialize a key policy structure to a default that forbids all * usage of the key @@ -197,23 +205,37 @@ int32_t psa_generator_import_key_test(security_t caller) /* Allocate a key slot for a transient key */ status = val->crypto_function(VAL_CRYPTO_ALLOCATE_KEY, &check1[i].key_handle[SLOT_4]); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(3)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(22)); /* Set the usage policy on a key slot */ status = val->crypto_function(VAL_CRYPTO_SET_KEY_POLICY, check1[i].key_handle[SLOT_4], &policy); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(17)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(23)); /* Create a symmetric key from data read from a generator */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_IMPORT_KEY, check1[i].key_handle[SLOT_4], check1[i].key_type[SLOT_2], BYTES_TO_BITS(check1[i].size), &generator); - TEST_ASSERT_EQUAL(status, PSA_ERROR_INSUFFICIENT_CAPACITY, TEST_CHECKPOINT_NUM(18)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_INSUFFICIENT_DATA, TEST_CHECKPOINT_NUM(24)); + + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle[SLOT_3]); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(25)); + + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle[SLOT_4]); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(26)); + } /* Abort a generator */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_ABORT, &generator); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(19)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(27)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle[SLOT_1]); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(28)); + + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle[SLOT_2]); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(29)); } return VAL_STATUS_SUCCESS; @@ -291,11 +313,16 @@ int32_t psa_generator_import_key_negative_test(security_t caller) /* Create a symmetric key from data read from a generator */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_IMPORT_KEY, check2[i].key_handle[SLOT_1], check2[i].key_type[SLOT_2], check2[i].size, &generator); - TEST_ASSERT_EQUAL(status, PSA_ERROR_OCCUPIED_SLOT, TEST_CHECKPOINT_NUM(9)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_ALREADY_EXISTS, TEST_CHECKPOINT_NUM(9)); /* Abort a generator */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_ABORT, &generator); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(8)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check2[i].key_handle[SLOT_1]); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(9)); + } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c020/test_data.h b/api-tests/dev_apis/crypto/test_c020/test_data.h index 95fe77b7..512625dc 100644 --- a/api-tests/dev_apis/crypto/test_c020/test_data.h +++ b/api-tests/dev_apis/crypto/test_c020/test_data.h @@ -67,7 +67,7 @@ static test_data check1[] = { {0x70, 0x24, 0x55, 0x0C, 0x14, 0x9D, 0xED, 0x29}, DES_8B_KEY_SIZE, {PSA_KEY_USAGE_DERIVE, PSA_KEY_USAGE_EXPORT}, {PSA_ALG_HKDF(PSA_ALG_SHA_1), PSA_ALG_CTR}, - 64, 80, PSA_ERROR_INSUFFICIENT_CAPACITY + 64, 80, PSA_ERROR_INSUFFICIENT_DATA }, #endif #endif diff --git a/api-tests/dev_apis/crypto/test_c021/test_c021.c b/api-tests/dev_apis/crypto/test_c021/test_c021.c index 64619d1c..7a3c39ca 100644 --- a/api-tests/dev_apis/crypto/test_c021/test_c021.c +++ b/api-tests/dev_apis/crypto/test_c021/test_c021.c @@ -105,6 +105,10 @@ int32_t psa_generator_abort_test(security_t caller) /* Abort the generator */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_ABORT, &generator); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(11)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c022/test_c022.c b/api-tests/dev_apis/crypto/test_c022/test_c022.c index 8f8ac601..049e8d1d 100644 --- a/api-tests/dev_apis/crypto/test_c022/test_c022.c +++ b/api-tests/dev_apis/crypto/test_c022/test_c022.c @@ -94,18 +94,27 @@ int32_t psa_key_derivation_test(security_t caller) /* Abort the generator */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_ABORT, &generator); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(6)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(7)); + continue; } /* Retrieve the current capacity of a generator */ status = val->crypto_function(VAL_CRYPTO_GET_GENERATOR_CAPACITY, &generator, &capacity); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(7)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(8)); - TEST_ASSERT_EQUAL(capacity, check1[i].capacity, TEST_CHECKPOINT_NUM(8)); + TEST_ASSERT_EQUAL(capacity, check1[i].capacity, TEST_CHECKPOINT_NUM(9)); /* Abort the generator */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_ABORT, &generator); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(9)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(11)); } return VAL_STATUS_SUCCESS; @@ -141,7 +150,7 @@ int32_t psa_key_derivation_negative_test(security_t caller) val->crypto_function(VAL_CRYPTO_KEY_POLICY_SET_USAGE, &policy, check2[i].usage, check2[i].key_alg); - val->print(PRINT_TEST, "[Check %d] Test psa_key_derivation with invalid key handle\n", + val->print(PRINT_TEST, "[Check %d] Test psa_key_derivation with Invalid key handle\n", g_test_count++); /* Set up a key derivation operation. Using this function to initialize the generate as * XOR or PRNG generator initialization is not implemented. @@ -152,7 +161,7 @@ int32_t psa_key_derivation_negative_test(security_t caller) check2[i].capacity); TEST_ASSERT_EQUAL(status, PSA_ERROR_INVALID_HANDLE, TEST_CHECKPOINT_NUM(6)); - val->print(PRINT_TEST, "[Check %d] Test psa_key_derivation with zero as key handle\n", + val->print(PRINT_TEST, "[Check %d] Test psa_key_derivation with Zero as key handle\n", g_test_count++); /* Set up a key derivation operation. Using this function to initialize the generate as * XOR or PRNG generator initialization is not implemented. @@ -163,7 +172,7 @@ int32_t psa_key_derivation_negative_test(security_t caller) check2[i].capacity); TEST_ASSERT_EQUAL(status, PSA_ERROR_INVALID_HANDLE, TEST_CHECKPOINT_NUM(7)); - val->print(PRINT_TEST, "[Check %d] Test psa_key_derivation with empty key handle\n", + val->print(PRINT_TEST, "[Check %d] Test psa_key_derivation with Empty key handle\n", g_test_count++); /* Allocate a key slot for a transient key */ status = val->crypto_function(VAL_CRYPTO_ALLOCATE_KEY, &empty_key_handle); @@ -181,7 +190,7 @@ int32_t psa_key_derivation_negative_test(security_t caller) empty_key_handle, check2[i].key_alg, check2[i].salt, check2[i].salt_length, check2[i].label, check2[i].label_length, check2[i].capacity); - TEST_ASSERT_EQUAL(status, PSA_ERROR_EMPTY_SLOT, TEST_CHECKPOINT_NUM(10)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_DOES_NOT_EXIST, TEST_CHECKPOINT_NUM(10)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c022/test_data.h b/api-tests/dev_apis/crypto/test_c022/test_data.h index ecd5cded..7dcc94f0 100644 --- a/api-tests/dev_apis/crypto/test_c022/test_data.h +++ b/api-tests/dev_apis/crypto/test_c022/test_data.h @@ -104,6 +104,14 @@ static test_data check1[] = { PSA_ERROR_NOT_PERMITTED }, #endif + +{"Test psa_key_derivation with unsupported key derivation algorithm\n", 14, PSA_KEY_TYPE_DERIVE, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9}, + AES_16B_KEY_SIZE, PSA_KEY_USAGE_DERIVE, PSA_ALG_HKDF(PSA_ALG_CATEGORY_HASH), + {0}, 0, {0}, 0, 32, + PSA_ERROR_NOT_SUPPORTED +}, #endif #ifdef ARCH_TEST_RSA_PKCS1V15_CRYPT @@ -116,7 +124,6 @@ static test_data check1[] = { PSA_ERROR_INVALID_ARGUMENT }, #endif - }; static test_data check2[] = { @@ -132,5 +139,4 @@ static test_data check2[] = { }, #endif #endif - }; diff --git a/api-tests/dev_apis/crypto/test_c024/test_c024.c b/api-tests/dev_apis/crypto/test_c024/test_c024.c index a375efee..16b52e66 100644 --- a/api-tests/dev_apis/crypto/test_c024/test_c024.c +++ b/api-tests/dev_apis/crypto/test_c024/test_c024.c @@ -90,12 +90,18 @@ int32_t psa_aead_encrypt_test(security_t caller) TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(5)); if (is_buffer_empty(check1[i].nonce, check1[i].nonce_length) == TRUE) + { nonce = NULL; + check1[i].nonce_length = 0; + } else nonce = check1[i].nonce; if (is_buffer_empty(check1[i].additional_data, check1[i].additional_data_length) == TRUE) + { additional_data = NULL; + check1[i].additional_data_length = 0; + } else additional_data = check1[i].additional_data; @@ -108,16 +114,25 @@ int32_t psa_aead_encrypt_test(security_t caller) TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(6)); if (check1[i].expected_status != PSA_SUCCESS) + { + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(7)); + } continue; /* Check if the length matches */ TEST_ASSERT_EQUAL(ciphertext_length, check1[i].expected_ciphertext_length, - TEST_CHECKPOINT_NUM(7)); + TEST_CHECKPOINT_NUM(8)); /* Check if the data matches */ TEST_ASSERT_MEMCMP(ciphertext, check1[i].expected_ciphertext, ciphertext_length, - TEST_CHECKPOINT_NUM(8)); + TEST_CHECKPOINT_NUM(9)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); } return VAL_STATUS_SUCCESS; @@ -152,16 +167,22 @@ int32_t psa_aead_encrypt_negative_test(security_t caller) check2[i].key_alg); if (is_buffer_empty(check2[i].nonce, check2[i].nonce_length) == TRUE) + { nonce = NULL; + check2[i].nonce_length = 0; + } else nonce = check2[i].nonce; if (is_buffer_empty(check2[i].additional_data, check2[i].additional_data_length) == TRUE) + { additional_data = NULL; + check2[i].additional_data_length = 0; + } else additional_data = check2[i].additional_data; - val->print(PRINT_TEST, "[Check %d] Test psa_aead_encrypt - invalid key handle\n", + val->print(PRINT_TEST, "[Check %d] Test psa_aead_encrypt - Invalid key handle\n", g_test_count++); /* Process an authenticated encryption operation */ status = val->crypto_function(VAL_CRYPTO_AEAD_ENCRYPT, check2[i].key_handle, @@ -171,7 +192,7 @@ int32_t psa_aead_encrypt_negative_test(security_t caller) &ciphertext_length); TEST_ASSERT_EQUAL(status, PSA_ERROR_INVALID_HANDLE, TEST_CHECKPOINT_NUM(3)); - val->print(PRINT_TEST, "[Check %d] Test psa_aead_encrypt - zero as key handle\n", + val->print(PRINT_TEST, "[Check %d] Test psa_aead_encrypt - Zero as key handle\n", g_test_count++); /* Process an authenticated encryption operation */ status = val->crypto_function(VAL_CRYPTO_AEAD_ENCRYPT, 0, @@ -181,7 +202,7 @@ int32_t psa_aead_encrypt_negative_test(security_t caller) &ciphertext_length); TEST_ASSERT_EQUAL(status, PSA_ERROR_INVALID_HANDLE, TEST_CHECKPOINT_NUM(4)); - val->print(PRINT_TEST, "[Check %d] Test psa_aead_encrypt - empty key handle\n", + val->print(PRINT_TEST, "[Check %d] Test psa_aead_encrypt - Empty key handle\n", g_test_count++); /* Allocate a key slot for a transient key */ status = val->crypto_function(VAL_CRYPTO_ALLOCATE_KEY, &check2[i].key_handle); @@ -198,7 +219,7 @@ int32_t psa_aead_encrypt_negative_test(security_t caller) check2[i].additional_data_length, check2[i].plaintext, check2[i].plaintext_length, ciphertext, check2[i].ciphertext_size, &ciphertext_length); - TEST_ASSERT_EQUAL(status, PSA_ERROR_EMPTY_SLOT, TEST_CHECKPOINT_NUM(7)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_DOES_NOT_EXIST, TEST_CHECKPOINT_NUM(7)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c024/test_data.h b/api-tests/dev_apis/crypto/test_c024/test_data.h index 9f52ecb8..f2800024 100644 --- a/api-tests/dev_apis/crypto/test_c024/test_data.h +++ b/api-tests/dev_apis/crypto/test_c024/test_data.h @@ -173,7 +173,7 @@ static test_data check2[] = { {0x5D, 0xC1, 0x72, 0x23, 0x66, 0x96, 0xFD, 0xFC, 0x93, 0x06, 0x27, 0x52, 0xC7, 0x0A, 0xCB, 0x36, 0x55, 0x30, 0xC9, 0x48, 0x8F, 0x5E, 0xA5, 0xB9, 0x51, 0xFB, 0x4E}, - BUFFER_SIZE, 27, PSA_ERROR_EMPTY_SLOT + BUFFER_SIZE, 27, PSA_ERROR_DOES_NOT_EXIST }, #endif #endif diff --git a/api-tests/dev_apis/crypto/test_c025/test_c025.c b/api-tests/dev_apis/crypto/test_c025/test_c025.c index f1bcc5c2..f9dc3fd5 100644 --- a/api-tests/dev_apis/crypto/test_c025/test_c025.c +++ b/api-tests/dev_apis/crypto/test_c025/test_c025.c @@ -89,12 +89,18 @@ int32_t psa_aead_decrypt_test(security_t caller) TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(5)); if (is_buffer_empty(check1[i].nonce, check1[i].nonce_length) == TRUE) + { nonce = NULL; + check1[i].nonce_length = 0; + } else nonce = check1[i].nonce; if (is_buffer_empty(check1[i].additional_data, check1[i].additional_data_length) == TRUE) + { additional_data = NULL; + check1[i].additional_data_length = 0; + } else additional_data = check1[i].additional_data; @@ -106,15 +112,25 @@ int32_t psa_aead_decrypt_test(security_t caller) TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(6)); if (check1[i].expected_status != PSA_SUCCESS) + { + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(7)); + continue; + } /* Check if the length matches */ TEST_ASSERT_EQUAL(plaintext_length, check1[i].expected_plaintext_length, - TEST_CHECKPOINT_NUM(7)); + TEST_CHECKPOINT_NUM(8)); /* Check if the data matches */ TEST_ASSERT_MEMCMP(plaintext, check1[i].expected_plaintext, plaintext_length, - TEST_CHECKPOINT_NUM(8)); + TEST_CHECKPOINT_NUM(9)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); } return VAL_STATUS_SUCCESS; @@ -149,16 +165,22 @@ int32_t psa_aead_decrypt_negative_test(security_t caller) check2[i].key_alg); if (is_buffer_empty(check2[i].nonce, check2[i].nonce_length) == TRUE) + { nonce = NULL; + check2[i].nonce_length = 0; + } else nonce = check2[i].nonce; if (is_buffer_empty(check2[i].additional_data, check2[i].additional_data_length) == TRUE) + { additional_data = NULL; + check2[i].additional_data_length = 0; + } else additional_data = check2[i].additional_data; - val->print(PRINT_TEST, "[Check %d] Test psa_aead_decrypt - invalid key handle\n", + val->print(PRINT_TEST, "[Check %d] Test psa_aead_decrypt - Invalid key handle\n", g_test_count++); /* Process an authenticated decryption operation */ status = val->crypto_function(VAL_CRYPTO_AEAD_DECRYPT, check2[i].key_handle, @@ -167,7 +189,7 @@ int32_t psa_aead_decrypt_negative_test(security_t caller) plaintext, check2[i].plaintext_size, &plaintext_length); TEST_ASSERT_EQUAL(status, PSA_ERROR_INVALID_HANDLE, TEST_CHECKPOINT_NUM(3)); - val->print(PRINT_TEST, "[Check %d] Test psa_aead_decrypt - zero as key handle\n", + val->print(PRINT_TEST, "[Check %d] Test psa_aead_decrypt - Zero as key handle\n", g_test_count++); /* Process an authenticated decryption operation */ status = val->crypto_function(VAL_CRYPTO_AEAD_DECRYPT, 0, @@ -176,7 +198,7 @@ int32_t psa_aead_decrypt_negative_test(security_t caller) plaintext, check2[i].plaintext_size, &plaintext_length); TEST_ASSERT_EQUAL(status, PSA_ERROR_INVALID_HANDLE, TEST_CHECKPOINT_NUM(4)); - val->print(PRINT_TEST, "[Check %d] Test psa_aead_decrypt - empty key handle\n", + val->print(PRINT_TEST, "[Check %d] Test psa_aead_decrypt - Empty key handle\n", g_test_count++); /* Allocate a key slot for a transient key */ status = val->crypto_function(VAL_CRYPTO_ALLOCATE_KEY, &check2[i].key_handle); @@ -192,7 +214,7 @@ int32_t psa_aead_decrypt_negative_test(security_t caller) check2[i].key_alg, nonce, check2[i].nonce_length, additional_data, check2[i].additional_data_length, check2[i].ciphertext, check2[i].ciphertext_size, plaintext, check2[i].plaintext_size, &plaintext_length); - TEST_ASSERT_EQUAL(status, PSA_ERROR_EMPTY_SLOT, TEST_CHECKPOINT_NUM(7)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_DOES_NOT_EXIST, TEST_CHECKPOINT_NUM(7)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c025/test_data.h b/api-tests/dev_apis/crypto/test_c025/test_data.h index 4ad0e17d..44ce513f 100644 --- a/api-tests/dev_apis/crypto/test_c025/test_data.h +++ b/api-tests/dev_apis/crypto/test_c025/test_data.h @@ -188,6 +188,23 @@ static test_data check1[] = { 0xD1, 0xFC, 0xB6, 0x91, 0xF3, 0x40, 0x6C, 0xBF, 0x53, 0x1F, 0x83, 0xA4}, 38, 23, PSA_ERROR_INVALID_SIGNATURE }, + +{"Test psa_aead_decrypt - Invalid tag length 0\n", 11, PSA_KEY_TYPE_AES, +{0xD7, 0x82, 0x8D, 0x13, 0xB2, 0xB0, 0xBD, 0xC3, 0x25, 0xA7, 0x62, 0x36, 0xDF, + 0x93, 0xCC, 0x6B}, + AES_16B_KEY_SIZE, PSA_KEY_USAGE_DECRYPT, PSA_ALG_AEAD_WITH_TAG_LENGTH(PSA_ALG_CCM, 0), +{0x48, 0xc0, 0x90, 0x69, 0x30, 0x56, 0x1e, 0x0a, 0xb0, 0xef, 0x4c, 0xd9, 0x72}, + 13, +{0x40, 0xa2, 0x7c, 0x1d, 0x1e, 0x23, 0xea, 0x3d, 0xbe, 0x80, 0x56, 0xb2, 0x77, + 0x48, 0x61, 0xa4, 0xa2, 0x01, 0xcc, 0xe4, 0x9f, 0x19, 0x99, 0x7d, 0x19, 0x20, + 0x6d, 0x8c, 0x8a, 0x34, 0x39, 0x51}, 32, +{0x45, 0x35, 0xd1, 0x2b, 0x43, 0x77, 0x92, 0x8a, 0x7c, 0x0a, 0x61, 0xc9, 0xf8, + 0x25, 0xa4, 0x86, 0x71, 0xea, 0x05, 0x91, 0x07, 0x48, 0xc8, 0xef}, BUFFER_SIZE, +{0x26, 0xc5, 0x69, 0x61, 0xc0, 0x35, 0xa7, 0xe4, 0x52, 0xcc, 0xe6, 0x1b, 0xc6, + 0xee, 0x22, 0x0d, 0x77, 0xb3, 0xf9, 0x4d, 0x18, 0xfd, 0x10, 0xb6, 0xd8, 0x0e, + 0x8b, 0xf8, 0x0f, 0x4a, 0x46, 0xca, 0xb0, 0x6d, 0x43, 0x13, 0xf0, 0xdb, 0x9b, + 0xe9}, 40, 24, PSA_ERROR_INVALID_ARGUMENT +}, #endif #endif }; diff --git a/api-tests/dev_apis/crypto/test_c026/test_c026.c b/api-tests/dev_apis/crypto/test_c026/test_c026.c index 9f43a7ab..03d9def5 100644 --- a/api-tests/dev_apis/crypto/test_c026/test_c026.c +++ b/api-tests/dev_apis/crypto/test_c026/test_c026.c @@ -79,9 +79,25 @@ int32_t psa_mac_sign_setup_test(security_t caller) check1[i].key_handle, check1[i].key_alg); TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(6)); - /* Abort a MAC operation */ + /* Whether setup succeeded or failed, abort must succeed. + Abort a MAC operation + */ status = val->crypto_function(VAL_CRYPTO_MAC_ABORT, &operation); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(7)); + + /* If setup failed, reproduce the failure, so that the caller can + * test the resulting state of the operation object. + */ + if (check1[i].expected_status != PSA_SUCCESS) + { + status = val->crypto_function(VAL_CRYPTO_MAC_SIGN_SETUP, &operation, + check1[i].key_handle, check1[i].key_alg); + TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(8)); + } + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(9)); } return VAL_STATUS_SUCCESS; @@ -141,7 +157,7 @@ int32_t psa_mac_sign_setup_negative_test(security_t caller) /* Start a multipart MAC calculation operation */ status = val->crypto_function(VAL_CRYPTO_MAC_SIGN_SETUP, &operation, check2[i].key_handle, check2[i].key_alg); - TEST_ASSERT_EQUAL(status, PSA_ERROR_EMPTY_SLOT, TEST_CHECKPOINT_NUM(7)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_DOES_NOT_EXIST, TEST_CHECKPOINT_NUM(7)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c027/test_c027.c b/api-tests/dev_apis/crypto/test_c027/test_c027.c index e761848f..b6e32889 100644 --- a/api-tests/dev_apis/crypto/test_c027/test_c027.c +++ b/api-tests/dev_apis/crypto/test_c027/test_c027.c @@ -90,22 +90,31 @@ int32_t psa_mac_update_test(security_t caller) /* Abort a MAC operation */ status = val->crypto_function(VAL_CRYPTO_MAC_ABORT, &operation); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(8)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(9)); + continue; } /* Finish the calculation of the MAC of a message */ status = val->crypto_function(VAL_CRYPTO_MAC_SIGN_FINISH, &operation, data, - sizeof(data)/sizeof(data[0]), &length); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(9)); + sizeof(data), &length); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); /* Add a message fragment to the same multipart MAC operation*/ status = val->crypto_function(VAL_CRYPTO_MAC_UPDATE, &operation, check1[i].data, check1[i].data_size); - TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(10)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(11)); /* Abort a MAC operation */ status = val->crypto_function(VAL_CRYPTO_MAC_ABORT, &operation); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(11)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(12)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(13)); } return VAL_STATUS_SUCCESS; @@ -113,11 +122,11 @@ int32_t psa_mac_update_test(security_t caller) int32_t psa_mac_update_invalid_operator_test(security_t caller) { - psa_mac_operation_t operation; - int32_t status; + int32_t i, status; + psa_mac_operation_t operation[] = {psa_mac_operation_init(), PSA_MAC_OPERATION_INIT, {0} }; + uint32_t operation_count = sizeof(operation)/sizeof(operation[0]); memset(data, 0, sizeof(data)); - memset(&operation, 0, sizeof(operation)); val->print(PRINT_TEST, "[Check %d] ", g_test_count++); val->print(PRINT_TEST, "Test psa_mac_update without mac setup\n", 0); @@ -125,14 +134,18 @@ int32_t psa_mac_update_invalid_operator_test(security_t caller) status = val->crypto_function(VAL_CRYPTO_INIT); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(1)); - /* Start a multipart MAC calculation operation */ - status = val->crypto_function(VAL_CRYPTO_MAC_UPDATE, &operation, data, - sizeof(data)/sizeof(data[0])); - TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(2)); + for (i = 0; i < operation_count; i++) + { + + /* Start a multipart MAC calculation for each operation */ + status = val->crypto_function(VAL_CRYPTO_MAC_UPDATE, &operation[i], data, + sizeof(data)/sizeof(data[0])); + TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(2)); - /* Abort a MAC operation */ - status = val->crypto_function(VAL_CRYPTO_MAC_ABORT, &operation); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(3)); + /* Abort the MAC operations */ + status = val->crypto_function(VAL_CRYPTO_MAC_ABORT, &operation[i]); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(3)); + } return VAL_STATUS_SUCCESS; } diff --git a/api-tests/dev_apis/crypto/test_c028/test_c028.c b/api-tests/dev_apis/crypto/test_c028/test_c028.c index 08982786..475feb77 100644 --- a/api-tests/dev_apis/crypto/test_c028/test_c028.c +++ b/api-tests/dev_apis/crypto/test_c028/test_c028.c @@ -96,27 +96,37 @@ int32_t psa_mac_sign_finish_test(security_t caller) /* Abort a MAC operation */ status = val->crypto_function(VAL_CRYPTO_MAC_ABORT, &operation); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(9)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); + continue; } /* Check if the MAC length matches with the expected length */ - TEST_ASSERT_EQUAL(length, check1[i].expected_length, TEST_CHECKPOINT_NUM(10)); + TEST_ASSERT_EQUAL(length, check1[i].expected_length, TEST_CHECKPOINT_NUM(11)); /* Check if the MAC data matches with the expected data */ - TEST_ASSERT_MEMCMP(check1[i].expected_data, data, length, TEST_CHECKPOINT_NUM(11)); + TEST_ASSERT_MEMCMP(check1[i].expected_data, data, length, TEST_CHECKPOINT_NUM(12)); memset(data, 0, sizeof(data)); - /* Finish the calculation of the MAC of a message using same operation + /* Calling mac finish twice in a row. + * Finish the calculation of the MAC of a message using same operation * should return error */ status = val->crypto_function(VAL_CRYPTO_MAC_SIGN_FINISH, &operation, data, check1[i].mac_size, &length); - TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(12)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(13)); /* Abort a MAC operation */ status = val->crypto_function(VAL_CRYPTO_MAC_ABORT, &operation); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(13)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(14)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(15)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c029/test_c029.c b/api-tests/dev_apis/crypto/test_c029/test_c029.c index d732d920..53be0dbf 100644 --- a/api-tests/dev_apis/crypto/test_c029/test_c029.c +++ b/api-tests/dev_apis/crypto/test_c029/test_c029.c @@ -79,9 +79,25 @@ int32_t psa_mac_verify_setup_test(security_t caller) check1[i].key_handle, check1[i].key_alg); TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(6)); - /* Abort a MAC operation */ + /* Whether setup succeeded or failed, abort must succeed. + * Abort a MAC operation + */ status = val->crypto_function(VAL_CRYPTO_MAC_ABORT, &operation); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(7)); + + /* If setup failed, reproduce the failure, so that the caller can + * test the resulting state of the operation object. + */ + if (check1[i].expected_status != PSA_SUCCESS) + { + status = val->crypto_function(VAL_CRYPTO_MAC_VERIFY_SETUP, &operation, + check1[i].key_handle, check1[i].key_alg); + TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(8)); + } + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(9)); } return VAL_STATUS_SUCCESS; @@ -136,7 +152,7 @@ int32_t psa_mac_verify_setup_negative_test(security_t caller) /* Start a multipart MAC verification operation */ status = val->crypto_function(VAL_CRYPTO_MAC_VERIFY_SETUP, &operation, check2[i].key_handle, check2[i].key_alg); - TEST_ASSERT_EQUAL(status, PSA_ERROR_EMPTY_SLOT, TEST_CHECKPOINT_NUM(6)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_DOES_NOT_EXIST, TEST_CHECKPOINT_NUM(6)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c030/test_c030.c b/api-tests/dev_apis/crypto/test_c030/test_c030.c index ad14048c..177b0f0e 100644 --- a/api-tests/dev_apis/crypto/test_c030/test_c030.c +++ b/api-tests/dev_apis/crypto/test_c030/test_c030.c @@ -94,19 +94,28 @@ int32_t psa_mac_verify_finish_test(security_t caller) /* Abort a MAC operation */ status = val->crypto_function(VAL_CRYPTO_MAC_ABORT, &operation); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(9)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); continue; } - /* Finish the calculation of the MAC of a message using same operation + /* Calling mac finish twice in a row. + * Finish the calculation of the MAC of a message using same operation * should return error */ status = val->crypto_function(VAL_CRYPTO_MAC_VERIFY_FINISH, &operation, check1[i].expected_mac, check1[i].mac_size); - TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(10)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(11)); /* Abort a MAC operation */ status = val->crypto_function(VAL_CRYPTO_MAC_ABORT, &operation); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(11)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(12)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(13)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c031/test_c031.c b/api-tests/dev_apis/crypto/test_c031/test_c031.c index 9988fae8..040e8a86 100644 --- a/api-tests/dev_apis/crypto/test_c031/test_c031.c +++ b/api-tests/dev_apis/crypto/test_c031/test_c031.c @@ -86,6 +86,10 @@ int32_t psa_mac_abort_test(security_t caller) /* Multiple Abort a MAC operation should succeed */ status = val->crypto_function(VAL_CRYPTO_MAC_ABORT, &operation); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(8)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(9)); } return VAL_STATUS_SUCCESS; @@ -103,8 +107,8 @@ int32_t psa_mac_abort_before_finish_test(security_t caller) uint8_t key_data[] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}; uint8_t input_data[] = {0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65}; - size_t key_length = sizeof(key_data)/sizeof(key_data[0]); - size_t inputdata_size = sizeof(input_data)/sizeof(input_data[0]); + size_t key_length = sizeof(key_data); + size_t inputdata_size = sizeof(input_data); int32_t status; memset(data, 0, sizeof(data)); @@ -159,5 +163,9 @@ int32_t psa_mac_abort_before_finish_test(security_t caller) BUFFER_SIZE, &length); TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(9)); + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); + return VAL_STATUS_SUCCESS; } diff --git a/api-tests/dev_apis/crypto/test_c032/test_c032.c b/api-tests/dev_apis/crypto/test_c032/test_c032.c index de4dcf78..44e4682b 100644 --- a/api-tests/dev_apis/crypto/test_c032/test_c032.c +++ b/api-tests/dev_apis/crypto/test_c032/test_c032.c @@ -111,9 +111,22 @@ int32_t psa_cipher_encrypt_setup_test(security_t caller) check1[i].key_handle, check1[i].key_alg); TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(6)); - /* Abort a cipher operation */ + /* Whether setup succeeded or failed, abort must succeed. + * Abort a cipher operation + */ status = val->crypto_function(VAL_CRYPTO_CIPHER_ABORT, &operation); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(7)); + + if (check1[i].expected_status != PSA_SUCCESS) + { + status = val->crypto_function(VAL_CRYPTO_CIPHER_ENCRYPT_SETUP, &operation, + check1[i].key_handle, check1[i].key_alg); + TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(8)); + } + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(9)); } return VAL_STATUS_SUCCESS; @@ -173,7 +186,7 @@ int32_t psa_cipher_encrypt_setup_negative_test(security_t caller) /* Set the key for a multipart symmetric encryption operation */ status = val->crypto_function(VAL_CRYPTO_CIPHER_ENCRYPT_SETUP, &operation, check2[i].key_handle, check2[i].key_alg); - TEST_ASSERT_EQUAL(status, PSA_ERROR_EMPTY_SLOT, TEST_CHECKPOINT_NUM(7)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_DOES_NOT_EXIST, TEST_CHECKPOINT_NUM(7)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c033/test_c033.c b/api-tests/dev_apis/crypto/test_c033/test_c033.c index 45d5b15f..c3b60096 100644 --- a/api-tests/dev_apis/crypto/test_c033/test_c033.c +++ b/api-tests/dev_apis/crypto/test_c033/test_c033.c @@ -111,9 +111,23 @@ int32_t psa_cipher_decrypt_setup_test(security_t caller) check1[i].key_handle, check1[i].key_alg); TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(6)); - /* Abort a cipher operation */ + /* Whether setup succeeded or failed, abort must succeed. + * Abort a cipher operation + */ status = val->crypto_function(VAL_CRYPTO_CIPHER_ABORT, &operation); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(7)); + + if (check1[i].expected_status != PSA_SUCCESS) + { + status = val->crypto_function(VAL_CRYPTO_CIPHER_DECRYPT_SETUP, &operation, + check1[i].key_handle, check1[i].key_alg); + TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(8)); + } + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(9)); + } return VAL_STATUS_SUCCESS; @@ -173,7 +187,7 @@ int32_t psa_cipher_decrypt_setup_negative_test(security_t caller) /* Set the key for a multipart symmetric decryption operation */ status = val->crypto_function(VAL_CRYPTO_CIPHER_DECRYPT_SETUP, &operation, check2[i].key_handle, check2[i].key_alg); - TEST_ASSERT_EQUAL(status, PSA_ERROR_EMPTY_SLOT, TEST_CHECKPOINT_NUM(7)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_DOES_NOT_EXIST, TEST_CHECKPOINT_NUM(7)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c034/test_c034.c b/api-tests/dev_apis/crypto/test_c034/test_c034.c index 65b680c3..97c5bee7 100644 --- a/api-tests/dev_apis/crypto/test_c034/test_c034.c +++ b/api-tests/dev_apis/crypto/test_c034/test_c034.c @@ -92,11 +92,16 @@ int32_t psa_cipher_generate_iv_test(security_t caller) /* Abort a cipher operation */ status = val->crypto_function(VAL_CRYPTO_CIPHER_ABORT, &operation); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(8)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(9)); + continue; } /* Check that if generated iv length match the expected length */ - TEST_ASSERT_EQUAL(iv_length, check1[i].expected_iv_length, TEST_CHECKPOINT_NUM(9)); + TEST_ASSERT_EQUAL(iv_length, check1[i].expected_iv_length, TEST_CHECKPOINT_NUM(10)); iv_sum = 0; for (j = 0; j < iv_length; j++) @@ -105,18 +110,22 @@ int32_t psa_cipher_generate_iv_test(security_t caller) } /* Check that if generated iv are zero */ - TEST_ASSERT_NOT_EQUAL(iv_sum, 0, TEST_CHECKPOINT_NUM(10)); + TEST_ASSERT_NOT_EQUAL(iv_sum, 0, TEST_CHECKPOINT_NUM(11)); /* Generating an IV for a symmetric encryption operation using the same operator * should fail */ status = val->crypto_function(VAL_CRYPTO_CIPHER_GENERATE_IV, &operation, iv, check1[i].iv_size, &iv_length); - TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(11)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(12)); /* Abort a cipher operation */ status = val->crypto_function(VAL_CRYPTO_CIPHER_ABORT, &operation); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(12)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(13)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(14)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c035/test_c035.c b/api-tests/dev_apis/crypto/test_c035/test_c035.c index 368b699c..010b2756 100644 --- a/api-tests/dev_apis/crypto/test_c035/test_c035.c +++ b/api-tests/dev_apis/crypto/test_c035/test_c035.c @@ -85,7 +85,7 @@ int32_t psa_cipher_set_iv_test(security_t caller) TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(7)); /* Setting an IV for a symmetric encryption operation using the same operator - * should fail + * should fail for both previous success and failure cases */ status = val->crypto_function(VAL_CRYPTO_CIPHER_SET_IV, &operation, check1[i].iv, check1[i].iv_size); @@ -94,6 +94,10 @@ int32_t psa_cipher_set_iv_test(security_t caller) /* Abort a cipher operation */ status = val->crypto_function(VAL_CRYPTO_CIPHER_ABORT, &operation); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(9)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c036/test_c036.c b/api-tests/dev_apis/crypto/test_c036/test_c036.c index 4a917c48..bf5969a9 100644 --- a/api-tests/dev_apis/crypto/test_c036/test_c036.c +++ b/api-tests/dev_apis/crypto/test_c036/test_c036.c @@ -25,10 +25,12 @@ client_test_t test_c036_crypto_list[] = { NULL, psa_cipher_update_test, + psa_cipher_update_negative_test, NULL, }; static int g_test_count = 1; +static uint8_t input[SIZE_32B]; static uint8_t output[SIZE_32B]; static psa_cipher_operation_t operation; @@ -107,25 +109,64 @@ int32_t psa_cipher_update_test(security_t caller) /* Abort a cipher operation */ status = val->crypto_function(VAL_CRYPTO_CIPHER_ABORT, &operation); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(11)); continue; } /* Check if the output length matches the expected length */ - TEST_ASSERT_EQUAL(length, check1[i].expected_output_length, TEST_CHECKPOINT_NUM(11)); + TEST_ASSERT_EQUAL(length, check1[i].expected_output_length, TEST_CHECKPOINT_NUM(12)); /* Check if the output data matches the expected data */ - TEST_ASSERT_MEMCMP(output, check1[i].expected_output, length, TEST_CHECKPOINT_NUM(12)); + TEST_ASSERT_MEMCMP(output, check1[i].expected_output, length, TEST_CHECKPOINT_NUM(13)); /* Encrypt or decrypt a message fragment in an invalid cipher operation should fail */ status = val->crypto_function(VAL_CRYPTO_CIPHER_UPDATE, &invalid_operation, check1[i].input, check1[i].input_length, output, check1[i].output_size, &length); - TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(13)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(14)); /* Abort a cipher operation */ status = val->crypto_function(VAL_CRYPTO_CIPHER_ABORT, &operation); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(14)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(15)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(16)); } return VAL_STATUS_SUCCESS; } + +int32_t psa_cipher_update_negative_test(security_t caller) +{ + int32_t i, status; + psa_cipher_operation_t operations[] = {psa_cipher_operation_init(), + PSA_CIPHER_OPERATION_INIT, {0} }; + uint32_t operation_count = sizeof(operations)/sizeof(operations[0]); + size_t length; + + memset(output, 0, sizeof(output)); + val->print(PRINT_TEST, "[Check %d] ", g_test_count++); + val->print(PRINT_TEST, "Test psa_cipher_update without cipher setup\n", 0); + + /* Initialize the PSA crypto library*/ + status = val->crypto_function(VAL_CRYPTO_INIT); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(1)); + + for (i = 0; i < operation_count; i++) + { + status = val->crypto_function(VAL_CRYPTO_CIPHER_UPDATE, &operations[i], input, + sizeof(input), output, sizeof(output), &length); + TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(2)); + + /* Abort a cipher operation */ + status = val->crypto_function(VAL_CRYPTO_CIPHER_ABORT, &operations[i]); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(3)); + } + + return VAL_STATUS_SUCCESS; +} + diff --git a/api-tests/dev_apis/crypto/test_c036/test_c036.h b/api-tests/dev_apis/crypto/test_c036/test_c036.h index 045271a0..61fe7e53 100644 --- a/api-tests/dev_apis/crypto/test_c036/test_c036.h +++ b/api-tests/dev_apis/crypto/test_c036/test_c036.h @@ -27,4 +27,5 @@ extern psa_api_t *psa; extern client_test_t test_c036_crypto_list[]; int32_t psa_cipher_update_test(security_t caller); +int32_t psa_cipher_update_negative_test(security_t caller); #endif /* _TEST_C036_CLIENT_TESTS_H_ */ diff --git a/api-tests/dev_apis/crypto/test_c036/test_data.h b/api-tests/dev_apis/crypto/test_c036/test_data.h index 17f9bf01..c5e4fbe8 100644 --- a/api-tests/dev_apis/crypto/test_c036/test_data.h +++ b/api-tests/dev_apis/crypto/test_c036/test_data.h @@ -101,6 +101,7 @@ static test_data check1[] = { #endif #ifdef ARCH_TEST_CBC_NO_PADDING +#ifdef ARCH_TEST_DES_1KEY {"Test psa_cipher_update - Encrypt - DES CBC (nopad)\n", 6, PSA_KEY_TYPE_DES, {0x01, 0x02, 0x04, 0x07, 0x08, 0x0b, 0x0d, 0x0e}, DES_8B_KEY_SIZE, PSA_KEY_USAGE_ENCRYPT, PSA_ALG_CBC_NO_PADDING, @@ -110,6 +111,7 @@ static test_data check1[] = { }, #endif #endif +#endif #ifdef ARCH_TEST_CBC_NO_PADDING #ifdef ARCH_TEST_DES_2KEY diff --git a/api-tests/dev_apis/crypto/test_c037/test_c037.c b/api-tests/dev_apis/crypto/test_c037/test_c037.c index fa2ab6c5..8a7fdc88 100644 --- a/api-tests/dev_apis/crypto/test_c037/test_c037.c +++ b/api-tests/dev_apis/crypto/test_c037/test_c037.c @@ -114,29 +114,37 @@ int32_t psa_cipher_finish_test(security_t caller) /* Abort a cipher operation */ status = val->crypto_function(VAL_CRYPTO_CIPHER_ABORT, &operation); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(11)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(12)); continue; } /* Check if the output length matches the expected length */ - TEST_ASSERT_EQUAL(finish_length, check1[i].expected_output_length, TEST_CHECKPOINT_NUM(12)); + TEST_ASSERT_EQUAL(finish_length, check1[i].expected_output_length, TEST_CHECKPOINT_NUM(13)); /* Check if the output data matches the expected data */ TEST_ASSERT_MEMCMP(output, check1[i].expected_output, (update_length + finish_length), - TEST_CHECKPOINT_NUM(13)); + TEST_CHECKPOINT_NUM(14)); /* Finish encrypting or decrypting a message using an invalid operation should fail */ status = val->crypto_function(VAL_CRYPTO_CIPHER_FINISH, &invalid_operation, output, check1[i].output_size[SLOT_2], &finish_length); - TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(14)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(15)); /* Abort a cipher operation */ status = val->crypto_function(VAL_CRYPTO_CIPHER_ABORT, &operation); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(15)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(16)); /* Abort a cipher operation */ status = val->crypto_function(VAL_CRYPTO_CIPHER_ABORT, &invalid_operation); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(16)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(17)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(18)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c038/test_c038.c b/api-tests/dev_apis/crypto/test_c038/test_c038.c index b8d8840c..54b4b7d5 100644 --- a/api-tests/dev_apis/crypto/test_c038/test_c038.c +++ b/api-tests/dev_apis/crypto/test_c038/test_c038.c @@ -97,6 +97,10 @@ int32_t psa_cipher_abort_test(security_t caller) /* Multiple abort cipher operation should return success*/ status = val->crypto_function(VAL_CRYPTO_CIPHER_ABORT, &operation); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(9)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); } return VAL_STATUS_SUCCESS; @@ -117,9 +121,9 @@ int32_t psa_cipher_abort_before_update_test(security_t caller) 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}; uint8_t iv[] = {0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a}; - size_t key_length = sizeof(key_data)/sizeof(key_data[0]); - size_t input_length = sizeof(input)/sizeof(input[0]); - size_t iv_size = sizeof(iv)/sizeof(iv[0]); + size_t key_length = sizeof(key_data); + size_t input_length = sizeof(input); + size_t iv_size = sizeof(iv); int32_t status; /* Initialize the PSA crypto library*/ @@ -173,5 +177,9 @@ int32_t psa_cipher_abort_before_update_test(security_t caller) input_length, output, SIZE_32B, &length); TEST_ASSERT_EQUAL(status, PSA_ERROR_BAD_STATE, TEST_CHECKPOINT_NUM(9)); + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); + return VAL_STATUS_SUCCESS; } diff --git a/api-tests/dev_apis/crypto/test_c039/test_c039.c b/api-tests/dev_apis/crypto/test_c039/test_c039.c index 0053fdd1..30a833e1 100644 --- a/api-tests/dev_apis/crypto/test_c039/test_c039.c +++ b/api-tests/dev_apis/crypto/test_c039/test_c039.c @@ -128,7 +128,10 @@ int32_t psa_asymmetric_encrypt_test(security_t caller) TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(5)); if (is_buffer_empty(check1[i].salt, check1[i].salt_length) == TRUE) + { salt = NULL; + check1[i].salt_length = 0; + } else salt = check1[i].salt; @@ -139,10 +142,16 @@ int32_t psa_asymmetric_encrypt_test(security_t caller) TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(6)); if (check1[i].expected_status != PSA_SUCCESS) + { + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(7)); + continue; + } /* Check if the output length matches with the expected output length */ - TEST_ASSERT_EQUAL(length, check1[i].expected_output_length, TEST_CHECKPOINT_NUM(7)); + TEST_ASSERT_EQUAL(length, check1[i].expected_output_length, TEST_CHECKPOINT_NUM(8)); /* We test encryption by checking that encrypt-then-decrypt gives back * the original plaintext because of the non-optional random @@ -152,14 +161,18 @@ int32_t psa_asymmetric_encrypt_test(security_t caller) status = val->crypto_function(VAL_CRYPTO_ASYMMTERIC_DECRYPT, check1[i].key_handle, check1[i].key_alg, output, length, salt, check1[i].salt_length, output, check1[i].output_size, &length); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(8)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(9)); /* Check if the output length matches with the input length */ - TEST_ASSERT_EQUAL(length, check1[i].input_length, TEST_CHECKPOINT_NUM(9)); + TEST_ASSERT_EQUAL(length, check1[i].input_length, TEST_CHECKPOINT_NUM(10)); /* Check if the output matches with the given input data */ - TEST_ASSERT_MEMCMP(output, check1[i].input, length, TEST_CHECKPOINT_NUM(10)); + TEST_ASSERT_MEMCMP(output, check1[i].input, length, TEST_CHECKPOINT_NUM(11)); } + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(12)); } return VAL_STATUS_SUCCESS; @@ -179,9 +192,6 @@ int32_t psa_asymmetric_encrypt_negative_test(security_t caller) for (i = 0; i < num_checks; i++) { - val->print(PRINT_TEST, "[Check %d] Test psa_asymmetric_encrypt - Invalid key handle\n", - g_test_count++); - /* Initialize a key policy structure to a default that forbids all * usage of the key */ @@ -223,7 +233,10 @@ int32_t psa_asymmetric_encrypt_negative_test(security_t caller) TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(6)); if (is_buffer_empty(check1[i].salt, check1[i].salt_length) == TRUE) + { salt = NULL; + check1[i].salt_length = 0; + } else salt = check1[i].salt; @@ -231,7 +244,7 @@ int32_t psa_asymmetric_encrypt_negative_test(security_t caller) status = val->crypto_function(VAL_CRYPTO_ASYMMTERIC_ENCRYPT, check2[i].key_handle, check2[i].key_alg, check2[i].input, check2[i].input_length, salt, check2[i].salt_length, output, check2[i].output_size, &length); - TEST_ASSERT_EQUAL(status, PSA_ERROR_EMPTY_SLOT, TEST_CHECKPOINT_NUM(7)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_DOES_NOT_EXIST, TEST_CHECKPOINT_NUM(7)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c039/test_data.h b/api-tests/dev_apis/crypto/test_c039/test_data.h index 48538253..5bb5109c 100644 --- a/api-tests/dev_apis/crypto/test_c039/test_data.h +++ b/api-tests/dev_apis/crypto/test_c039/test_data.h @@ -167,13 +167,13 @@ static test_data check1[] = { 128, 1024, PSA_SUCCESS }, -{"Test psa_asymmetric_encrypt - Small output buffer\n", 5, PSA_KEY_TYPE_RSA_PUBLIC_KEY, -{0}, 162, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT, PSA_ALG_RSA_PKCS1V15_CRYPT, +{"Test psa_asymmetric_encrypt - Small output buffer\n", 5, PSA_KEY_TYPE_RSA_KEYPAIR, +{0}, 610, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT, PSA_ALG_RSA_PKCS1V15_CRYPT, {0}, 0, {0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, - 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}, 22, 110, - 128, 1024, PSA_ERROR_INVALID_ARGUMENT + 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}, 22, 120, + 128, 1024, PSA_ERROR_BUFFER_TOO_SMALL }, #endif diff --git a/api-tests/dev_apis/crypto/test_c040/test_c040.c b/api-tests/dev_apis/crypto/test_c040/test_c040.c index 69dc7ef0..760ab197 100644 --- a/api-tests/dev_apis/crypto/test_c040/test_c040.c +++ b/api-tests/dev_apis/crypto/test_c040/test_c040.c @@ -128,7 +128,10 @@ int32_t psa_asymmetric_decrypt_test(security_t caller) TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(5)); if (is_buffer_empty(check1[i].salt, check1[i].salt_length) == TRUE) + { salt = NULL; + check1[i].salt_length = 0; + } else salt = check1[i].salt; @@ -139,13 +142,23 @@ int32_t psa_asymmetric_decrypt_test(security_t caller) TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(6)); if (check1[i].expected_status != PSA_SUCCESS) + { + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(7)); + continue; + } /* Check if the output length matches with the expected length */ - TEST_ASSERT_EQUAL(length, check1[i].expected_output_length, TEST_CHECKPOINT_NUM(7)); + TEST_ASSERT_EQUAL(length, check1[i].expected_output_length, TEST_CHECKPOINT_NUM(8)); /* Check if the output matches with the expected data */ - TEST_ASSERT_MEMCMP(output, check1[i].expected_output, length, TEST_CHECKPOINT_NUM(8)); + TEST_ASSERT_MEMCMP(output, check1[i].expected_output, length, TEST_CHECKPOINT_NUM(9)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); } return VAL_STATUS_SUCCESS; @@ -165,9 +178,6 @@ int32_t psa_asymmetric_decrypt_negative_test(security_t caller) for (i = 0; i < num_checks; i++) { - val->print(PRINT_TEST, "[Check %d] Test psa_asymmetric_decrypt - Invalid key handle\n", - g_test_count++); - /* Initialize a key policy structure to a default that forbids all * usage of the key */ @@ -209,7 +219,10 @@ int32_t psa_asymmetric_decrypt_negative_test(security_t caller) TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(6)); if (is_buffer_empty(check1[i].salt, check1[i].salt_length) == TRUE) + { salt = NULL; + check1[i].salt_length = 0; + } else salt = check1[i].salt; @@ -217,7 +230,7 @@ int32_t psa_asymmetric_decrypt_negative_test(security_t caller) status = val->crypto_function(VAL_CRYPTO_ASYMMTERIC_DECRYPT, check2[i].key_handle, check2[i].key_alg, check2[i].input, check2[i].input_length, salt, check2[i].salt_length, output, check2[i].output_size, &length); - TEST_ASSERT_EQUAL(status, PSA_ERROR_EMPTY_SLOT, TEST_CHECKPOINT_NUM(7)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_DOES_NOT_EXIST, TEST_CHECKPOINT_NUM(7)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c041/test_c041.c b/api-tests/dev_apis/crypto/test_c041/test_c041.c index 55106f72..101e16a0 100644 --- a/api-tests/dev_apis/crypto/test_c041/test_c041.c +++ b/api-tests/dev_apis/crypto/test_c041/test_c041.c @@ -120,13 +120,23 @@ int32_t psa_asymmetric_sign_test(security_t caller) TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(6)); if (check1[i].expected_status != PSA_SUCCESS) + { + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(7)); + continue; + } /* Check if the output length matches with the expected length */ - TEST_ASSERT_EQUAL(length, check1[i].expected_signature_length, TEST_CHECKPOINT_NUM(7)); + TEST_ASSERT_EQUAL(length, check1[i].expected_signature_length, TEST_CHECKPOINT_NUM(8)); /* Check if the output matches with the expected data */ - TEST_ASSERT_MEMCMP(signature, check1[i].expected_signature, length, TEST_CHECKPOINT_NUM(8)); + TEST_ASSERT_MEMCMP(signature, check1[i].expected_signature, length, TEST_CHECKPOINT_NUM(9)); + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); } return VAL_STATUS_SUCCESS; @@ -145,8 +155,6 @@ int32_t psa_asymmetric_sign_negative_test(security_t caller) for (i = 0; i < num_checks; i++) { - val->print(PRINT_TEST, "[Check %d] Test psa_asymmetric_sign - Invalid key handle\n", - g_test_count++); /* Initialize a key policy structure to a default that forbids all * usage of the key */ @@ -168,7 +176,7 @@ int32_t psa_asymmetric_sign_negative_test(security_t caller) signature, check2[i].signature_size, &length); TEST_ASSERT_EQUAL(status, PSA_ERROR_INVALID_HANDLE, TEST_CHECKPOINT_NUM(3)); - val->print(PRINT_TEST, "[Check %d] Test psa_asymmetric_sign - zero as key handle\n", + val->print(PRINT_TEST, "[Check %d] Test psa_asymmetric_sign - Zero as key handle\n", g_test_count++); /* Sign a hash or short message with a private key */ status = val->crypto_function(VAL_CRYPTO_ASYMMTERIC_SIGN, 0, @@ -191,7 +199,7 @@ int32_t psa_asymmetric_sign_negative_test(security_t caller) status = val->crypto_function(VAL_CRYPTO_ASYMMTERIC_SIGN, check2[i].key_handle, check2[i].key_alg, check2[i].input, check2[i].input_length, signature, check2[i].signature_size, &length); - TEST_ASSERT_EQUAL(status, PSA_ERROR_EMPTY_SLOT, TEST_CHECKPOINT_NUM(7)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_DOES_NOT_EXIST, TEST_CHECKPOINT_NUM(7)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c042/test_c042.c b/api-tests/dev_apis/crypto/test_c042/test_c042.c index 97f3be99..43ed5ecd 100644 --- a/api-tests/dev_apis/crypto/test_c042/test_c042.c +++ b/api-tests/dev_apis/crypto/test_c042/test_c042.c @@ -114,6 +114,10 @@ int32_t psa_asymmetric_verify_test(security_t caller) check1[i].key_alg, check1[i].input, check1[i].input_length, check1[i].signature, check1[i].signature_size); TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(6)); + + /* Destroy a key and restore the slot to its default state */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(7)); } return VAL_STATUS_SUCCESS; @@ -178,7 +182,7 @@ int32_t psa_asymmetric_verify_negative_test(security_t caller) status = val->crypto_function(VAL_CRYPTO_ASYMMTERIC_VERIFY, check2[i].key_handle, check2[i].key_alg, check2[i].input, check2[i].input_length, check2[i].signature, check2[i].signature_size); - TEST_ASSERT_EQUAL(status, PSA_ERROR_EMPTY_SLOT, TEST_CHECKPOINT_NUM(7)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_DOES_NOT_EXIST, TEST_CHECKPOINT_NUM(7)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c043/test_c043.c b/api-tests/dev_apis/crypto/test_c043/test_c043.c index 2d665746..c61aad40 100644 --- a/api-tests/dev_apis/crypto/test_c043/test_c043.c +++ b/api-tests/dev_apis/crypto/test_c043/test_c043.c @@ -86,28 +86,36 @@ int32_t psa_key_agreement_test(security_t caller) /* Abort a generator */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_ABORT, &generator); TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(7)); + + /* Destroy a key and restore the slot to its default state */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(8)); continue; } /* Retrieve the current capacity of a generator */ status = val->crypto_function(VAL_CRYPTO_GET_GENERATOR_CAPACITY, &generator, &capacity); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(8)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(9)); /* Check if the generator capacity matches with the expected capacity */ - TEST_ASSERT_EQUAL(capacity, check1[i].expected_capacity, TEST_CHECKPOINT_NUM(9)); + TEST_ASSERT_EQUAL(capacity, check1[i].expected_capacity, TEST_CHECKPOINT_NUM(10)); /* Read some data from a generator */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_READ, &generator, output, check1[i].expected_output_length); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(11)); /* Check if the output matches with the expected data */ TEST_ASSERT_MEMCMP(output, check1[i].expected_output, check1[i].expected_output_length, - TEST_CHECKPOINT_NUM(11)); + TEST_CHECKPOINT_NUM(12)); /* Abort a generator */ status = val->crypto_function(VAL_CRYPTO_GENERATOR_ABORT, &generator); - TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(12)); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(13)); + + /* Destroy a key and restore the slot to its default state */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(14)); } return VAL_STATUS_SUCCESS; @@ -172,7 +180,7 @@ int32_t psa_key_agreement_negative_test(security_t caller) status = val->crypto_function(VAL_CRYPTO_KEY_AGREEMENT, &generator, check2[i].key_handle, check2[i].peer_key, check2[i].peer_key_length, check2[i].key_alg); - TEST_ASSERT_EQUAL(status, PSA_ERROR_EMPTY_SLOT, TEST_CHECKPOINT_NUM(7)); + TEST_ASSERT_EQUAL(status, PSA_ERROR_DOES_NOT_EXIST, TEST_CHECKPOINT_NUM(7)); } return VAL_STATUS_SUCCESS; diff --git a/api-tests/dev_apis/crypto/test_c044/source.mk b/api-tests/dev_apis/crypto/test_c044/source.mk new file mode 100644 index 00000000..3e694cc3 --- /dev/null +++ b/api-tests/dev_apis/crypto/test_c044/source.mk @@ -0,0 +1,20 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_c044.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = diff --git a/api-tests/dev_apis/crypto/test_c044/test_c044.c b/api-tests/dev_apis/crypto/test_c044/test_c044.c new file mode 100644 index 00000000..aa597895 --- /dev/null +++ b/api-tests/dev_apis/crypto/test_c044/test_c044.c @@ -0,0 +1,302 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_c044.h" +#include "test_data.h" +#include "val_crypto.h" + +client_test_t test_c044_crypto_list[] = { + NULL, + psa_copy_key_test, + psa_copy_key_negative_test, + NULL, +}; + +static int g_test_count = 1; +static uint8_t data[BUFFER_SIZE]; + +int32_t psa_copy_key_test(security_t caller) +{ + uint32_t length, i; + const uint8_t *key_data; + psa_key_policy_t policy, target_policy, constraint; + psa_key_handle_t target_handle = 0; + psa_key_type_t key_type, target_type; + psa_algorithm_t expected_key_alg; + psa_key_usage_t expected_usage; + size_t bits, target_bits; + int num_checks = sizeof(check1)/sizeof(check1[0]); + int32_t status, export_status; + + /* Initialize the PSA crypto library*/ + status = val->crypto_function(VAL_CRYPTO_INIT); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(1)); + + /* 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); + + /* Setting up the watchdog timer for each check */ + status = val->wd_reprogram_timer(WD_CRYPTO_TIMEOUT); + TEST_ASSERT_EQUAL(status, VAL_STATUS_SUCCESS, TEST_CHECKPOINT_NUM(2)); + + /* Initialize a key policy structure to a default that forbids all + * usage of the key + */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &policy); + + 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); + + /* Allocate a key slot for a transient key */ + status = val->crypto_function(VAL_CRYPTO_ALLOCATE_KEY, &check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(3)); + + /* Set the usage policy on a key slot */ + status = val->crypto_function(VAL_CRYPTO_SET_KEY_POLICY, check1[i].key_handle, &policy); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(4)); + + /* Import the key data into the key slot */ + status = val->crypto_function(VAL_CRYPTO_IMPORT_KEY, check1[i].key_handle, + check1[i].key_type, key_data, check1[i].key_length); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(5)); + + /* Get basic metadata about a key */ + status = val->crypto_function(VAL_CRYPTO_GET_KEY_INFORMATION, check1[i].key_handle, + &key_type, &bits); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(6)); + + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &target_policy); + + /* Allocate a key slot for a transient key */ + status = val->crypto_function(VAL_CRYPTO_ALLOCATE_KEY, &target_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(7)); + + /* Set the standard fields of a policy structure */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_SET_USAGE, &target_policy, + check1[i].target_usage, check1[i].target_key_alg); + + /* Set the usage policy on a key slot */ + status = val->crypto_function(VAL_CRYPTO_SET_KEY_POLICY, target_handle, &target_policy); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(8)); + + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &target_policy); + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &constraint); + + val->crypto_function(VAL_CRYPTO_KEY_POLICY_SET_USAGE, &constraint, + check1[i].constraint_usage, check1[i].constraint_key_alg); + /* Make a copy of a key */ + status = val->crypto_function(VAL_CRYPTO_COPY_KEY, check1[i].key_handle, target_handle, + &constraint); + TEST_ASSERT_EQUAL(status, check1[i].expected_status, TEST_CHECKPOINT_NUM(9)); + + if (check1[i].expected_status != PSA_SUCCESS) + continue; + + /* Destroy the source to ensure that this doesn't affect the target */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check1[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); + + /* Get basic metadata about a key */ + status = val->crypto_function(VAL_CRYPTO_GET_KEY_INFORMATION, target_handle, + &target_type, &target_bits); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(11)); + + TEST_ASSERT_EQUAL(target_type, key_type, TEST_CHECKPOINT_NUM(12)); + TEST_ASSERT_EQUAL(target_bits, bits, TEST_CHECKPOINT_NUM(13)); + + status = val->crypto_function(VAL_CRYPTO_GET_KEY_POLICY, target_handle, &target_policy); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(14)); + + val->crypto_function(VAL_CRYPTO_KEY_POLICY_GET_USAGE, &target_policy, &expected_usage); + val->crypto_function(VAL_CRYPTO_KEY_POLICY_GET_ALGORITHM, &target_policy, + &expected_key_alg); + + TEST_ASSERT_EQUAL(expected_usage, check1[i].expected_usage, TEST_CHECKPOINT_NUM(15)); + TEST_ASSERT_EQUAL(expected_key_alg, check1[i].expected_key_alg, TEST_CHECKPOINT_NUM(16)); + + if (expected_usage & PSA_KEY_USAGE_EXPORT) + export_status = PSA_SUCCESS; + else + export_status = PSA_ERROR_NOT_PERMITTED; + + /* Export a key in binary format */ + status = val->crypto_function(VAL_CRYPTO_EXPORT_KEY, target_handle, data, + BUFFER_SIZE, &length); + TEST_ASSERT_EQUAL(status, export_status, TEST_CHECKPOINT_NUM(17)); + + if (export_status != PSA_SUCCESS) + continue; + + TEST_ASSERT_EQUAL(length, check1[i].expected_key_length, TEST_CHECKPOINT_NUM(18)); + + if (PSA_KEY_TYPE_IS_UNSTRUCTURED(check1[i].key_type)) + { + TEST_ASSERT_MEMCMP(data, check1[i].key_data, length, TEST_CHECKPOINT_NUM(19)); + } + else if (PSA_KEY_TYPE_IS_RSA(check1[i].key_type) || PSA_KEY_TYPE_IS_ECC(check1[i].key_type)) + { + TEST_ASSERT_MEMCMP(data, key_data, length, TEST_CHECKPOINT_NUM(20)); + } + else + { + return VAL_STATUS_INVALID; + } + + /* Destroy the key */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, target_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(21)); + } + + return VAL_STATUS_SUCCESS; +} + +int32_t psa_copy_key_negative_test(security_t caller) +{ + int num_checks = sizeof(check2)/sizeof(check2[0]); + int32_t i, status; + psa_key_policy_t policy, target_policy, constraint; + psa_key_handle_t target_handle; + + /* Initialize the PSA crypto library*/ + status = val->crypto_function(VAL_CRYPTO_INIT); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(1)); + + for (i = 0; i < num_checks; i++) + { + val->print(PRINT_TEST, "[Check %d] Test psa_copy_key with unallocated target key slot\n", + g_test_count++); + + /* Setting up the watchdog timer for each check */ + status = val->wd_reprogram_timer(WD_CRYPTO_TIMEOUT); + TEST_ASSERT_EQUAL(status, VAL_STATUS_SUCCESS, TEST_CHECKPOINT_NUM(2)); + + /* Allocate a key slot for a transient key */ + status = val->crypto_function(VAL_CRYPTO_ALLOCATE_KEY, &check2[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(3)); + + /* Initialize a key policy structure to a default that forbids all + * usage of the key + */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &policy); + memset(&target_handle, 0xDEADDEAD, sizeof(target_handle)); + + /* 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_handle, + check2[i].key_type, check2[i].key_data, check2[i].key_length); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(4)); + + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &constraint); + val->crypto_function(VAL_CRYPTO_KEY_POLICY_SET_USAGE, &constraint, + check2[i].constraint_usage, check2[i].constraint_key_alg); + + /* Make a copy of a key with unallocated target handle*/ + status = val->crypto_function(VAL_CRYPTO_COPY_KEY, check2[i].key_handle, target_handle, + &constraint); + TEST_ASSERT_EQUAL(status, PSA_ERROR_INVALID_HANDLE, TEST_CHECKPOINT_NUM(5)); + + val->print(PRINT_TEST, "[Check %d] Test psa_copy_key with target containing key material\n", + g_test_count++); + val->crypto_function(VAL_CRYPTO_KEY_POLICY_INIT, &target_policy); + + /* Allocate a key slot for a target key */ + status = val->crypto_function(VAL_CRYPTO_ALLOCATE_KEY, &target_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(6)); + + /* Set the standard fields of a policy structure */ + val->crypto_function(VAL_CRYPTO_KEY_POLICY_SET_USAGE, &target_policy, + check2[i].target_usage, check2[i].target_key_alg); + + /* Set the usage policy on a key slot */ + status = val->crypto_function(VAL_CRYPTO_SET_KEY_POLICY, target_handle, &target_policy); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(7)); + + /* Make a copy of a key */ + status = val->crypto_function(VAL_CRYPTO_COPY_KEY, check2[i].key_handle, target_handle, + &constraint); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(8)); + + /* Make a copy of a key in the existing target slot*/ + status = val->crypto_function(VAL_CRYPTO_COPY_KEY, check2[i].key_handle, target_handle, + &constraint); + TEST_ASSERT_EQUAL(status, PSA_ERROR_ALREADY_EXISTS, TEST_CHECKPOINT_NUM(9)); + + val->print(PRINT_TEST, "[Check %d] Test psa_copy_key with no source handle\n", + g_test_count++); + /* Destroy the contents of source and target slots */ + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, check2[i].key_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(10)); + + status = val->crypto_function(VAL_CRYPTO_DESTROY_KEY, target_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(11)); + + /* Allocate a key slot for a target key */ + status = val->crypto_function(VAL_CRYPTO_ALLOCATE_KEY, &target_handle); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(12)); + + /* Set the usage policy on a key slot */ + status = val->crypto_function(VAL_CRYPTO_SET_KEY_POLICY, target_handle, &target_policy); + TEST_ASSERT_EQUAL(status, PSA_SUCCESS, TEST_CHECKPOINT_NUM(13)); + + /* Make a copy of a key with no source material*/ + status = val->crypto_function(VAL_CRYPTO_COPY_KEY, check2[i].key_handle, target_handle, + &constraint); + TEST_ASSERT_EQUAL(status, PSA_ERROR_DOES_NOT_EXIST, TEST_CHECKPOINT_NUM(14)); + } + + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/dev_apis/crypto/test_c044/test_c044.h b/api-tests/dev_apis/crypto/test_c044/test_c044.h new file mode 100644 index 00000000..f187d3b0 --- /dev/null +++ b/api-tests/dev_apis/crypto/test_c044/test_c044.h @@ -0,0 +1,31 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_C044_CLIENT_TESTS_H_ +#define _TEST_C044_CLIENT_TESTS_H_ + +#include "val_crypto.h" +#define test_entry CONCAT(test_entry_, c044) +#define val CONCAT(val, test_entry) +#define psa CONCAT(psa, test_entry) + +extern val_api_t *val; +extern psa_api_t *psa; +extern client_test_t test_c044_crypto_list[]; + +int32_t psa_copy_key_test(security_t caller); +int32_t psa_copy_key_negative_test(security_t caller); +#endif /* _TEST_C044_CLIENT_TESTS_H_ */ diff --git a/api-tests/dev_apis/crypto/test_c044/test_data.h b/api-tests/dev_apis/crypto/test_c044/test_data.h new file mode 100644 index 00000000..47786db8 --- /dev/null +++ b/api-tests/dev_apis/crypto/test_c044/test_data.h @@ -0,0 +1,348 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_handle_t key_handle; + psa_key_type_t key_type; + uint8_t key_data[34]; + uint32_t key_length; + psa_key_usage_t usage; + psa_key_usage_t target_usage; + psa_key_usage_t constraint_usage; + psa_key_usage_t expected_usage; + psa_algorithm_t key_alg; + psa_algorithm_t target_key_alg; + psa_algorithm_t constraint_key_alg; + psa_algorithm_t expected_key_alg; + uint32_t expected_bit_length; + uint32_t expected_key_length; + psa_status_t expected_status; +} test_data; + +static const uint8_t rsa_384_keypair[1]; +static const uint8_t rsa_384_keydata[1]; + +static const 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}; + +static const uint8_t rsa_256_keydata[] = { + 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}; + +static const uint8_t ec_keydata[] = { + 0x04, 0xde, 0xa5, 0xe4, 0x5d, 0x0e, 0xa3, 0x7f, 0xc5, 0x66, 0x23, 0x2a, 0x50, 0x8f, + 0x4a, 0xd2, 0x0e, 0xa1, 0x3d, 0x47, 0xe4, 0xbf, 0x5f, 0xa4, 0xd5, 0x4a, 0x57, 0xa0, + 0xba, 0x01, 0x20, 0x42, 0x08, 0x70, 0x97, 0x49, 0x6e, 0xfc, 0x58, 0x3f, 0xed, 0x8b, + 0x24, 0xa5, 0xb9, 0xbe, 0x9a, 0x51, 0xde, 0x06, 0x3f, 0x5a, 0x00, 0xa8, 0xb6, 0x98, + 0xa1, 0x6f, 0xd7, 0xf2, 0x9b, 0x54, 0x85, 0xf3, 0x20}; + +static const uint8_t ec_keypair[] = { + 0x68, 0x49, 0xf9, 0x7d, 0x10, 0x66, 0xf6, 0x99, 0x77, 0x59, 0x63, 0x7c, 0x7e, 0x38, + 0x99, 0x46, 0x4c, 0xee, 0x3e, 0xc7, 0xac, 0x97, 0x06, 0x53, 0xa0, 0xbe, 0x07, 0x42}; + +static test_data check1[] = { +#ifdef ARCH_TEST_CIPER_MODE_CTR +#ifdef ARCH_TEST_AES_128 +{"Test psa_copy_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, 0x77}, + AES_16B_KEY_SIZE, + PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, + PSA_ALG_CTR, PSA_ALG_CTR, PSA_ALG_CTR, PSA_ALG_CTR, + BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_SUCCESS +}, +#endif + +#ifdef ARCH_TEST_AES_192 +{"Test psa_copy_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, 0x05}, + AES_24B_KEY_SIZE, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CTR, PSA_ALG_CTR, PSA_ALG_CTR, PSA_ALG_CTR, + BYTES_TO_BITS(AES_24B_KEY_SIZE), AES_24B_KEY_SIZE, PSA_SUCCESS +}, +#endif + +#ifdef ARCH_TEST_AES_256 +{"Test psa_copy_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_KEY_USAGE_DECRYPT, PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, + PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, + PSA_ALG_CTR, PSA_ALG_CTR, PSA_ALG_CTR, PSA_ALG_CTR, + BYTES_TO_BITS(AES_32B_KEY_SIZE), AES_32B_KEY_SIZE, PSA_SUCCESS +}, +#endif +#endif + +#ifdef ARCH_TEST_RSA_PKCS1V15_SIGN_RAW +#ifdef ARCH_TEST_RSA_2048 +{"Test psa_copy_key 2048 RSA public key\n", 4, PSA_KEY_TYPE_RSA_PUBLIC_KEY, + {0}, + 270, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_SIGN, PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_SIGN, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_ENCRYPT, PSA_KEY_USAGE_EXPORT, + PSA_ALG_RSA_PKCS1V15_SIGN_RAW, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + PSA_ALG_RSA_PKCS1V15_SIGN_RAW, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + 2048, 270, PSA_SUCCESS +}, + +{"Test psa_copy_key with RSA 2048 keypair\n", 5, PSA_KEY_TYPE_RSA_KEYPAIR, + {0}, + 1193, + PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, + PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_VERIFY, PSA_KEY_USAGE_EXPORT, + PSA_ALG_RSA_PKCS1V15_SIGN_RAW, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + PSA_ALG_RSA_PKCS1V15_SIGN_RAW, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + 2048, 1193, PSA_SUCCESS +}, + +{"Test psa_copy_key with Incompatible target policy(source and target)\n", + 12, PSA_KEY_TYPE_RSA_KEYPAIR, + {0}, + 1193, + PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, + PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_ANY_HASH), PSA_ALG_RSA_PSS(PSA_ALG_ANY_HASH), + -1, -1, + 2048, 1193, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_copy_key with Incompatible constraint\n", 6, PSA_KEY_TYPE_RSA_KEYPAIR, + {0}, + 1193, + PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, + PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_ANY_HASH), PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_ANY_HASH), + PSA_ALG_RSA_PSS(PSA_ALG_ANY_HASH), -1, + 2048, 1193, PSA_ERROR_INVALID_ARGUMENT +}, + +{"Test psa_copy_key with unexport source key usage\n", 7, PSA_KEY_TYPE_RSA_KEYPAIR, + {0}, + 1193, + PSA_KEY_USAGE_SIGN, PSA_KEY_USAGE_EXPORT, + PSA_KEY_USAGE_EXPORT, 0, + PSA_ALG_RSA_PKCS1V15_SIGN_RAW, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + PSA_ALG_RSA_PKCS1V15_SIGN_RAW, PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + 2048, 1193, PSA_SUCCESS +}, + +#endif +#endif + +#ifdef ARCH_TEST_CIPER_MODE_CTR +#ifdef ARCH_TEST_DES_1KEY +{"Test psa_copy_key with DES 64 bit key\n", 8, PSA_KEY_TYPE_DES, + {0x70, 0x24, 0x55, 0x0C, 0x14, 0x9D, 0xED, 0x29}, + DES_8B_KEY_SIZE, + PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, + PSA_ALG_CTR, PSA_ALG_CTR, PSA_ALG_CTR, PSA_ALG_CTR, + BYTES_TO_BITS(DES_8B_KEY_SIZE), DES_8B_KEY_SIZE, PSA_SUCCESS +}, +#endif + +#ifdef ARCH_TEST_DES_2KEY +{"Test psa_copy_key with Triple DES 2-Key\n", 9, 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_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, + PSA_ALG_CTR, PSA_ALG_CTR, PSA_ALG_CTR, PSA_ALG_CTR, + BYTES_TO_BITS(DES3_2KEY_SIZE), DES3_2KEY_SIZE, PSA_SUCCESS +}, +#endif + +#ifdef ARCH_TEST_DES_3KEY +{"Test psa_copy_key with Triple DES 3-Key\n", 10, 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_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, + PSA_ALG_CTR, PSA_ALG_CTR, PSA_ALG_CTR, PSA_ALG_CTR, + BYTES_TO_BITS(DES3_3KEY_SIZE), DES3_3KEY_SIZE, PSA_SUCCESS +}, +#endif +#endif + +#ifdef ARCH_TEST_ECDSA +#ifdef ARCH_TEST_ECC_CURVE_SECP256R1 +{"Test psa_copy_key with EC Public key\n", 11, + PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_CURVE_SECP256R1), + {0}, + 65, + PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, + PSA_ALG_ECDSA_ANY, PSA_ALG_ECDSA_ANY, PSA_ALG_ECDSA_ANY, PSA_ALG_ECDSA_ANY, + 256, 65, PSA_SUCCESS +}, +#endif + +#ifdef ARCH_TEST_ECC_CURVE_SECP224R1 +{"Test psa_copy_key with EC keypair\n", 12, + PSA_KEY_TYPE_ECC_KEYPAIR(PSA_ECC_CURVE_SECP224R1), + {0}, + 28, + PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, + PSA_ALG_ECDSA_ANY, PSA_ALG_ECDSA_ANY, PSA_ALG_ECDSA_ANY, PSA_ALG_ECDSA_ANY, + 224, 28, PSA_SUCCESS +}, +#endif +#endif + +#ifdef ARCH_TEST_CIPER_MODE_CTR +#ifdef ARCH_TEST_AES +{"Test psa_copy_key with Incompatible target policy\n", 13, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9, 0x77}, + AES_16B_KEY_SIZE, + PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, + PSA_ALG_CTR, PSA_ALG_CBC_NO_PADDING, PSA_ALG_CBC_NO_PADDING, PSA_ALG_CBC_NO_PADDING, + BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_ERROR_INVALID_ARGUMENT +}, +#endif +#endif +}; + +static test_data check2[] = { +#ifdef ARCH_TEST_CIPER_MODE_CTR +#ifdef ARCH_TEST_AES_128 +{"Test psa_copy_key negative cases\n", 14, PSA_KEY_TYPE_AES, +{0x49, 0x8E, 0xC7, 0x7D, 0x01, 0x95, 0x0D, 0x94, 0x2C, 0x16, 0xA5, 0x3E, 0x99, + 0x5F, 0xC9, 0x77}, + AES_16B_KEY_SIZE, + PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, PSA_KEY_USAGE_EXPORT, + PSA_ALG_CTR, PSA_ALG_CTR, PSA_ALG_CTR, PSA_ALG_CTR, + BYTES_TO_BITS(AES_16B_KEY_SIZE), AES_16B_KEY_SIZE, PSA_SUCCESS +}, +#endif +#endif +}; diff --git a/api-tests/dev_apis/crypto/test_c044/test_entry.c b/api-tests/dev_apis/crypto/test_c044/test_entry.c new file mode 100644 index 00000000..7eebc5c4 --- /dev/null +++ b/api-tests/dev_apis/crypto/test_c044/test_entry.c @@ -0,0 +1,53 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_c044.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_CRYPTO_BASE, 44) +#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 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_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_c044_crypto_list, FALSE); + + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->crypto_function(VAL_CRYPTO_FREE); + val->test_exit(); +} diff --git a/api-tests/dev_apis/crypto/testsuite.db b/api-tests/dev_apis/crypto/testsuite.db index 66170eea..ebe6839a 100644 --- a/api-tests/dev_apis/crypto/testsuite.db +++ b/api-tests/dev_apis/crypto/testsuite.db @@ -63,5 +63,6 @@ test_c040 test_c041 test_c042 test_c043 +test_c044 (END) diff --git a/api-tests/dev_apis/internal_trusted_storage/test_s002/test_its_data.h b/api-tests/dev_apis/internal_trusted_storage/test_s002/test_its_data.h index 325e862e..303b25fd 100755 --- a/api-tests/dev_apis/internal_trusted_storage/test_s002/test_its_data.h +++ b/api-tests/dev_apis/internal_trusted_storage/test_s002/test_its_data.h @@ -118,6 +118,9 @@ static const test_data s002_data[] = { { VAL_ITS_SET, PSA_ITS_ERROR_WRITE_ONCE /* Setting flag to zero for UID should fail */ }, +{ + VAL_ITS_REMOVE, PSA_ITS_ERROR_WRITE_ONCE /* Storage should not be removed */ +}, { VAL_ITS_GET_INFO, PSA_ITS_SUCCESS /* Check that the WRITE_ONCE flag is preserved */ }, diff --git a/api-tests/dev_apis/internal_trusted_storage/test_s004/test_s004.c b/api-tests/dev_apis/internal_trusted_storage/test_s004/test_s004.c index 00f42451..9db24a79 100755 --- a/api-tests/dev_apis/internal_trusted_storage/test_s004/test_s004.c +++ b/api-tests/dev_apis/internal_trusted_storage/test_s004/test_s004.c @@ -42,11 +42,11 @@ int32_t psa_sst_get_data_check(security_t caller) uint32_t status,j; /* Set data for UID */ - status = SST_FUNCTION(s004_data[1].api, uid + 1, TEST_BUFF_SIZE, write_buff,0); + status = SST_FUNCTION(s004_data[1].api, uid, TEST_BUFF_SIZE, write_buff,0); TEST_ASSERT_EQUAL(status, s004_data[1].status, TEST_CHECKPOINT_NUM(1)); /* Call get function and check the data consistency */ - status = SST_FUNCTION(s004_data[2].api, uid + 1, 0, TEST_BUFF_SIZE, read_buff); + status = SST_FUNCTION(s004_data[2].api, uid, 0, TEST_BUFF_SIZE, read_buff); TEST_ASSERT_EQUAL(status, s004_data[2].status, TEST_CHECKPOINT_NUM(2)); TEST_ASSERT_MEMCMP(read_buff, write_buff, TEST_BUFF_SIZE, TEST_CHECKPOINT_NUM(3)); diff --git a/api-tests/dev_apis/internal_trusted_storage/test_s006/test_s006.c b/api-tests/dev_apis/internal_trusted_storage/test_s006/test_s006.c index 1c7066b0..d1d81bc7 100755 --- a/api-tests/dev_apis/internal_trusted_storage/test_s006/test_s006.c +++ b/api-tests/dev_apis/internal_trusted_storage/test_s006/test_s006.c @@ -73,7 +73,7 @@ int32_t psa_sst_flags_not_supported(security_t caller) { test_status = psa_sst_remove_api(uid, TEST_BUFF_SIZE, write_buff, (flag & (~PSA_SST_FLAG_WRITE_ONCE))); - if (test_status != VAL_STATUS_SUCCESS) + if (test_status != VAL_STATUS_SUCCESS) return test_status; } else if (status == s006_data[0].status) diff --git a/api-tests/dev_apis/internal_trusted_storage/test_s009/test_its_data.h b/api-tests/dev_apis/internal_trusted_storage/test_s009/test_its_data.h index a4f0b7e1..80e7fb1a 100755 --- a/api-tests/dev_apis/internal_trusted_storage/test_s009/test_its_data.h +++ b/api-tests/dev_apis/internal_trusted_storage/test_s009/test_its_data.h @@ -33,25 +33,31 @@ static const test_data s009_data[] = { 0, 0 /* This is dummy for index0 */ }, { - VAL_ITS_SET, PSA_ITS_ERROR_INVALID_ARGUMENTS /* Call set API with 0 write buffer */ + VAL_ITS_SET, PSA_ITS_SUCCESS /* Call set API with NULL write buffer and 0 length */ }, { - VAL_ITS_GET_INFO, PSA_ITS_ERROR_UID_NOT_FOUND /* Call to get_info API should fail */ + VAL_ITS_GET_INFO, PSA_ITS_SUCCESS /* Verify UID is created */ }, { - VAL_ITS_SET, PSA_ITS_SUCCESS /* Create storage of zero size */ + VAL_ITS_GET, PSA_ITS_SUCCESS /* Call get API with NULL write buffer and 0 length */ }, { - VAL_ITS_SET, PSA_ITS_ERROR_INVALID_ARGUMENTS /* Try to set 0 buffer for previous created storage */ + VAL_ITS_REMOVE, PSA_ITS_SUCCESS /* Remove the storage entity */ +}, +{ + VAL_ITS_GET_INFO, PSA_ITS_ERROR_UID_NOT_FOUND /* Verify UID is removed */ +}, +{ + VAL_ITS_SET, PSA_ITS_SUCCESS /* Create storage of zero size and valid write buffer */ }, { VAL_ITS_GET_INFO, PSA_ITS_SUCCESS /* Call get_info API to check data size */ }, { - 0, 0 /* This is dummy for index6 */ + 0, 0 /* This is dummy for index8 */ }, { - VAL_ITS_GET, PSA_ITS_ERROR_INVALID_ARGUMENTS /* Call get API with 0 read buffer */ + VAL_ITS_GET, PSA_ITS_SUCCESS /* Call get API with 0 length and NULL read buffer */ }, { VAL_ITS_SET, PSA_ITS_SUCCESS /* Increase the asset size */ diff --git a/api-tests/dev_apis/internal_trusted_storage/test_s009/test_ps_data.h b/api-tests/dev_apis/internal_trusted_storage/test_s009/test_ps_data.h index 982561c8..129bca08 100755 --- a/api-tests/dev_apis/internal_trusted_storage/test_s009/test_ps_data.h +++ b/api-tests/dev_apis/internal_trusted_storage/test_s009/test_ps_data.h @@ -33,25 +33,31 @@ static const test_data s009_data[] = { 0, 0 /* This is dummy for index0 */ }, { - VAL_PS_SET, PSA_PS_ERROR_INVALID_ARGUMENT /* Call set API with 0 write buffer */ + VAL_PS_SET, PSA_PS_SUCCESS /* Call set API with NULL write buffer and 0 length */ }, { - VAL_PS_GET_INFO, PSA_PS_ERROR_UID_NOT_FOUND /* Call to get_info API should fail */ + VAL_PS_GET_INFO, PSA_PS_SUCCESS /* Verify UID is created */ }, { - VAL_PS_SET, PSA_PS_SUCCESS /* Create storage of zero size */ + VAL_PS_GET, PSA_PS_SUCCESS /* Call get API with NULL write buffer and 0 length */ }, { - VAL_PS_SET, PSA_PS_ERROR_INVALID_ARGUMENT /* Try to set 0 buffer for previous created storage */ + VAL_PS_REMOVE, PSA_PS_SUCCESS /* Remove the storage entity */ +}, +{ + VAL_PS_GET_INFO, PSA_PS_ERROR_UID_NOT_FOUND /* Verify UID is removed */ +}, +{ + VAL_PS_SET, PSA_PS_SUCCESS /* Create storage of zero size and valid write buffer */ }, { VAL_PS_GET_INFO, PSA_PS_SUCCESS /* Call get_info API to check data size */ }, { - 0, 0 /* This is dummy for index6 */ + 0, 0 /* This is dummy for index8 */ }, { - VAL_PS_GET, PSA_PS_ERROR_INVALID_ARGUMENT /* Call get API with 0 read buffer */ + VAL_PS_GET, PSA_PS_SUCCESS /* Call get API with 0 length and NULL read buffer */ }, { VAL_PS_SET, PSA_PS_SUCCESS /* Increase the asset size */ diff --git a/api-tests/dev_apis/internal_trusted_storage/test_s009/test_s009.c b/api-tests/dev_apis/internal_trusted_storage/test_s009/test_s009.c index a1b54246..4567c666 100755 --- a/api-tests/dev_apis/internal_trusted_storage/test_s009/test_s009.c +++ b/api-tests/dev_apis/internal_trusted_storage/test_s009/test_s009.c @@ -28,7 +28,7 @@ client_test_t test_s009_sst_list[] = { NULL, - psa_sst_invalid_arguments_check, + psa_sst_zero_length_check, NULL, }; @@ -36,7 +36,7 @@ static psa_sst_uid_t uid = UID_BASE_VALUE + 5; static uint8_t write_buff[TEST_BUFF_SIZE] = {0x99, 0x01, 0x02, 0x03, 0x04, 0x23, 0xF6, 0x07, 0x08, \ 0x0D, 0x70, 0xA1, 0xFF, 0xFF, 0x14, 0x73, 0x46, 0x97, 0xE8, 0xDD}; -int32_t psa_sst_invalid_arguments_check(security_t caller) +int32_t psa_sst_zero_length_check(security_t caller) { uint32_t status; @@ -45,38 +45,48 @@ int32_t psa_sst_invalid_arguments_check(security_t caller) status = SST_FUNCTION(s009_data[1].api, uid, 0, NULL, 0); TEST_ASSERT_EQUAL(status, s009_data[1].status, TEST_CHECKPOINT_NUM(1)); - /* Call the get_info function and match the attributes */ + /* Call the get_info function to verify UID created */ status = SST_FUNCTION(s009_data[2].api, uid, &info); TEST_ASSERT_EQUAL(status, s009_data[2].status, TEST_CHECKPOINT_NUM(2)); - /* Set data for UID with length 0 and valid write buffer */ - val->print(PRINT_TEST, "[Check 2] Create UID with zero data length\n", 0); - status = SST_FUNCTION(s009_data[3].api, uid, 0, write_buff, 0); + /* Call get API with NULL read buffer */ + val->print(PRINT_TEST, "[Check 2] Call get API with NULL read buffer and data length 0\n", 0); + status = SST_FUNCTION(s009_data[3].api, uid, 0, 0, NULL); TEST_ASSERT_EQUAL(status, s009_data[3].status, TEST_CHECKPOINT_NUM(3)); - /* Set data for UID with length 0 and NULL pointer */ - val->print(PRINT_TEST, "[Check 3] Try to set NULL buffer for existing UID\n", 0); - status = SST_FUNCTION(s009_data[4].api, uid, 0, NULL, 0); + /* Remove the UID */ + val->print(PRINT_TEST, "[Check 3] Remove the UID\n", 0); + status = SST_FUNCTION(s009_data[4].api, uid); TEST_ASSERT_EQUAL(status, s009_data[4].status, TEST_CHECKPOINT_NUM(4)); - /* Call the get_info function and match the attributes */ + /* Call the get_info function to verify UID is removed */ + val->print(PRINT_TEST, "[Check 4] Call get_info API to verify UID removed\n", 0); status = SST_FUNCTION(s009_data[5].api, uid, &info); TEST_ASSERT_EQUAL(status, s009_data[5].status, TEST_CHECKPOINT_NUM(5)); - TEST_ASSERT_EQUAL(info.size, 0, TEST_CHECKPOINT_NUM(6)); - /* Call get API with NULL read buffer and valid UID */ - val->print(PRINT_TEST, "[Check 4] Call get API with NULL read buffer and data length 0\n", 0); - status = SST_FUNCTION(s009_data[7].api, uid, 0, 0, NULL); + /* Create UID with length 0 and valid write buffer */ + val->print(PRINT_TEST, "[Check 5] Create UID with zero data_len and valid write buffer\n", 0); + status = SST_FUNCTION(s009_data[6].api, uid, 0, write_buff, 0); + TEST_ASSERT_EQUAL(status, s009_data[6].status, TEST_CHECKPOINT_NUM(6)); + + /* Call the get_info function and match the attributes */ + status = SST_FUNCTION(s009_data[7].api, uid, &info); TEST_ASSERT_EQUAL(status, s009_data[7].status, TEST_CHECKPOINT_NUM(7)); + TEST_ASSERT_EQUAL(info.size, 0, TEST_CHECKPOINT_NUM(8)); + + /* Call get API with NULL read buffer and valid UID */ + val->print(PRINT_TEST, "[Check 8] Call get API with NULL read buffer and data length 0\n", 0); + status = SST_FUNCTION(s009_data[9].api, uid, 0, 0, NULL); + TEST_ASSERT_EQUAL(status, s009_data[9].status, TEST_CHECKPOINT_NUM(9)); /* Change the length to test_buff_size */ - val->print(PRINT_TEST, "[Check 5] Increase the length\n", 0); - status = SST_FUNCTION(s009_data[8].api, uid, TEST_BUFF_SIZE, write_buff, 0); - TEST_ASSERT_EQUAL(status, s009_data[8].status, TEST_CHECKPOINT_NUM(8)); + val->print(PRINT_TEST, "[Check 9] Increase the length\n", 0); + status = SST_FUNCTION(s009_data[10].api, uid, TEST_BUFF_SIZE, write_buff, 0); + TEST_ASSERT_EQUAL(status, s009_data[10].status, TEST_CHECKPOINT_NUM(10)); /* Remove the UID */ - status = SST_FUNCTION(s009_data[9].api, uid); - TEST_ASSERT_EQUAL(status, s009_data[9].status, TEST_CHECKPOINT_NUM(9)); + status = SST_FUNCTION(s009_data[11].api, uid); + TEST_ASSERT_EQUAL(status, s009_data[11].status, TEST_CHECKPOINT_NUM(11)); return VAL_STATUS_SUCCESS; } diff --git a/api-tests/dev_apis/internal_trusted_storage/test_s009/test_s009.h b/api-tests/dev_apis/internal_trusted_storage/test_s009/test_s009.h index db084cfc..11b32d17 100755 --- a/api-tests/dev_apis/internal_trusted_storage/test_s009/test_s009.h +++ b/api-tests/dev_apis/internal_trusted_storage/test_s009/test_s009.h @@ -31,6 +31,6 @@ extern val_api_t *val; extern psa_api_t *psa; extern client_test_t test_s009_sst_list[]; -int32_t psa_sst_invalid_arguments_check(security_t caller); +int32_t psa_sst_zero_length_check(security_t caller); #endif /* _TEST_S009_CLIENT_TESTS_H_ */ diff --git a/api-tests/dev_apis/internal_trusted_storage/test_s010/test_its_data.h b/api-tests/dev_apis/internal_trusted_storage/test_s010/test_its_data.h index 7b1b6d37..1a811b11 100644 --- a/api-tests/dev_apis/internal_trusted_storage/test_s010/test_its_data.h +++ b/api-tests/dev_apis/internal_trusted_storage/test_s010/test_its_data.h @@ -29,7 +29,7 @@ typedef struct { static const test_data s010_data[] = { { - VAL_ITS_SET, PSA_ITS_SUCCESS /* Create with UID value zero should fail */ + VAL_ITS_SET, PSA_ITS_ERROR_INVALID_ARGUMENTS /* Create with UID value zero should fail */ }, }; #endif /* _TEST_S010_ITS_DATA_TESTS_H_ */ diff --git a/api-tests/dev_apis/internal_trusted_storage/test_s010/test_ps_data.h b/api-tests/dev_apis/internal_trusted_storage/test_s010/test_ps_data.h index e88ed9b4..fb06deb9 100644 --- a/api-tests/dev_apis/internal_trusted_storage/test_s010/test_ps_data.h +++ b/api-tests/dev_apis/internal_trusted_storage/test_s010/test_ps_data.h @@ -29,7 +29,7 @@ typedef struct { static const test_data s010_data[] = { { - VAL_PS_SET, PSA_PS_SUCCESS /* Create with UID value zero should fail */ + VAL_PS_SET, PSA_PS_ERROR_INVALID_ARGUMENT /* Create with UID value zero should fail */ }, }; #endif /* _TEST_S010_PS_DATA_TESTS_H_ */ diff --git a/api-tests/dev_apis/internal_trusted_storage/test_s010/test_s010.c b/api-tests/dev_apis/internal_trusted_storage/test_s010/test_s010.c index c74bad3f..8e556ce1 100644 --- a/api-tests/dev_apis/internal_trusted_storage/test_s010/test_s010.c +++ b/api-tests/dev_apis/internal_trusted_storage/test_s010/test_s010.c @@ -42,7 +42,7 @@ int32_t psa_sst_uid_value_zero_check(security_t caller) /* Set with UID value zero should fail */ val->print(PRINT_TEST, "[Check 1] Creating storage with UID 0 should fail\n", 0 ); status = SST_FUNCTION(s010_data[0].api, uid, TEST_BUFF_SIZE, write_buff, 0); - TEST_ASSERT_NOT_EQUAL(status, s010_data[0].status, TEST_CHECKPOINT_NUM(1)); + TEST_ASSERT_EQUAL(status, s010_data[0].status, TEST_CHECKPOINT_NUM(1)); return VAL_STATUS_SUCCESS; } diff --git a/api-tests/docs/Arm_PSA_APIs_Arch_Test_Validation_Methodology.pdf b/api-tests/docs/Arm_PSA_APIs_Arch_Test_Validation_Methodology.pdf old mode 100644 new mode 100755 index 1ec5b796743a3be73449ebd671ba0ad21a67765f..b22a7242cc319037785f5f2a385a4cd484f38c02 GIT binary patch literal 590051 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�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%0DNa`ryz?2 zd~zr73)#_aoYTV7P=(%0UvN>I`7I2K2i1tueDB1HmZ)~&T#{nA0j(bgs_B1;&KJi? zfTEjY-)3sg+pTcQDPgJ#6pg%UJaJ%)5(vNKJX0?49I7e+W^EYCZzLGa+b_BIKsU=}0@I8evH!M4jEEgMKrfBae+ zC35EIC00boHwdSesMC=YE2F6shr-nM4E3j^3Hwu+oBFI6U8(t2izX|A`RUwR>m_0z zG^{9e^q>Gy8^Lpr-bLh2z_hV?M-km-)Q4Q!E3$2<+(!w~FTKh`edJB=HTW2I(tu5K z-haNjOhVPAG8pQ^C?;Jwq{6hK4%cfbWSB5^vZoR=^BBKnT&f`uX&cK z;*X)@6_;nKGjK{$g?F|^8%ppa2cOeMv^qz~@movVUThokR4ap=f=Hju$Fa;+9&Ir6zzjy$W>mDYAJy*x?63Pu7gFZNL`ZAAR9|@&(XLP!Kcxjv`~Fr z+Z+JFjn7z9*mjBW&4UU?b5G*S8{?uVPAB`9?Cq}TFou8lPU~uh*=`#~iOdOF^v{qj zX43f>@_Sspdc{z~C0V^Y0w}523mk;U75-uQag7vfyHm@T4RQQa zf-P4aZioQEM-osYJr*>BjN95xifH!{qi?NM5e1z%!=_OZuz~#x7k&JawsRWB61BAk zsq9b=t^z+#ftuFk7}PlB#1LG%QReHpCx&uk*#y?!ofnjtZVNtdc!hi*aVn%HrmUg& z*_U*_c#CIv+wW-D_M(QIyeJBMVxMMf(QfRw;S{4RV_d`Sg^C&DO0^G#3SZ32W*KxD zQmAE-SBTa#bA~^L`05I4Ushm2&}dT@siKN;cbJgu>@#@;B&^*HC@*PT8jK+!U#L5P zy{I7!`Y-JfQ{K_4LneGEce;L4RogpHld(7ilqApNk{x|c!cHO~jyNc&@}2TRi_s;Wbt zo5F-51eaCh!CoAgqkY?x4$cDlcE?R*ZF}WTvm^Q5qE}>r?1OFj1gH%i(3a?6q9h4r zCY=aGn6n#ur{AbnyD*KzZ6AUEvt3B6(s0bPNv5E@<}w_ntR_=?KJvmwG7(N2zGqc9 zQXi?<6%^xwrDV+#ZjnJ+`g@o7_!ki#pmWs$&F~>%⪙AOeK{Q(sfKRYe?*5y-^S< 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

-;dCK%UMzrzVpK6vz|U}gqTIH(@9dtXu22roa?=xH zSQ`YDie64jBQtpOD{*_RDOZhm@KDaPQ9ml_VQ}~#qIfQj){PdU#bv#Oa`rs+oOh_J zRR``PjG@)CJ#EF>chP!j1*(s$My)isgv1HO;#n-Xomru?BN{_*yLxWiAq$Z6am0mW z99pT$*7|A|)u^(P4wyo$l{U=OsGwR zahK3%*4~kDd%Jhw^6_mr0y-sv%yJ$KZs3prdRVUMEAe2##MsbbLuk_DMWFun$kH|Q z`UlY)Hrv6Ks?d}WKkHM;t_3UDs>R9GkKL}U7^w5MFw+cti=EoEg#?>{#Q7zX%dkA~ zW?Oe#GsECdZCH*Z4maHP9=6#66Mvm&DBE785F9wknLZ*>NoS!HAj~7j#j@-;y<1Q_ z<69ihH}@{H?N!lWqhjVfS&qY?I8$}oTN>@XI1e6&#Q9{aS_WO&YrC|w#wc5FrV(ELiGLX06_DjuO8nifE@gx7?PZMnX}SfmIDR z!Ym~S77G;5Bxd-##D}YU&vntQ} z_?&zOb`^$03tA%2vvasP%;USXRrW)L?iXP#1YhSkJ4cV`jbZ}%nl4?of5qtyK8<2v z-35XnDu!y*L{i@_1`f~`Rgfpf-Z&XXRk;ZV4b}kL)G+|)Rw$zeiy8`%V|C1+_8hVQsPns5O#dU_@4Vt!XC2;33{DvO}=Y9$C6p={!CZ8TDphkg-;NsGr zaeC3FrI!gw?j-b_ss0l0c3gjxVRb z-4|x3{8EZ#k?F-Hjoo6N)1s<>N)3h}Re5sJFgZ^cPz4kiAff(A?nSCj9AsB*E{zy1BUVLc$NcJ4d!j zi`P^{NHisk|NS9WrX!t^x_hMjk_>IgQL+OzJL_iEp^1!P%kq~>>*Lt5@zF&I^WP+5 zNRC#OoLIK3v@2A|53JNfnnGi0g@c`M$(-MfIFHy$95 zChVh`m>un%r)mPTHL?`y{4>@A;@?k&m^P%48SV@fX{%wj*+GUS@K|m_-QlfPE`IMN z&Jc8LtOj}Kx9%xv1j~Zq%zt?Jh(fx9v6P@ACJY8oGCzbu9hP{fSHD0idrnlAY~5~o z7mz?-iY$hG*l+mlm6ed|x9)Kx<61{+;H&h&>J2=rB<)VO9$0>?N1#wSwdyKSnQh$z zpX9Oz)5Mp<^}MqKnBZBzh1cHZUWEYjsTB!Wnh`ThKgAWmXz3BZ_nbk&zL!?=%auFc zh`7}RL9`HCsNbH5vm!by^24G1Kq@gDq5{5#n>+dyvXntA?bMn(zS_f<_<|9CWs;eB z=D`@iN_mpd4zHUCs)wAnXvvfJHvw-bM)eD|7YT<;s{E|1e&sM?Kj?B@hXRn&z4hLBPbnYKxsUf_&|dYo|yM;$}0Bz*(qOb4+S6 zQi=VRZDw<9zi`|$(A6Vt%>+w1D5(71`&7y2k5k`AKbehD7iRd(K=bUmZ!XbG{+V7Q zWHf8QQjS7)-D(2L=}s>KqLKN6GXx~EKqo1EP)8o$k)je}eaKTd8YPUqRI(wVl&UyS z0$S0^+pFFnFfIotc4iYs5ywxoeInPM`%|;r)`df6gQb=b?H4U&DG&AABEW2Zb7#as zQ7R>s;^b*K%P78O&ktOvn*|zsu@_*Q?jOAj&Ks6(#1t*2!k4jdsLuV7k6KFeGO0fq z*UDHzYzjxQ*FPYc$7?=eGyDP^$DCu+k7Gfro3?Az&a%6-jDGh%tVo)~QZ)%{P$&eL z6MTP!G7_iK0bl8?RZNi*C*i-3U?$6C8{Z!;$1C~cof$dg6dR%_1hTaDc@Fd*JR}yK zPTSBDblku~mo21Yu7bR0B74}+dN~G<=Zs=*JA|ve!}P*(GKUMVW4z?J$+(nRt}E;| zu<{r5Xn4iUFwtAAbOTDx(&1F3dC(yrp>&9qni4N&@h4!e+~&{5PZ^ivxAw&1gj0(2 zhjaECZa-Zj;}bU~D|;xE8j82HJn%4xREMlb_7Y(7i8lpoeJQLGgyZ`9DRCdbKSj{X zkiitRYYY(k3kV&Ql0dU6x8|JnQDQOA!oR?UAl!0^KPs~gTnk&%0KpS!;zoLFai67= zM3ep7O_J`8EG_G$H`?h_m_Ct*9+G{zp8-KBqm9cD44W~kO)rj3Kl(&RG;-+*^~;U+ zU!VG<%Y_blW0tg%E&Wmb$Bew97TnXRQ-x|mvz`gF3$@i^Is-~zlWh~Z_<*$SO7yaQ z(aiMUt z!FqzCwW*dDG$>~;E}f6uE|)|&yG&>gzw1t^4EgX#+@1$)f`M_#Un|Fh4o-II{d$_S z$y4@&q6|X_YJ)C7$WA`?&0m;R(u}~Y;I5yD^sfi2uMG)p( zO43eq8f?MuOF#6Or>q8y(dqEv7%ERk!a{I`ggZpHN&4m@wL1y(Vs~lXG!v?GTkN<9 zyH8~+1@m0I4#=q3f^8Ni55&Cu&8@S!V?_<%eTO2cB7}FO z*89u_iQ#|OEwC9yjIz4bOIzPf{!U|r_QPn!pd|U5Y}6~{{AxRzyExa0zeZ*XHdXMZ z(zY{b_C2hox*nGjOO3qyTqskK&iDi?`aRb3u=)>`9~I&0Jw7lGxdRuWh|5I)=mgyy zMqvJ%&Nt=2wLgdZ4Aj_&T8(5gs&=tEOVcOolWV(3RyU&qG@fC;jYA7_HH1goK-D$S zmNWs-kapiTA_s4C=l?Z!j?tMk-M@~LiEZ1-9ox2TPLhdj+qP}n6HknZZR3t6KJ%>i z{MUOvJm;+QvAfr*?yg-@ncGES(Rxh*IXTwe-J9Frns9fm zyFFK8w|Boz8-ui;`jqoPFy_9NC(<)a4Tj+WS_VAq=313!E};KZ!Ca_;Vr`QX)WeeT z4vKL#PIRNibKm?;lX;L%VS29n+To|DdZkD@RUvMgGgyhXZZ|RdM$Pq;MPAMyqr?{5 zp8h@oGwpLXjpIx)&&Q&qnrVT}GOwq@#0IgG;H1O^lp_%Ox6&2@J;98HnoOPk? z4wO{T8q~|x6ALN5w7tA!F_=y?jE8Y4)jG6WX9q#gj{KlThQ@6KLxj)_ATZ#&y9kkK zW0+A9G=VhIJ1%@I|GjO+)@z1 zzdTlAw}|u81i?1(`(*J!nD#HAy;*ycQ47oaduBe?Sd{~Twu$D@a*GQh!T4aY^*EGP z{9)gwP)ThV9|Vl$?fxdgGfvD)%87TPS4vrg5WE9PP~!=9nvE>}RO;nb=Pr|iroEvl zm*uEhu*0kGmvT{$a>hm_Eb3xamrSYj{d8?%;EDOTra0BSN4-o~q3d@>QGHwCXF02) zDnFy+Dx=VKVLy2Q48GR%Xx^$k3C1b6fxIu|V2_xQVR*Jh(#!grG9UKdUm6tXf^A_x z``T?+^sCdf@O%GuR2Xs+t};cC?^uiZz8d>D!!hP~tpmO23~J2B3Eh&kQ2IX8* zBdS>Y;wRmg*xgu>2&S=uO-CK}jfdg4PyLpb!GZO3%Z;IcZm48})f(}Oh6<{ZHSt~- zyvIaydqPfZf>rLg#b7wIX!BJv&|WK@Xutw=giZC*SIrY6mjd9BPfo2`9Oo+e8A4%m zji>)egC)lT!csd&%*%f7AQbkM43Dz(Zv=8!K&PcgzLI$!bD{TByU z@<-SWqd{f=k1Hc4KT|#FHOcV>$?D+eS^pgIG}qnsdpp6bgCMLnwNnn-`RTl|*6TMc zIS>Rm%6uk7E?OSGBwR4iv1v?d^314T*X@orxM9RO#ci=tN`kcdYXf~VUOY!r$X+=8 zkwWP!L)z+ZUu?4EC5mgkVF74VwBuy32bs+<>@3`9-tX}3WtXWnKObAqGM-J6VH zQDMa*E@XGo{)Rg`iZK(vBO>*GjJey+d)PkI<&k+Dmkxd{N6zJ?HLb)MDamH0e|*}T zq6Gf*N9b675&*YJYhlRXz}gpKmMC+2Xq=D^BeEd>5mLbnY)|QEsnUEHvy8V-Bv&i$ z`g3Y|-yi$_slbVfRY;itFGoJCL&50gD`yXA4d${L#yUG&M!4is8n0Z@J($-$s@kH6 z=RJ>eR!kvJS-bke(@Zyhtr)DnS*ImIdy)@%40qro1w(a4AM%vQ<<}&qYIuDfBm98~ zVpdx5SI%muq2MmZwOkxpZB(6t&>%A>S!j9@UQzFKpqRCE8N+3Jo&b0irWkUDI$qD; z_CnK9rL#ccU%T6&8ACaTjnlCfL~LZ2Yx%;g&Il1|*wj4B@ruMHqgWSjV~o;cSDA6r z&k>_k*6Hq!=le=GtS=O}8xKt2=19Op_!0q4!HaXPB@W8NbyK?j*)CAMnOqMtTg8ru z81Np@#3~_0Z>6^T9Irg&&{i!QS~q0%3T8ftr(=K|HPNodoM6CD>Ijnsdd^D^>zhUF zBk&1@Htp52NAy_qr@`RcV+`-^`UEX@%ma&u$T}Xe4`Q}p!a53kGcH+DYoZe56@1l$ zJ_RZlHjbM|iJDWPY4kA;7x`Bxgl(ti>@Ddocu1qkd;_S+4#k5Wwy%7a>AmoppClL? zD|S`N(lzYuIqdOAvt|6Yett1` zM+q2AK%x&EHtWOKP`>b}Je+&Kq7gQXHcc!e?$zTKhtg(=O4Lyz1l(@7b_glo*CUNU zZ?c4#F5XL{Rl-Y{0<&&T-)O%H8}Oo^G|S0 zpq%AHQG_=`tcE}w*izQ5hwq@%ypX^djMd?m@*|C%KZMfrOo zVGU0|^F3yD3EO7(?7sZ7G%`@6!|7#x@yXnUs~nM%y4z$_4h67ulDYtsA_`K?J>8+Q z-hqw*nSx9u=jx5}-3huJf8B}|TZ=|gAiX`}3D2b?J2)gJYbplB`9m>sAW=h;FGH=! zJ_HQpN@HP+w8WC!&d7RQq!aFAXmjoPy~|sbq7cS=pYVtc4T||(FSWrT zbI#Pz%1z1(`_;1E7e}IBE56tiQQCNt&$_R;w4nS5c7#4OvFd z1&8ZT(c*E;t)XNueF=gMvWd~FxQvXe_Ialth*54yew(&$3LNnu1gq_?Ks|AV!=--5H9s?P`QGv@24+euvCTbFDu7ZebFCor-z(^W>tNJX|VYGSyA&x7aUf!_(Ls_XuzYfxges zA>nSz1*-CzcGNgMADYh-GFuW`NJaaxem@Pq^qyddmT5DS-vgy7ad;A^C$qg@n9n3j zd&rk^Df=HWcbPCak1W5ne)mlqc|Y13ROIt+vtMO>o-wbTv)s}b&iDw1^9zU_yGA(L zO_iePl}%lnlVqIV`Yuq-XTi@NcP=%tJDm+>eDeN;RebmL%;uStH5kKKi?=4|k@Q2#fqsz=rjK8A0H@vff9nRDMqXfn!z-i-ph zxDLJ*nuu{zILQO~>a^0M8#r(WEuL%8^zOy;J~G9!i@fMh7#YJ6mr2zMu!6(Fudz4L zz1hcD=0@0y@$!l40^Xv(!*QC&&ew}7vvqq7pc(9)A{p zTvh&kMYfY19`Xyz(4fcl6o>TXP?q!C%PaqnC~x9usqsJ18DR~y_1Mp;)r1bPuFc#c z*hcANOAHEdLa;;4vhw&Vp(`c1LoKaZnP}sZng)|YwZv$*&~m8O`g=U6jU;PMT+6oe zk*kBtC^Hsg$jH|(85}_+`#UDg3u6td@dQp&YQNW}60My#`tE}8^q`ZS!FXR@m*hxG zsa{~jEh5- zf4a4R@t0!X*ULzr(L63P*PYv#xJ1bz$=|kc4?_;+2In)&y_*nAL}bxrIABl~UN@u3 zJ&k1smbP@m&5uS(WPDgyRoSM1!s4EP>a^b%MG2LX^l(3cZM6LcqDDsYxLz|5Bi}3p zT-lAM=U?yC)+S()(D{%j8Q){W&uKmNhxYP7Ew@&R67-NF`onJdaAHTkDtjo7KnNGQ zwG0sZ{xlz+))9l${BkRosEYmh`wv=9v9 zlp1hMr7s&73VI zL#xQW&66d>c<6mKUF6VPBp7|tRQJ5x_g~p#;gAhXCi=Ep@;)XL*(6!ovESbkp(Scdq?xKufJu;HDH#8+z;c)Gh~* z7L^$TZ7wg;uW9%4?vM3rV4rN~Ncl4(9@_33(=Exx+VPHDppxgo^3XlFRQz-OvZrN| z*))qe%sBpvG+CpPVpm-7YYv{;H#G(14Ef+0M|9Sl2S@rYc(tF-tT0qK$*oY#E(=jJ zQH2ha+f>@irD_|3I7A-v?qmOjpZWw4aD4xbO*eY3VKU-}8;Ha~-YpLnm~>Y?Ejg|( zCV_?R-84GUA$y&r;yFxuqa(N-ITTyMT}R`j^V|Hp_2rV}kqkNIeC#NL7<=(m1vqtu z3Z&jBuwR3Js6C$wh>zHCt;3CClh zNqLDS1MT!dZ}}CJV7%Z>E#q9Q;bZ49kYxx&ArK_|^FC@4)1;U>>P6j=5_Dc^Ott4e ze;PHR(1?oU67qS5zcqZxm@RwEdNhmvm2}C~=Pxc@;41TT{ zv4zpXz2Et_psuSqX_pG%0I(cIvgD6X!?tk&7IKK}&5H}Gk_)&92>@snW@?CTI1tLY zvLZ>_OwpKho%ngVpVVkrNFNo6(W2xWxPjf-`owFM`F92(W)-bMKGQyR=jLW;hO{eU zd`${Qc}#0p96M~&qZC+}U{h+C`c)FvsDpUc0NAeQONO@29lvQC1jqJok#D5SNA0nH zhNg%X>EALN6IOeVhWJRKLs%8azin zxP;|YRg^xWKV3!!L)nrNud7tpA2vFZ8LB=YMU>&AOc2A5O>&3G#>mjNs1|BR?6o!B zghfNZGHhV~Kxp$StV8?d#sQQ&vT!sz2;g4-g+4k0B*;hoVOvAr5koA?MAzXl3+xbK zgkjcyx3~35F$6e|f?FCLy6tLapBMNt|Be?|E}AbP6$2S-NzeNFzBMwBX)D2cj0&11m(+k_Q^*#`4}$>CQ$OG3Wj>$C?35mey*F2 zUm(hko;lJAmY3)L@~N5Y>U8H&z@(`=Xgq@G*1+gjEGANM3F&EKQZSv)RBU4Pn%5=iMy9QSf6>Mm2{Vl0Z@wk? zWKloe<-l%E5sLIU4qHbcrW#G#mtsPreyVfdLV;5}{^yFE705WnTu#7Veqs4}ZNdv0 zVT>cvJ_<5M!bFBzQw(k>Xg1%K-F?RrkX=U4DaH#rNTy^IiYf$#$FRtgbS= z-mkh;G6YLuRkPQ};(;xH)Fe;~yGf7ukn3bC@eK z>}g`0y172xkqtd9M?5Y%^PZ4Jk2U%<@i#WQ5Mv@l8bU@q3s9Q7z>@V~81J9)=q`ZC z1gheUvVvTn(-lrUC?;8!B2vZgf>2ne;=CYYleBap!PC6fXWqUu0-Y19Fw zCjT~h7(@^FUtSvLp6|I=jvs+;a{0pSzaK;y0CAY3QB81Bgz~ft@!7vVSxIm0%gL$~ z$tgTWArEgDzhjVy(vEl(U%@Zcd!3UWH9kc_Tc>v)DLPS*=8^gyN!ez8eY~=S9Y>E* z4=X+*R!z=dyY1)tF}BipMwhn{@#_vHqTY1}Km+;MI8PokD_~+^QxX}9prsdeFCU~5 z%qpg^JUN$6!14QHx3Y-PkzSu5deq%#{%RyWE8wMUT^uG1`|62LJVRPTwHD~(rH$>E z$LP^*>i#^ta2ith*f+Z7^uH#HzkZOWKyPY^YJF#j-mVM146xsz8zG|XvLEWEQK3(N zw@B%;y{+TF)ZV@0ZlMLq2jv8TlhczkI;Y$M;h)ppG+5*AVnTZkY<@$r4)jVZgYmu* zB+ownfTeKrg%EFUj@si-E(gE$Fa$~@w)gUudr&tu$ZgGLYGCrMhA!lfcRWXeu~f!` zXVLB3;_f15QNf;WfB0m$5;k4Gje5_Vz1S!U@(D!bX~PKb{{D(Gg`6~2gpt~kbmWu# z?DHy}wBz_Li9fkU2f9{y_zJ__hUBN3Kbkv1Pvw6ib26hTUZ&_l8 z4koUC`zssXh1bggk2Kpk`QDc=X{uGB-wFOB6vmb&RZc=HdbE!8M_+_r3Q{emGZ zV?uQO?%6McWokHSmTR6+?O*KdtxQaqKfiyfZS)@0=C%-2wPo@4^fh-}sP5B(Me`&b z0d2tTy{{_5@M+Tx18Wlt4Etu7XssC9MQ!|*+wN9Wk}_!vY;J?7C+VZj;TFQ$S0sT;} z(B*;s{$An*+hD$ehXxlc3u(DDl|R{6e1kZ_(-c&k^WF#91WrQ&BGt0 zWP-727E$IfWrbhm-|{vE$n{|T86Ud z%Oj`>!K;~;KMJeaMdzAD>yxgIhVC`NcR4BZKLMGL!D~MU3E&v?I_Zr$rI|IVIhMOj z+SD?Rbl@c)?nz*w1qxkMmH1sDDF+U%mZXBX5u-0}E?x>e%jHCgU|!aRiRPH%{xw44 z_-}+H?&0!};5IP;@K4yJ!X#n!!^PZ*N#cjGi@BJ&slA!`-&6n0w^^B4{wHE$<@zUJ zV&(kzc!^jQnUSbtztqs(ruY+Rmjtf=!1M#TaXy*RU8S zDypMf|Hz=cA<)7!q#j-yj#{?+O=u#$j3gxon*9uEFUKWD~w98k=lWF%odrS(vF=0|^qn*Z_HLrWbU3h`Uw3y zEY}{Q5TN)NdzB0*@9T7$XIcJf&xsoON?20uTI0Z?05gZ?MHOJ%gevP0@&O?(koM&U z4!2yVX7>#zanDBn>v!oWm(MUn1z>p44NZPV=Gj^zqNr@TTg(vB+627^RI1r2ibR zDpoeKD9&k?+OWGo6QRJ56Fl7*(Ky0&K?;zw#dTQ({Gib{sd{6^n{03yrCc4ZA{l5- z0|mU_d_H=!V&7r(W{Y&Kl#=jrFi|O;Uk64kspfOECQ(N6v%ZMeq8eCSB;=L>%Qr@t zCvU23`Wlw@xTEmS6lkx^E^$cxX~AIl*Y5TqpVA~-%_{NOUV&Ft@H4&qo5DiIxvRCo zj3s#S9W+5I(usyPg~YN?wQI;wv9~9se)$3uLKZN|S?@oI*SwZu`cf!HYC!IMMg2-$ z0aHt7PVCW=Lcup1E^arNK*)q_Sj3#ugdy`nfYd_4Jq9Tr5}A@rK$)=E!R zY}95hAry{yiL~j0azyi@pl4#6HwoJSxgVI^V=BOCEj95i{R&l2TaF)4KJp%;$N7I4 zz$Tl``Zg}NFh>)FiG3c#9XEP86&8u`Oi#ddW<_yQy3T#xBBU4^=zBPC`~n>}_`xx~ zmsQOkQkta)-CB?NTkIAYxl2)<6`ZyVIwdxQ8u(}$&<`d?VP`#vSt>BH(Jm@Il|`b_ILRRO zA5ByZfj`wh2AV@B0yecQMp1~X?p@H;6EIm%bEAw3^9%B_y(MQPC1OK#4}tDHJLGC_ zO!DavQ>0iS7T0FES!wrq_CZe$mBI?sHvx_(xP6ke8NZ}1n%3))*b(aqEFK?c?#bh} zd&w5`d;-s{6MIh)l3_{m70@V*t;d0PMJrwVGZ)Y$#(6lmRv)!I=}S1?^cj}A=x{1z z#dcNr9rVXoEWFgj`tIKo#Fv5Uzkj4PlqMA^gdgio#$_iV`Xav2t(WpK)#S-yC{(Jz zn-n5mY(xQ%McJ9x;|Z?xaSo8;+=OcIiI#EKzs?*O(z`RlJkvr17zl0^PpB?U@>ggs zIUHifjZDO_30Dnsc1unZ@*u)wZP=9^5ccZoNz(55HQhkDg+eHI-Kmj~4`1TXZVROq zJN6808(kh%2Xht3_n<4!B-h77;eOT6ncE{WCD#*k^KvQ=8uN+U-_)Wn`_o`$FVxs6 zJ;tVdzzH~j&f{5=#&f|+w-~Y0Gu&?0hF_|y7Q-W@5r=RUR~NPMnnx?%kp)ev`1PPo zI%x}w_CusSr@lg9p$xGN6w29ZH4?frsDGo51T@aI`c2#sk)|g{k(GIsof&nime93{ z-|lu44&OFxCr}Dr2$L(uDpwflRhWj->&p5%@;YU@uKp+I+d^%dhghJYjFLNaD zg(!!}(7E@i5Xk*-rq`7}GF^SJER&281e-Mv4$Z9;zFTCN!q`wbWu@+ zFJf5SBHwcwZ<$4aK1nq zAwm@sBEhBcw)k3o{sEaaO(0kXX5lfV0Xuc;+A`nka8}@z8?;9#%SSs2LsF_Tps>-{ z@lsaZ&F)2NNBmZ&nPk2(y6Vge8-4EywVA>lgv~M%&#bB6+aU4A&p!m59K>&NP2{VH z?rQgeD#ENzT7T5x)u77a3EQs;mI4}&qH#x57+7VqJ?{A=wVlOhK|t~S-h*IEVmWEO zCO#!S?nO%NvWs&yPoO&@wN6s)YRx)SA-m1mMGb)d#~-MgC(?F-b0T zQYk*IS zJO=~6TA=XedrSTi@q~)$PN03g9*0bHtR(l*OfY{9FYeQ=XHrW(Vzat9t{USb z`YRwe`!IZvmj?VaGAqx%xHt<>^DcXD!-l?E!iV%qYyV=7Bz8WE0kHg*Mw+@C#P*zV zz$#;?PKg5RM1GrW{*iv+o&mU%SxjRq6g<}l_(yHIY zKPRSw?f~pTgDHq?V_UTiTe9kg+eutW3XWdRZZ1>N6O>&qB9y?OOkY-iQ5R(0<%-dfa`76l3EX~80I6aq5F1cTeI zrII*>X2?Iq-xy85O(0^W#XLoFK?fTs4tXK2_fpXJ`&8&r_(9B0egtL__o28=YgLyZ zgg3>z>JaX6zTwp_fF2VH5rYQee!*QJ1$vC|r1S2<_5aoWukt@-WhNq%i{eD9cjT$H ziq#XD-&_-cGu$|mOs)nm^J;3K;mFC+S-u9wlMLI79*l`%sH=Bg=vA*y8{VV#5|gWb(a0qk?2#2)W*{Lh6RUV zly@2G33^`zGo~LF=Se(}RKCZ;GP?Pf-;TPP*!ZT0D1=l#0_ukYQ3=G?4+Yf|@TVrv z2=qc2MXRkcD+Rtc%TNr4Uw2aH)XVxVCI{ZFzSCw_N{s`FAJg{CW?2$|jL>2_pL5(jY$3@5%a zDC3I;I1su!(q{1_1U*HWCU_F68wnTx`ZaX0fpeGi|LQv-Re|2JXC~=i~-9vk;Y%isW>dO}W zXv^Ma9jW!)7n==a1UGF0dfwe3TVMsfwMku&>TmvbOi}w)5gYic-3Y4#Hr*EPOhiQ- zc!R`4r-83M0qNz4C+O1{sML_4W+o2eG$fg9VSF@&Pw`jvE-dpis#8+rGT6m@da2<| zjIaWTMlA{jORs`A4dg@8S)e4mRWEGxJy}JrsX_~$AGZSe9(I1EHmRr&EcV!7d9|nH zEwq8D)`2q(X;3e?R7z@QELeEbFE%Zy)_av-s zZVV%2HhNJ~yPqYIS*c9wPWS?21oAzkhJtY}q%(!=X_ZGeI_`?v&zY7QQ_Olc27N&m zbX42+;u%M(l3vu(x(>g$`g6O^iSQma?TD)Cptksx??^SnblodzT?LY?v99Rw?>a(c z-fVN)38pe2g``NEZ$l3KIZdSLOI)7AU=vWG935VX;;Q5kMYY)7 z6ag*@+F`TSYK!_42(liGA!$GQ{2K0suC5YOg^7Z5Wnm01$BbBNdQ38%A_m`Upl^gN z77gjXB}eW(X61A5X7@_*riMfCjwZFwDEM z$H%kMP@_Q+87JGutp)t@g(-=M1~wcs`=hs*afQ{rrx+*t@;5yUuaC#Zi?oW9{iT|EW3YcP5^ffxk5T&fo`QCQw^`elTCtCMD}a4fpiWbR`R z{MQuz`2bG$ID=UgR^r$sQR#*xpc;ALk=K(-lwqcCp^;MIw};SD*si&UaGc2;TvCy# z6p4MvExh%an*UT9d*zJBns<3&%T;SAJCdx-yb8Nm9w|MR77tn^nZom{ zT6*Q`jHYxt{x}UH@|*zvc+JEhu|9A1W|G*`V|LGalzpHS6*W&)Y zXR3sAjl6jgRob{6<_ol3UiE@t&YoLEGz&4c^F}{es2Vzc$ zDCzMLeyUF&918++uyCNdkoJBGPf$n|b0WP%%xhi5>PP7KkES)9;@{)}F_UkMd$)ky zoL#|pR^zlTXL4a5?DH>G=fOuFMb<&#+ScPF^IUHhvw?3aP-b)#7|#VR8+31$I(CTTkh`@c8(4~P8Ugo=%ojY-Ac{+|c<=Mz(5Qnm6j|9imk zpAn*^Vs7zweG5R$N(|8dFR%K(eVIH-7ov5<{i?lCL%RM&tmFLb%t#yD8vl2KQHe-@1*C3duJ8? zWi91=hy4c%fck+EPW_ug^IX<|XMpR^5K@gXN7~S1?k@CxNK2&L=zd~3TFLfr%@Xsq z39s~6T|vF+rwYb0l-vS^rWiYO<*P)F9FD(Mc~J{hzNahxtzKQ={LmRE>LTz1xeICT zQl6f?S^2bPxsSaJt%la6>3S?neD+k%M*6k$es_s^9w*C8F<)TBH|Bb;?y0g6`lvi# z+;|a_y_IKrD)~JM-M7dFTz^W|_TtyDjJ|1pvOd*WNpDToYn`0oC4c$VZn9Mi+zPoo zWL;%uk>75suB%(fb!i_SKhk`%=q0X2tLXX1MG>5b%}7j?Iz_$JlJ`7853VZLy;R51 zc9+)lXrzx7?P(U}nvw`Bd~wcAi6_QMhzWt^DeJ`3?#YP^Oy3s9*C3J^<<1z-GHNv84e=-uZsJ3vjky4CBw_;+jG+C4wLvGf1+Itj50#S7#SvCl?PVa|;Am R0DuDlmV!cDQ33(>e*xT`0@(ln diff --git a/api-tests/docs/porting_guide_dev_apis.md b/api-tests/docs/porting_guide_dev_apis.md index 55e536ff..83491235 100644 --- a/api-tests/docs/porting_guide_dev_apis.md +++ b/api-tests/docs/porting_guide_dev_apis.md @@ -1,21 +1,19 @@ -# Porting Guide - Developer APIs Architecture Test Suite +# Porting Guide: Developer APIs Architecture Test Suite ----------------------------------------------------- ## Introduction -The 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. +The architecture test suite contains the *Platform Abstraction Layer* (PAL) which abstracts platform-specific information from the tests. You must implement and port the PAL interface functions to your target platform. Create and update the target configuration file to match the details of this target platform. -This document provides details on the porting steps and the PAL APIs. +This document provides the porting steps and the list of 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 api-tests/platform/targets//.
+You must populate your system configuration and provide it as an input to the test suite. The system configuration is captured in a single static input configuration file called **target.cfg**, available at **api-tests/platform/targets//**.
-An example of the input configuration file is as shown. +An example input configuration file is as shown. // UART device info uart.num=1; @@ -31,42 +29,42 @@ An example of the input configuration file is as shown. watchdog.0.intr_id = 0xFF; watchdog.0.permission = TYPE_READ_WRITE; - More details on the structure of the input can be obtained from val/common/val_target.h. + For details on the structure of the input, refer to **val/common/val_target.h**. ### Adding a new target - - Create a new directory in platform/targets/. For reference, see the existing platform tgt_dev_apis_mbedos_fvp_mps2_m4 directory. - - cp -rf platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/ platform/targets// - - Update platform/targets//target.cfg with your platform detail. Refer val/common/val_target.h for structure details. - - Update platform/targets//Makefile appropriately to select correct instances of PAL files for compilation. To compile dev_apis suites, you must set PSA_IPC_IMPLEMENTED to 0. This selects the non-secure PAL instances for the driver services and eliminates IPC dependency for dev_apis tests. - - Refer "PAL API list" section to view list of PAL APIs that must be ported for your target platform. These APIs definitions are available in nspe//pal_\*\_intf.c. These APIs are written for tgt_dev_apis_mbedos_fvp_mps2_m4 platform. You can reuse the code if it works for your platform. Otherwise you must port them for your platform specific peripherals. - - Update Crypto configuration file - nspe/crypto/pal_crypto_config.h to enable/disable crypto features selectively for crypto test suite - - The platform make file is invoked as part of test suite build tool(./setup.sh) step and it creates /BUILD/platform/pal_nspe.a archive. + 1. Create a new directory in **platform/targets/**. For reference, see the existing platform **tgt_dev_apis_mbedos_fvp_mps2_m4** directory. + 2. Execute `cp -rf platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/ platform/targets//`. + 3. Update **platform/targets//target.cfg** with your platform details. Refer to **val/common/val_target.h** for structure details. + 4. Update **platform/targets//Makefile** appropriately to select the correct instances of PAL files for compilation. To compile **dev_apis** suites, you must set **PSA_IPC_IMPLEMENTED** to 0. This selects the Non-secure PAL instances for the driver services and eliminates IPC dependency for dev_apis tests. + 5. Refer to the **List of PAL APIs** section to view the list of PAL APIs that must be ported for your target platform. These API definitions are available in **nspe//pal_\*\_intf.c**. These APIs are written for tgt_dev_apis_mbedos_fvp_mps2_m4 platform. You can reuse the code if it works for your platform. Otherwise, you must port them for your platform-specific peripherals. + 6. Update Crypto configuration file **nspe/crypto/pal_crypto_config.h** to enable or disable Crypto features selectively for the Crypto test suite. **Note**: -Test suite needs access to the following peripherals:
- - One UART to print nspe and spe messages - - One Watchdog timer to help recovery from any fatal error conditions +- The platform makefile is invoked as part of test suite build tool (**./setup.sh**) step and it creates **/BUILD/platform/pal_nspe.a** archive. +- The test suite requires access the following peripherals: + - One UART to print NSPE and SPE messages + - One Watchdog timer to help recover from any fatal error conditions - Non-volatile memory support to preserve test status over watchdog timer reset -## PAL API list -Since test suite is agnostic to various system targets, before building the tests, you must port below PAL NSPE APIs. These functions will require implementation for your target platform.
+## List of PAL APIs +Since the test suite is agnostic to various system targets, you must port the following PAL NSPE APIs before building the tests. Implement these functions for your target platform.
| 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 | int pal_uart_init_ns(uint32_t uart_base_addr); | This function initializes the UART | uart base addr
| -| 03 | int pal_print_ns(char *str, uint32_t data); | This function parses the input string and writes bytes into UART TX FIFO| str : Input String
data : Value for format specifier
| -| 04 | int pal_wd_timer_init_ns(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
| +| 01 | int pal_spi_read(addr_t addr, uint8_t *data, uint32_t len); | Reads peripherals using SPI commands | addr : Address of the peripheral
data : Read buffer
len : Length of the read buffer in bytes
| +| 02 | int pal_uart_init_ns(uint32_t uart_base_addr); | Initializes the UART | UART base address
| +| 03 | int pal_print_ns(char *str, int32_t data); | Parses the input string and writes bytes into UART TX FIFO| str : Input String
data : Value for format specifier
| +| 04 | int pal_wd_timer_init_ns(addr_t base_addr, uint32_t time_us, uint32_t timer_tick_us); | Initializes a 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
| | 05 | int pal_wd_timer_enable_ns(addr_t base_addr); | Enables a hardware watchdog timer | base_addr : Base address of the watchdog module
| | 06 | int pal_wd_timer_disable_ns(addr_t base_addr); | Disables a hardware watchdog timer | base_addr : Base address of the watchdog module
| -| 07 | int pal_nvmem_read_ns(addr_t base, uint32_t offset, void *buffer, int size); | Reads from given non-volatile address. | base : Base address of nvmem
offset : Offset
buffer : Pointer to source address
size : Number of bytes
| +| 07 | int pal_nvmem_read_ns(addr_t base, uint32_t offset, void *buffer, int size); | Reads from the given non-volatile address. | base : Base address of nvmem
offset : Offset
buffer : Pointer to source address
size : Number of bytes
| | 08 | int pal_nvmem_write_ns(addr_t base, uint32_t offset, void *buffer, int size); | Writes into given non-volatile address. | base : Base address of nvmem
offset : Offset
buffer : Pointer to source address
size : Number of bytes
| -| 09 | int32_t pal_crypto_function(int type, va_list valist); | This API will call the requested crypto function | type : function code
valist : variable argument list
| -| 10 | uint32_t pal_its_function(int type, va_list valist); | This API will call the requested internal trusted storage function | type : function code
valist : variable argument list
| -| 11 | uint32_t pal_ps_function(int type, va_list valist); | This API will call the requested protected storage function | type : function code
valist : variable argument list
| -| 12 | int32_t pal_attestation_function(int type, va_list valist); | This API will call the requested initial attestation function | type : function code
valist : variable argument list
| +| 09 | int32_t pal_crypto_function(int type, va_list valist); | Calls the requested Crypto function | type : Function code
valist : variable argument list
| +| 10 | uint32_t pal_its_function(int type, va_list valist); | Calls the requested Internal Trusted Storage function | type : Function code
valist : Variable argument list
| +| 11 | uint32_t pal_ps_function(int type, va_list valist); | Calls the requested Protected Storage function | type : Function code
valist : Variable argument list
| +| 12 | int32_t pal_attestation_function(int type, va_list valist); | Calls the requested Initial Attestation function | type : Function code
valist : Variable argument list
| ## License Arm PSA test suite is distributed under Apache v2.0 License. diff --git a/api-tests/docs/porting_guide_ff.md b/api-tests/docs/porting_guide_ff.md index 7f6f9f05..adceb371 100644 --- a/api-tests/docs/porting_guide_ff.md +++ b/api-tests/docs/porting_guide_ff.md @@ -1,21 +1,19 @@ -# Porting Guide - PSA-FF Architecture Test Suite +# Porting Guide: PSA-FF Architecture Test Suite ----------------------------------------------------- ## Introduction -The 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. +The architecture test suite contains a *Platform Abstraction Layer* (PAL) which abstracts platform-specific information from the tests. You must implement and port the PAL interface functions to your target platform. Create and update the target configuration file to match the details of this target platform. -This document provides details on the porting steps and the PAL APIs. +This document provides the porting steps and the list of 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 api-tests/platform/targets//.
+You must populate your system configuration and provide it as an input to test suite. The system configuration is captured in a single static input configuration file called **target.cfg**, available at **api-tests/platform/targets//**.
-An example of the input configuration file is as shown. +An example input configuration file is as shown. // UART device info uart.num=1; @@ -31,50 +29,52 @@ An example of the input configuration file is as shown. watchdog.0.intr_id = 0xFF; watchdog.0.permission = TYPE_READ_WRITE; - More details on the structure of the input can be obtained from val/common/val_target.h. + For details on the structure of the input, refer to **val/common/val_target.h**. ### Adding a new target - - Create a new directory in platform/targets/. For reference, see the existing platform tgt_ff_mbedos_fvp_mps2_m4 directory. - - cp -rf platform/targets/tgt_ff_mbedos_fvp_mps2_m4/ platform/targets// - - Update platform/targets//target.cfg with your platform detail. Refer val/common/val_target.h for structure details. - - Update the platform information available in manifest files located in platform/targets//manifests/ directory with your platform information. The platform detail must match with device detail provided in the target.cfg file. - - Update platform/targets//Makefile appropriately to select correct instances of PAL files for compilation. To compile IPC tests, you must set PSA_IPC_IMPLEMENTED to 1 and remaining Developer APIs related variables to 0. This selects the secure PAL instances for the driver services and eliminates dev_apis dependency for IPC tests. - - Refer "PAL API list" section to view list of PAL APIs 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 tgt_ff_mbedos_fvp_mps2_m4 platform. You can reuse the code if it works for your platform. Otherwise you must port them for your platform specific peripherals. - - The platform make file is invoked as part of test suite build tool(./setup.sh) step and it creates /BUILD/platform/pal_nspe.a archive for NPSE files and respective object for SPE files at /BUILD/platform/spe/\*\_driver_sp.o. Later, these SPE objects are used by spbuild.mk to create appropriate SPE partition archive file. + 1. Create a new directory in **platform/targets/**. For reference, see the existing platform tgt_ff_mbedos_fvp_mps2_m4 directory. + 2. Execute `cp -rf platform/targets/tgt_ff_mbedos_fvp_mps2_m4/ platform/targets//`. + 3. Update **platform/targets//target.cfg** with your target platform details. Refer to **val/common/val_target.h** for structure details. + 4. Update the platform information available in manifest files located in **platform/targets//manifests/** directory with your platform information. The platform details must match the device details provided in the target.cfg file. + 5. Update **platform/targets//Makefile** appropriately to select the correct instances of PAL files for compilation. To compile IPC tests, you must set PSA_IPC_IMPLEMENTED to 1 and the remaining Developer APIs related variables to 0. This selects the Secure PAL instances for the driver services and eliminates dev_apis dependency for IPC tests. + 6. Refer to the **List of PAL APIs** section to view the list of PAL APIs that must be ported for your target platform. These API definitions are available in **nspe//pal_\*\_intf.c** and **spe/pal_\*\_intf.c** files. These APIs are written for **tgt_ff_mbedos_fvp_mps2_m4** platform. You can reuse the code if it works for your platform. Otherwise, you must port them for your platform-specific peripherals. **Note**: - Test suite needs access to the following peripherals. When PSA_IPC_IMPLEMENTED is set to 1, driver functionalities are implemented as RoT-services in driver partition. Other Secure partitions and non-secure code calls to these RoT-services to get appropriate driver services. - - One UART to print nspe and spe messages - - One Watchdog timer to help recovery from any fatal error conditions +- The platform makefile is invoked as part of test suite build tool (**./setup.sh**) step and it creates **/BUILD/platform/pal_nspe.a** archive for NPSE files and respective object for SPE files at **/BUILD/platform/spe/\*\_driver_sp.o**. These SPE objects are used by **spbuild.mk** to create the appropriate SPE partition archive file. +- The test suite requires access to the peripherals mentioned below. When PSA_IPC_IMPLEMENTED is set to 1, driver functionalities are implemented as RoT-services in driver partition. Other Secure partitions and Non-secure code calls to these RoT-services to get appropriate driver services. + - One UART to print NSPE or SPE messages and to cover secure partition interrupt handling scenarios + - One Watchdog timer to help recover from any fatal error conditions - Non-volatile memory support to preserve test status over watchdog timer reset +## List of PAL APIs -## PAL API list -Since Test suite is agnostic to various system targets, before building the tests, you must port below PAL APIs. These functions will require implementation for your target platform.
+Since the test suite is agnostic to various system targets, you must port the following PAL NSPE APIs before building the tests. Implement these functions for your target platform.
-- Following are the list of PAL APIs used in NSPE:
+The following is 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
| +| 01 | int pal_spi_read(addr_t addr, uint8_t *data, uint32_t len); | Reads peripherals using SPI commands | addr : Address of the peripheral
data : Read buffer
len : Length of the read buffer in bytes
| -- Following are the list of PAL APIs used in SPE:
+The following is 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
| +| 01 | void pal_uart_init(addr_t uart_base_addr); | Initializes the UART | uart_base_addr : Base address of the UART
| +| 02 | void pal_print(char *str, int32_t data); | 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 a 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
| +| 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
| +| 09 | void pal_generate_interrupt(void); | Trigger interrupt for IRQ signal assigned to driver partition | None | +| 10 | void pal_disable_interrupt(void); | Disable the interrupt that was generated using pal_generate_interrupt API. | None | ## License Arm PSA test suite is distributed under Apache v2.0 License. -------------- -*Copyright (c) 2019, Arm Limited and Contributors. All rights reserved.* +*Copyright (c) 2018-2019, Arm Limited and Contributors. All rights reserved.* diff --git a/api-tests/docs/psa_attestation_testlist.md b/api-tests/docs/psa_attestation_testlist.md index d8d7f105..478b494e 100644 --- a/api-tests/docs/psa_attestation_testlist.md +++ b/api-tests/docs/psa_attestation_testlist.md @@ -4,7 +4,7 @@ |-----------|--------------------------------------|-------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | test_a001 | PSA_ATTEST_ERR_SUCCESS | psa_initial_attest_get_token()
psa_initial_attest_get_token_size() | 1. Provide correct inputs to API with described challenge sizes
2. Expect API to return this define as return value each time
3. Verify the token | 1. Challenge_size = 32
2. Challenge_size = 48
3. Challenge_size = 64 | | | PSA_ATTEST_ERR_INVALID_INPUT | psa_initial_attest_get_token()
psa_initial_attest_get_token_size() | 1. Provide described challenge sizes to the API along with other valid parameters
2. Expect API to return this define as return value each time | 1. Challenge_size is zero
2. Invalid challenge size between 0 and 32
3. Invalid challenge size between 32 and 64
4. Challenge_size is greater than MAX_CHALLENGE_SIZE | -| | PSA_ATTEST_ERR_TOKEN_BUFFER_OVERFLOW | psa_initial_attest_get_token() | 1. Provide described taken size to the API along with other valid parameters
2. Expect API to return this define as return value each time | Pass the token_size which less than actual/required token size | +| | PSA_ATTEST_ERR_TOKEN_BUFFER_OVERFLOW | psa_initial_attest_get_token() | 1. Provide described taken size to the API along with other valid parameters
2. Expect API to return this define as return value each time | 1. Token_size as zero
2. Token_size less than challenge size | | | PSA_ATTEST_ERR_INIT_FAILED | psa_initial_attest_get_token()
psa_initial_attest_get_token_size() | Can't simulate. Test can't generate stimulus where attestation initialisation fails | | | | PSA_ATTEST_ERR_CLAIM_UNAVAILABLE | psa_initial_attest_get_token() | Can't simulate. Test can't generate stimulus where claim can unavailable | | | | PSA_ATTEST_ERR_GENERAL | psa_initial_attest_get_token()
psa_initial_attest_get_token_size() | Can't simulate. Test can't generate stimulus where unexpected error happened during API operation | | diff --git a/api-tests/docs/psa_crypto_testlist.md b/api-tests/docs/psa_crypto_testlist.md index 12def99d..983ba367 100644 --- a/api-tests/docs/psa_crypto_testlist.md +++ b/api-tests/docs/psa_crypto_testlist.md @@ -24,8 +24,8 @@ | | | | | | | 2. Incorrect key data size | | | | | | | | | | | | | | | | | -| | | | | PSA_ERROR_INVALID_HANDLE | Calling this function with invalid key handle should return this error | 1. Invalid key slot
2. Zero key slot | -| | | | | PSA_ERROR_OCCUPIED_SLOT | Pass the key slot to store data which is already occupied | Already occupied key slot | +| | | | | PSA_ERROR_INVALID_HANDLE | Calling this function with invalid key handle should return this error | 1. Destroyed key handle
2. Zero as key handle
3. Unallocated key handle | +| | | | | PSA_ERROR_ALREADY_EXISTS | 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 | 1. 16 Byte AES | | | | | | | 2. Initialize a key policy structure to a default that forbids all usage of the key | 2. 24 Byte AES | | | | | | | | | @@ -38,10 +38,12 @@ | | | | | | | 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_NOT_PERMITTED | Calling this function with with key policy as verify should return this error | Key policy as PSA_KEY_USAGE_VERIFY | | | | | | PSA_ERROR_INVALID_HANDLE | Calling this function with invalid parameter should return this error | 1. Zero key slot | -| | | | | | | 2. Invalid key slot | +| | | | | | | 2. Unallocated key slot | +| | | | | | | 3. Destroyed 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 | +| | | | | PSA_ERROR_DOES_NOT_EXIST | 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 | 1. 2048 RSA public key | | | | | | | 2. Initialize a key policy structure to a default that forbids all usage of the key | 2. 2048 RSA keypair | | | | | | | 3. Allocate a key slot for a transient key and set the standard fields of a policy structure | 3. EC Public key | @@ -50,18 +52,16 @@ | | | | | | 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 | | -| | | | | PSA_ERROR_INVALID_ARGUMENT | 1. Initialize the PSA crypto library | 1. 16 Byte AES | -| | | | | | 2. Initialize a key policy structure to a default that forbids all usage of the key | 2. 24 Byte AES | -| | | | | | 3. Allocate a key slot for a transient key and set the standard fields of a policy structure | 3. 32 Byte AES | -| | | | | | 4. Set the usage policy on a key slot | 4. DES 64 bit key | -| | | | | | 5. Import the key data into the key slot | 5. Triple DES 2-Key | -| | | | | | 6. Get basic metadata about a key | 6. Triple DES 3-Key | -| | | | | | 7. Export a key in binary format | | -| | | | | | 8. Check if original key data matches with the exported data | | | | | | | PSA_ERROR_BUFFER_TOO_SMALL | Calling this function with buffer size less than required | Less buffer size | -| | | | | PSA_ERROR_INVALID_HANDLE | Calling this function with invalid parameter should return this error | 1. Zero key slot | -| | | | | | | 2. Invalid key slot | +| | | | | PSA_ERROR_INVALID_ARGUMENT | Calling this function with invalid parameter should return this error | 1. DES 64 bit key | +| | | | | | | 2. Triple DES 2-Key | +| | | | | | | 3. Triple DES 3-Key | +| | | | | PSA_ERROR_NOT_PERMITTED | Calling this function with with key policy as verify should return this error | Key policy as PSA_KEY_USAGE_VERIFY | +| | | | | PSA_ERROR_INVALID_HANDLE | Calling this function with invalid key handle should return this error | 1. Zero key slot | +| | | | | | | 2. Unallocated key slot | +| | | | | | | 3. Destroyed key slot | | | | | | PSA_ERROR_BAD_STATE | Calling this function with key policy that cannot be exported | Invalid key policy usage | +| | | | | PSA_ERROR_DOES_NOT_EXIST | Calling this function with empty key slot | Empty key slot | | | test_c005 | psa_destroy_key | Destroy a key and restore the slot to its default state. | PSA_SUCCESS | 1. Initialize the PSA crypto library | 1. 16 Byte AES | | | | | | | 2. Initialize a key policy structure to a default that forbids all usage of the key | 2. 24 Byte AES | | | | | | | 3. Allocate a key slot for a transient key and set the standard fields of a policy structure | 3. 32 Byte AES | @@ -86,8 +86,9 @@ | | | | | | | 9. EC Public key | | | | | | | | 10. EC keypair | | | | | | PSA_ERROR_INVALID_HANDLE | 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 | +| | | | | | | 2. Unallocated key slot | +| | | | | | | 2. Destroyed key slot | +| | | | | PSA_ERROR_DOES_NOT_EXIST | 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 | 1. 16 Byte AES | @@ -100,10 +101,10 @@ | | | | | | | 8. Triple DES 3-Key | | | | | | | | 9. EC Public key | | | | | | | | 10. EC keypair | -| | | | | PSA_ERROR_INVALID_HANDLE | Calling this function with invalid parameter should return this error | 1. Invalid key policy | +| | | | | PSA_ERROR_INVALID_HANDLE | Calling this function with invalid parameter should return this error | 1. Unallocated key slot | | | | | | | | 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 | +| | | | | | | 3. Destroyed key slot | +| | | | | PSA_ERROR_ALREADY_EXISTS | 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 | 1. 16 Byte AES | | | | | | | 2. Initialize a key policy structure to a default that forbids all usage of the key | 2. 24 Byte AES | | | | | | | 3. Allocate a key slot for a transient key and set the standard fields of a policy structure | 3. 32 Byte AES | @@ -115,7 +116,7 @@ | | | | | | 9. Retrieve the algorithm field of a policy structure | 9. EC Public key | | | | | | | 10. Make sure they match the original value | 10. EC keypair | | | | | | PSA_ERROR_INVALID_HANDLE | Calling this function with invalid parameter should return this error | 1. Zero key slot | -| | | | | | | 2. Invalid key slot | +| | | | | | | 2. Destroyed key slot | | | test_c009 | psa_allocate_key | Allocate a key slot for a transient key | PSA_SUCCESS | 1. Initialize the PSA crypto library | 1. Volatile keys | | | | | | | 2. Initialize a key policy structure to a default that forbids all usage of the key | | | | | | | | 3. Allocate a key slot for a transient key | | @@ -127,7 +128,7 @@ | | | | | PSA_ERROR_INSUFFICIENT_MEMORY | Calling this function with multiple time | | | | | | | | | | | | | | | | | | -| | test_c010 | psa_get_key_lifetime | Retrieve the lifetime of a key slot. | PSA_SUCCESS | 1. Initialize the PSA crypto library | Testing only volatile keys as other key types are currently not supported | +| | test_c010 | psa_get_key_lifetime | Retrieve the lifetime of a key slot. | PSA_SUCCESS | 1. Initialize the PSA crypto library | Testing only volatile keys and persistance key types will be supported in future release | | | | | | | 2. Initialize a key policy structure to a default that forbids all usage of the key | | | | | | | | 3. Allocate a key slot for a transient key and set the standard fields of a policy structure | | | | | | | | 4. Set the usage policy on a key slot | | @@ -136,7 +137,6 @@ | | | | | | 7. Get the lifetime of a key slot | | | | | | | PSA_ERROR_INVALID_HANDLE | | 1. Zero key slot | | | | | | | | 2. Invalid key slot | -| | | | | | | 3. Empty key slot | | | | | | PSA_ERROR_INVALID_ARGUMENT | | 1. Invalid key policy | | Message Authentication Codes | test_c011 | psa_hash_start | Start a multipart hash operation. | PSA_SUCCESS | 1. Initialize the PSA crypto library | 1. MD2 | | | | | | | 2. Start a multipart hash operation | 2. MD4 | @@ -162,8 +162,8 @@ | | | | | | | 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 | +| | | | | PSA_ERROR_BAD_STATE | 1. Calling this function without calling the psa_hash_start() should return error | Inactive operation handle | +| | | | | | 2. 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 | 1. MD2 | | | | | | | 2. Start a multipart hash operation | 2. MD4 | | | | | | | 3. Add a message fragment to a multipart hash operation | 3. MD5 | @@ -173,7 +173,8 @@ | | | | | | | 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_BAD_STATE | Calling this function with inactive operation handle should return error | 1. Inactive operation handle | +| | | | | | Calling this function with invalid operation handle should return error | 2. Invalid 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 | 1. MD2 | @@ -206,15 +207,16 @@ | | | | | | 7. Check if key type and key length matches | | | | | | | | 8. Export a key in binary format | | | | | | | | 9. Check if the metadata matches | | -| | | | | PSA_ERROR_INVALID_HANDLE | Calling this function with invalid key slot should return this error | Invalid key slot | +| | | | | PSA_ERROR_INVALID_HANDLE | Calling this function with unallocated key slot should return this error | Unallocated key slot | | | | | | PSA_ERROR_INVALID_HANDLE | Calling this function with zero as key slot should return this error | Zero as key slot | +| | | | | PSA_ERROR_INVALID_HANDLE | Calling this function with destroyed key slot should return this error | Destroyed as key slot | | | | | | PSA_ERROR_INVALID_ARGUMENT | Calling this function with Null extra and Non-Zero extra size should return this error | Null extra and Non-Zero extra size | -| | | | | PSA_ERROR_OCCUPIED_SLOT | Calling this function with pre-occupied key slot should return this error | Pre-occupied key slot | +| | | | | PSA_ERROR_ALREADY_EXISTS | Calling this function with pre-occupied key slot should return this error | Pre-occupied key slot | | | | | | PSA_ERROR_NOT_SUPPORTED | Calling this function to generate only public key should return this error | Key type as public key | | | test_c017 | psa_generate_random | Generate random bytes | PSA_SUCCESS | 1. Initialize the PSA crypto library | 1. 16 Byte data | -| | | | | | 2. Generate random bytes | 2. 24 Byte data | -| | | | | | 3. Check that if generated data are Non-Zero | 3. 32 Byte data | -| | | | | | | 4. 64 Byte data | +| | | | | | 2. Generate random bytes Run several times, to ensure that every output byte will be nonzero at least once | 2. 24 Byte data | +| | | | | | 3. Check that no more than bytes have been overwritten | 3. 32 Byte data | +| | | | | | 4. Check that every byte was changed to nonzero at least once. | 4. 64 Byte data | | | | | | | | 5. 128 Byte data | | | | | | | | 6. 256 Byte data | | | | | | | | 7. 512 Byte data | @@ -231,9 +233,10 @@ | | | | | | 9. Generate random bytes for remaining capacity | 9. Request maximum capacity | | | | | | | 10. Check that if generated data are non-zero | | | | | | | | 11. Generate random bytes and check that it fails | | -| | | | | PSA_ERROR_INSUFFICIENT_CAPACITY | Calling this function with output size greater than the current capacity should return this error | output size greater than the current capacity | -| | | | | PSA_ERROR_INSUFFICIENT_CAPACITY | Calling this function with capacity greater than the allowed capacity should return this error | request maximum capacity +1 | -| | test_c019 | psa_generator_get_capacity | Retrieve the current capacity of a generator | PSA_SUCCESS | 1. Initialize the PSA crypto library | 1. Output size less than generator capacity | +| | | | | PSA_ERROR_INSUFFICIENT_DATA | Calling this function with output size greater than the current capacity should return this error | Output size greater than the current capacity | +| | | | | PSA_ERROR_INSUFFICIENT_DATA | Calling this function with capacity greater than the allowed capacity should return this error | Request maximum capacity +1 | +| | | | | PSA_ERROR_BAD_STATE | Calling this function without setup should return this error | | +| | test_c019 | psa_get_generator_capacity | Retrieve the current capacity of a generator | PSA_SUCCESS | 1. Initialize the PSA crypto library | 1. Output size less than generator capacity | | | | | | | 2. Initialize a key policy structure | 2. Output size equal to generator capacity | | | | | | | 3. Allocate a key slot for a transient key and set the standard fields of a policy structure | | | | | | | | 4. Set the usage policy on a key slot | | @@ -244,6 +247,7 @@ | | | | | | 9. Generate random bytes | | | | | | | | 10. Retrieve the current capacity of a generator | | | | | | | | 11. Check that it is equal to the remaining capacity | | +| | | | | PSA_ERROR_BAD_STATE | Calling this function without setup should return this error | | | | test_c020 | psa_generator_import_key | Create a symmetric key from data read from a generator | PSA_SUCCESS | 1. Initialize the PSA crypto library | 1. 16 Byte AES | | | | | | | 2. Initialize a key policy structure | 2. 32 Byte AES | | | | | | | 3. Allocate a key slot for a transient key and set the standard fields of a policy structure | | @@ -269,12 +273,12 @@ | | | | | | 23. Set the usage policy on a new key slot | | | | | | | | 24. Create a symmetric key from data read from a generator for the some size | | | | | | | | Check that it fails | | -| | | | | PSA_ERROR_INSUFFICIENT_CAPACITY | Calling this function with output greater than capacity should return this error | Output greater than capacity | +| | | | | PSA_ERROR_INSUFFICIENT_DATA | Calling this function with output greater than capacity should return this error | Output greater than capacity | | | | | | PSA_ERROR_INVALID_ARGUMENT | Calling this function with public key algorithm should return this error | 1. RSA public key
2.Invalid key size | | | | | | PSA_ERROR_INVALID_HANDLE | Calling this function with invalid arguments should return this error | 1. Invalid key slot | | | | | | | | 2. Zero as key slot | | | | | | | | | -| | | | | PSA_ERROR_OCCUPIED_SLOT | Calling this function with already occupied key slot should return this error | Pre-occupied key slot | +| | | | | PSA_ERROR_ALREADY_EXISTS | Calling this function with already occupied key slot should return this error | Pre-occupied key slot | | | test_c021 | psa_generator_abort | Abort a generator | PSA_SUCCESS | 1. Initialize the PSA crypto library | 1. Abort | | | | | | | 2. Initialize a key policy structure | 2. Multiple | | | | | | | 3. Allocate a key slot for a transient key and set the standard fields of a policy structure | 3. Calling generator functions after abort should fail | @@ -290,10 +294,13 @@ | | | | | | 5. Set up a key derivation operation | | | | | | | | 6. Retrieve the current capacity of a generator | | | | | | | | 7. Make sure that the capacity is same as input capacity | | -| | | | | PSA_INVALID_ARGUMENT | Calling this function with invalid argument should return this error | 1. Invalid algorithm 2. Unsupported generator capacity | +| | | | | PSA_INVALID_ARGUMENT | Calling this function with invalid argument should return this error | 1. Invalid algorithm | +| | | | | | | 2. Unsupported generator capacity | | | | | | | | 3. Unsupported key type | +| | | | | PSA_ERROR_NOT_PERMITTED | Calling this function with incorrect usage should return this error | 1. Incorrect usage | +| | | | | PSA_ERROR_NOT_SUPPORTED | Calling this function with unsupported key derivation algorithm should return this error | 1. Unsupported key derivation algorithm | | | | | | PSA_ERROR_INVALID_HANDLE | Calling this functoin wih incorrect key handle | 1. Invalid key handle
2. Zero as key slot | -| | | | | PSA_ERROR_EMPTY_SLOT | Calling this function with empty key slot should return this error | Empty key slot | +| | | | | PSA_ERROR_DOES_NOT_EXIST | Calling this function with empty key slot should return this error | Empty key slot | | Key policies | test_c023 | psa_key_policy_get_usage | Retrieve the usage field of a policy structure | PSA_SUCCESS | 1. Initialize the PSA crypto library | 1. Encrypt | | | | | | | 2. Initialize a key policy structure | 2. Decrypt | | | | | | | 3. Allocate a key slot for a transient key and set the standard fields of a policy structure | 3. Export | @@ -310,7 +317,7 @@ | | | | | | 8. Check if the cipher text is expected length | | | | | | | PSA_ERROR_NOT_SUPPORTED | | 1. DES key | | | | | | | | 2. Unsupported algorithm | -| | | | | PSA_ERROR_EMPTY_SLOT | | Empty key slot | +| | | | | PSA_ERROR_DOES_NOT_EXIST | | Empty key slot | | | | | | PSA_ERROR_INVALID_HANDLE | | 1. Zero as key slot | | | | | | | | 2. Invalid key slot | | | | | | PSA_ERROR_NOT_PERMITTED | | 1. Small output buffer size | @@ -325,7 +332,8 @@ | | | | | | 8. Check if the plain text is expected length | | | | | | | PSA_ERROR_NOT_SUPPORTED | | 1. DES key | | | | | | | | 2. Unsupported algorithm | -| | | | | PSA_ERROR_EMPTY_SLOT | | Empty key slot | +| | | | | PSA_ERROR_DOES_NOT_EXIST | | Empty key slot | +| | | | | PSA_ERROR_INVALID_ARGUMENT | | Invalid tag length | | | | | | PSA_ERROR_INVALID_HANDLE | | 1. Zero as key slot | | | | | | | | 2. Invalid key slot | | | | | | PSA_ERROR_NOT_PERMITTED | | 1. Small output buffer size | @@ -338,12 +346,15 @@ | | | | | | 6. Start a multipart MAC calculation operation | | | | | | | PSA_ERROR_NOT_SUPPORTED | | 1. 16 Byte AES - GMAC | | | | | | | | 2. Incompatible HMAC for CMAC | -| | | | | | | 3. Bad algorithm (unknown MAC algorithm)
2. Zero key slot | | | | | | PSA_ERROR_NOT_PERMITTED | | Invalid usage | -| | | | | PSA_ERROR_EMPTY_SLOT | | Empty key slot | +| | | | | PSA_ERROR_DOES_NOT_EXIST | | Empty key slot | +| | | | | PSA_ERROR_BUFFER_TOO_SMALL | | Small output buffer | +| | | | | PSA_ERROR_INVALID_HANDLE | | 1. Unallocated key handle | +| | | | | | | 2. Zero as key handle | | | test_c040 | psa_asymmetric_decrypt | Decrypt a short message with a private key | PSA_SUCCESS | 1. Initialize the PSA crypto library | 1. RSA KEYPAIR PKCS1V15 | | | | | | | 2. Initialize a key policy structure | 2. RSA KEYPAIR OAEP SHA256 | | | | | | | 3. Allocate a key slot for a transient key and set the standard fields of a policy structure | 3. RSA KEYPAIR OAEP SHA256 with label | @@ -529,7 +550,7 @@ | | | | | PSA_ERROR_INVALID_ARGUMENT | | 1. Invalid key type (RSA public key) | | | | | | | | 2. Invalid algorithm | | | | | | | | 3. Invalid key type (AES Key) | -| | | | | PSA_ERROR_EMPTY_SLOT | | Empty key slot | +| | | | | PSA_ERROR_DOES_NOT_EXIST | | Empty key slot | | | | | | PSA_ERROR_NOT_PERMITTED | | Invalid usage | | | | | | PSA_ERROR_INVALID_HANDLE | | 1. Invalid key slot
2. Zero key slot | | | | | | PSA_ERROR_BUFFER_TOO_SMALL | | Small output buffer | @@ -546,7 +567,7 @@ | | | | | | | 3. Invalid key type (AES Key) | | | | | | | | 4. Wrong hash size | | | | | | PSA_ERROR_INVALID_HANDLE | | 1. Invalid key slot
2. Zero key slot | -| | | | | PSA_ERROR_EMPTY_SLOT | | Empty key slot | +| | | | | PSA_ERROR_DOES_NOT_EXIST | | Empty key slot | | | | | | PSA_ERROR_NOT_PERMITTED | | Invalid usage | | | | | | PSA_ERROR_BUFFER_TOO_SMALL | | Small output buffer | | | test_c042 | psa_asymmetric_verify | Verify the signature a hash or short message using a public key | PSA_SUCCESS | 1. Initialize the PSA crypto library | 1. RSA KEYPAIR PKCS1V15 RAW | @@ -561,10 +582,46 @@ | | | | | PSA_ERROR_INVALID_HANDLE | | 1. Invalid key slot
2. Zero key slot | | | | | | PSA_ERROR_INVALID_SIGNATURE | | Wrong signature size | | | | | | | | Wrong signature | -| | | | | PSA_ERROR_EMPTY_SLOT | | Empty key slot | +| | | | | PSA_ERROR_DOES_NOT_EXIST | | Empty key slot | | | | | | PSA_ERROR_NOT_PERMITTED | | Invalid usage | | | | | | PSA_ERROR_NOT_SUPPORTED | | Invalid key type (AES Key) | | | | | | PSA_ERROR_BUFFER_TOO_SMALL | | Small output buffer | +| | test_c043 | psa_key_agreement | Set up a key agreement operation | PSA_SUCCESS | 1. Initialize the PSA crypto library | 1. ECDH SECP256R1 | +| | | | | | 2. Initialize a key policy structure | 2. ECDH SECP384R1 | +| | | | | | 3. Allocate a key slot for a transient key and set the standard fields of a policy structure | | +| | | | | | 4. Set the key data based on key type | | +| | | | | | 5. Set the usage policy on a key slot | | +| | | | | | 6. Import the key data into the key slot | | +| | | | | | 7. Set up a key agreement operation | | +| | | | | | 8. Retrieve the current capacity of a generator | | +| | | | | | 9. Check if the generator capacity matches with the expected capacity | | +| | | | | | 10. Read some data from a generator | | +| | | | | | 11. Check if the output matches with the expected data | | +| | | | | PSA_ERROR_INVALID_ARGUMENT | | 1. Not a key agreement alg | +| | | | | | | 2. Public key on different curve | +| | | | | | | 2. Public key instead of private key | +| | | | | PSA_ERROR_INVALID_HANDLE | | 1. Invalid key slot
2. Zero key slot | +| | | | | PSA_ERROR_DOES_NOT_EXIST | | Empty key slot | +| | | | | PSA_ERROR_NOT_PERMITTED | | Invalid usage | +| | | | | PSA_ERROR_NOT_SUPPORTED | | Invalid key type (AES Key) | +| | test_c044 | psa_copy_key | Copy key material from one location to another | PSA_SUCCESS | 1. Initialize the PSA crypto library | 1. 16 Byte AES | +| | | | | | 2. Initialize a key policy structure | 2. 24 Byte AES | +| | | | | | 3. Allocate a key slot for a transient key and set the standard fields of a policy structure | 3. 32 Byte AES with constraints | +| | | | | | 4. Set the key data based on key type | 4. 2048 RSA public key | +| | | | | | 5. Set the usage policy on a key slot | 5. 2048 RSA key pair | +| | | | | | 6. Import the key data into the key slot | 6. DES 64 bit key | +| | | | | | 7. Allocate a key slot for the target key | 7. Triple DES 2-Key | +| | | | | | 8. Set the usage policy on a key slot | 8. Triple DES 3-Key | +| | | | | | 9. Make a copy of a key | 9. EC Public key | +| | | | | | 10. Destroy the source to ensure that this doesn't affect the target | 10. EC key pair | +| | | | | | 11. Export a key in binary format and check if it matches with source material | 11. Incompatible target polic | +| | | | | PSA_ERROR_INVALID_ARGUMENT | | 1. Incompatible target policy(source and target) | +| | | | | | | 2. Incompatible constraint | +| | | | | PSA_SUCCESS | | 1. Unexport source key usage | +| | | | | PSA_ERROR_INVALID_HANDLE | | 1. Unallocated target key slot | +| | | | | PSA_ERROR_DOES_NOT_EXIST | | Empty source handle | +| | | | | PSA_ERROR_ALREADY_EXISTS | | Target already containing key material | + ## License Arm PSA test suite is distributed under Apache v2.0 License. diff --git a/api-tests/docs/psa_ipc_testlist.md b/api-tests/docs/psa_ipc_testlist.md index fd324253..2313422c 100644 --- a/api-tests/docs/psa_ipc_testlist.md +++ b/api-tests/docs/psa_ipc_testlist.md @@ -1,106 +1,122 @@ # PSA FF IPC Testcase checklist -| Tests | Scenario Rules | Client- Server Test Functions Pair | Test Algorithm/Comments/Test Limitation | PSA API Crosses | Is scenario implemented in this release? | -|------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------|------------------------------------------| -| test_i001 | 1. psa_framework_version(): Retrieve the version of the PSA Framework API that is implemented. Manifest field- psa_framework_version: Version of the PSA Firmware Framework specification this manifest conforms to. | [client/server]_test_psa_framework_version() | Call the psa_framework_version() API at both side SPE and NSPE and compare the return value with PSA_FRAMEWORK_VERSION. | psa_framework_version | Yes | -| | 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 | Yes | -| 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() returns the Secure Partition interrupt signals that have been asserted from a subset of signals provided by the caller. 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,psa_get,psa_reply | Yes | -| | 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,psa_get,psa_reply,psa_close | Yes | -| | 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 euqal 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 establish:
- 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 | Yes | -| | 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 connectionRepeat (1) to (5) for different status code, SUCCESS, +ve, -ve..
Perform all above steps from NSPE and SPE both. | psa_call,psa_reply | Yes | -| | 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 | Yes | -| | 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 | Yes | -| | 1. psa_wait(): When MODE(PSA_BLOCK) is one, the function will block the caller until one of the requested signals is asserted.
2. psa_wait(): Callers must set RES to zero, implementations must ignore the value of RES.
| [client/server]_test_psa_block_behave() | 1. Client connects to a RoT service mutilpe times.
2. RoT service psa_wait(PSA_BLOCK|(Non-zero value for timeout[30:0])) API is executed without while(1) loop.
3. RoT service serves the connections by rejecting them.
This is a sanity check, a successful handshaking between client and server for requested connection represents check pass.
Perform above steps from NSPE and SPE both. | psa_wait | Yes | -| | 1. psa_wait(): 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_poll_behave() | 1. Client connects to a RoT service mutilpe times.
2. RoT service executes psa_wait(PSA_POLL) in a while loop and checks the API's polling behaviour.
3. Call psa_wait(PSA_POLL) when no request is made by client and checks the return value.
Perform above steps from NSPE and SPE both. | psa_wait | Yes | -| 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_CONNECT2. 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 | Yes | -| | 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, | Yes | -| | 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 | Yes | -| | 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.A memory reference contains a start address and an associated length. A zero-length memory reference is one where the length is zero. The start address of a zero-length memory reference can safely take any value and must be ignored by the implementation.
2. psa_call(): If in_len is zero then in_vec is ignored
3. sa_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 | Yes | -| | 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| test_i008 | 1. psa_connect() does not return if Service to non_secure_client is not available
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 | Yes | -| test_i009 | 1. psa_connect() does not return if SID is not mentioned in dependencies field.
2. Manifest parameter- dependencies (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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| 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 | Yes | -| test_i047 | psa_get does not return if The msg pointer provided is not a valid memory reference | [client/server]_test_psa_get_with_invalid_msg_pointer() | Call psa_get with invalid msg pointer.
Selection of invalid pointer is as below:
if (ISOLATION_LEVEL > 1)
// PSA RoT Pointer
psa_get(msg_pointer = driver_mmio_base);
else
psa_get(msg_pointer = NULL);
| psa_get | Yes | -| test_i048 | psa_call does not return if address of in_vec is invalid for client | [client/server]_test_psa_call_with_invalid_invec_pointer | Call psa_call with invalid address for invec
Selection of invalid invec pointer is as below:
if caller == NONSECURE
// PSA RoT pointer
invec_pointer = driver_mmio_base;

else

if (ISOLATION_LEVEL > 1)
invec_pointer = driver_mmio_base;
else
invec_pointer = NULL; | psa_call | Yes | -| test_i049 | psa_call does not return if address of out_vec is invalid for client | [client/server]_test_psa_call_with_invalid_outvec_pointer() | Call psa_call with invalid address for outvec
Selection of invalid outvec pointer is as below:
if caller == NONSECURE
// PSA RoT pointer
outvec_pointer = driver_mmio_base;

else

if (ISOLATION_LEVEL > 1)
outvec_pointer = driver_mmio_base;
else
outvec_pointer = NULL; | psa_call | Yes | -| test_i050 | psa_call does not return if psa_invec.base address is invalid for client | [client/server]_test_psa_call_with_invalid_invec_base() | Call psa_call with invalid address for psa_invec.base
Selection of invalid base is as below:
if caller == NONSECURE
// PSA RoT pointer
invalid_base = driver_mmio_base;

else

if (ISOLATION_LEVEL > 1)
invalid_base = driver_mmio_base;
else
invalid_base = NULL; | psa_call | Yes | -| test_i051 | psa_call does not return if psa_outvec.base address is invalid for client | [client/server]_test_psa_call_with_invalid_outvec_base() | Call psa_call with invalid address for psa_outvec.base
Selection of invalid base is as below:
if caller == NONSECURE
// PSA RoT pointer
invalid_base = driver_mmio_base;

else

if (ISOLATION_LEVEL > 1)
invalid_base = driver_mmio_base;
else
invalid_base = NULL; | psa_call | Yes | -| test_i052 | psa_call does not return if psa_invec.base addr is valid but psa_invec.base+size address is invalid for client | [client/server]_test_psa_call_with_invalid_invec_end_addr() | Call psa_call with valid address for psa_invec.base but (psa_invec.base + psa_invec.size) pointing to invalid address
Selection of base and size are as below:
if caller == NONSECURE
valid_base = nspe_mmio_region_base;
invalid_size = (driver_mmio_region_base - nspe_mmio_region_base + 1);

else

if (ISOLATION_LEVEL > 1)
valid_base = client_mmio_region_base;
invalid_size = (driver_mmio_region_base - client_mmio_region_base + 1);
| psa_call | Yes | -| test_i053 | psa_call does not return if psa_outvec.base addr is valid but psa_invec.base+sizeaddress is invalid for client | [client/server]_test_psa_call_with_invalid_outvec_end_addr() | Call psa_call with valid address for psa_outvec.base but (psa_outvec.base + psa_outvec.size) pointing to invalid address
Selection of base and size are as below:
if caller == NONSECURE
valid_base = nspe_mmio_region_base;
invalid_size = (driver_mmio_region_base - nspe_mmio_region_base + 1);

else

if (ISOLATION_LEVEL > 1)
valid_base = client_mmio_region_base;
invalid_size = (driver_mmio_region_base - client_mmio_region_base + 1);
| psa_call | Yes | -| test_i054 | psa_call does not return if psa_outvec.base is not writable | [client/server]_test_psa_call_with_not_writable_outvec_base() | Call psa_call with not writable (function address - code memory) psa_outvec.base | psa_call | Yes | -| test_i055 | psa_read does not return if the memory reference for buffer is invalid | [client/server]_test_psa_read_with_invalid_buffer_addr() | Call psa_read with invalid buffer addr.
Selection of buffer address is as below:
if (ISOLATION_LEVEL > 1)
buffer = driver_mmio_region_base;
else
buffer = NULL;
| psa_read | Yes | -| test_i056 | psa_read does not return if the memory reference for buffer is not writable | [client/server]_test_psa_read_with_not_writable_buffer_addr() | Call psa_read with not writable (function address- code memory) | psa_read | Yes | -| test_i057 | psa_write does not return if the memory reference for buffer is invalid | [client/server]_test_psa_write_with_invalid_buffer_addr() | Call psa_write with invalid buffer addr.
Selection of buffer address is as below:
if (ISOLATION_LEVEL > 1)
buffer = driver_mmio_region_base;
else
buffer = NULL;
| psa_write | Yes | -| test_i058 | 1. psa_notify() is used to asynchronously wake up another Secure Partition. The receiving partition uses psa_wait() to detect, or wait for, assertion of its PSA_DOORBELL signal. The value of partition_id must be greater than zero as the target of notification must be a Secure Partition.
2. psa_clear() clears the PSA_DOORBELL signal.
3. psa_clear(): The target Partition doorbell will remain asserted until it calls psa_clear(). | [client/server]_test_psa_doorbell_signal() | 1. Client connects to RoT service.
2. RoT services executes asserts PSA_DOORBELL singal back to client after accepting the connection.
3. Client checks the delivery of PSA_DOORBELL singal using psa_wait().
4. Client clears the doorbell and closes the connection.
5. RoT service receives the closing connection request.
| psa_notify,psa_wait,psa_clear | Yes | -| test_i059 | psa_notify does not return if Partition ID does not correspond to a Secure Partition | [client/server]_test_psa_notify_with_neg_part_id() | Call psa_notify with negative partition id | psa_notify | Yes | -| test_i060 | psa_notify does not return if Partition ID does not correspond to a Secure Partition | [client/server]_test_psa_notify_with_invalid_pos_part_id() | Call psa_notify with positive partition id which does not exist in the platform | psa_notify | Yes | -| test_i061 | psa_clear does not return if The Secure Partition¿s doorbell signal is not currently asserted | [client/server]_test_psa_clear_at_unasserted_doorbell_sig() | Call psa_clear when doorbell signal is not asserted | psa_clear | Yes | -| test_i062 | psa_wait() does not return signal_mask does not include any assigned signals. | [client/server_test_psa_wait_with_unassigned_signal() | Call psa_wait with signal mask that doesn't include any assigned signal | psa_wait | Yes | -| test_i063 | psa_wait() 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 signal_mask should be ignored. | [client/server]_test_psa_wait_signal_mask() | 1. Select signal_mask = (SERVER_UNSPECIFED_MINOR_V_SIG | SERVER_RELAX_MINOR_VERSION_SIG);
2. Server partition requests the client partition to make connection using sid=SERVER_SECURE_CONNECT_ONLY_SID. This connection request act as irritator to psa_wait(signal_mask) call and it is used to cover the rule - Signals that are not in signal_mask should be ignored by psa_wait.
3. NSPE client connects to a server partition using SID whose signal are part of signal_mask
4. Server partition executes psa_wait with necessary signal_mask. RoT service checks that returned signal value is subset of signals indicated in the signal_mask
5. At the end, server partition completes the starved (irritator) connection request of SERVER_SECURE_CONNECT_ONLY_SID. | psa_wait | Yes | -| test_i064 | psa_eoi does not return if irq_signal is not an interrupt signal | driver_test_psa_eoi_with_non_intr_signal() | Call to psa_eoi with non-interrupt signal(PSA_DOORBELL).
Note: The interrupt related test check is captured in driver_partition.c as this is the only partition in test suite that holds the interrupt line. | psa_eoi | Yes | -| test_i065 | psa_eoi does not return if irq_signal is not currently asserted | driver_test_psa_eoi_with_unasserted_signal() | Call to psa_eoi with interrupt signal which is currently not asserted.
Note: The interrupt related test check is captured in driver_partition.c as this is the only partition in test suite that holds the interrupt line. | psa_eoi | Yes | -| test_i066 | psa_eoi does not return if irq_signal indicates more than one signal | driver_test_psa_eoi_with_multiple_signals() | Call to psa_eoi with irq_signal provided with multiple signals.
Note: The interrupt related test check is captured in driver_partition.c as this is the only partition in test suite that holds the interrupt line. | psa_eoi | Yes | -| | 1. psa_wait() returns the Secure Partition interrupt signals that have been asserted from the subset of signals indicated in the bitmask provided. (psa_wait API to recieve interrupt signal)
2. psa_eoi():Informs the SPM that an interrupt has been handled (end of interrupt). This will re-enable the interrupt line.
3.psa_eoi(): A signal remains active until it is processed by psa_eoi
4. Manifest- irqs (optional, unique)This attribute is a list of IRQ lines which are assigned to the Secure Partition.A Secure Partition always has exclusive access to an assigned IRQ. Secure Partitions are not allowed to share IRQs with other Secure Partitions.Each IRQ specified must provide a signal field. This field contains a symbolic name for the signal, used by the SPM to indicate when the interrupt is asserted. Each IRQ line is declared as either of the following:¿ line_num: A valid IRQ number for the platform.¿ line_name: A named IRQ, represented by a string identifier. | | The test for this scenario will be implemented in future release for PSA Test suite. Test Limitation:
1. Rules around sharing of irq lines can't be tested as specifiying it can result into build error.
2. Test suite partition manifests are rely on only line_num for specifying irq line number as line_name is subject to resolved in Implementation defined manner. | psa_wait,psa_eoi | No | -| | Only Code is executable in secure partition | | The test for this scenario will be implemented in future release for PSA Test suite. Checks:
1. Execute instruction from writable memory
2. Execute instruction from read-only data | | No | -| | Only Private data is writable in secure partition | | The test for this scenario will be implemented in future release for PSA Test suite. Checks:
1. Write to code space
2. Write to constant data space | | No | -| | Isolation Level-1 rules:
I3: If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B. From B access below asserts of A:
- Variables
- Execution stacks
- Allocation heap
- Memory-mapped I/O regions

Where, A & B combination are:
A=Application RoT & B=NSPE
A=PSA RoT & B=NSPE
A=SPM & B=NSPE | | The test for this scenario will be implemented in future release for PSA Test suite. | | No | -| | Isolation Level-2 rules- In addition to the Level 1 requirements, Level 2 firmware isolation must follow below rule:
I3: If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B. From B access below asserts of A:
- Variables
- Execution stacks
- Allocation heap
- Memory-mapped I/O regions

Where, A & B combination are:
A=PSA RoT partition & B=Application RoT partition
A=SPM & B=Application RoT partition | | The test for this scenario will be implemented in future release for PSA Test suite. | | No | -| | Isolation Level-3 rules- In addition to the Level 2 requirements, Level 3 firmware isolation must follow below rule:
I3: If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B. From B access below asserts of A:
- Variables
- Execution stacks
- Allocation heap
- Memory-mapped I/O regions

Where, A & B combination are:
A=Secure Partition & B= Other Secure Partition
A=SPM & B=Secure Partition | | The test for this scenario will be implemented in future release for PSA Test suite. | | No | -| | A Secure Partition is guaranteed to be able to read and write its private heap regions.
Manifest Parameter- heap_size. Properties: Optional.This attribute indicates the Secure Partition¿s heap size in bytes.The size value is represented either as a positive integer or as a hexadecimal string.If this field is specified in the manifest then the value must be greater than 0. If this field is not specified in the manifest then the SPM can assume the size is 0.If the framework does not implement the dynamic memory allocation API, then a manifest which specifies a heap_size must produce a build error. | | The test for this scenario will be implemented in future release for PSA Test suite. Check that specified heap memory is writable using malloc function. | | No | -| NO_EXPLICIT_TEST | A Secure Partition is guaranteed to be able to read and write its private stack.
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. | N/A | No explicit test written to cover this rule. PSA IPC tests manifests are provided with tests partition required stack_size. A successful execution of tests partition code without stack access related faults, indirectly verify this field. | N/A | Yes | -| NO_EXPLICIT_TEST | mmio_regions (optional, unique):
List of memory-mapped I/O region objects which the Secure Partition needs access to. 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
A numbered region consists of a base address and a size. The size must be represented either as a positive integer or as a hexadecimal string. The base address must be represented as a hexadecimal string.
MMIO regions must not overlap.
An MMIO region must include a permission attribute. The following permissions are available:
READ-ONLY
READ-WRITE | N/A | Comments:
1. 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.
2. Rules around sharing of MMIO regions is covered as part of isolation tests.
3. Rules around overlapping of MMIO regions can't be tested as specifying that into manifest results into compilation fail.
4. Test suite partition manifests are rely on numbered_region only as named_region is subject to resolved in Implementation defined manner. | N/A | Yes | -| NO_EXPLICIT_TEST | Manifest Parameter - id (required, unique):A Secure Partition ID must be a non-zero positive 32-bit value. | N/A | PSA IPC tests partitions are provided with unique Partition ID (which is non-zero positive) and name field compliant to manifest rules. | N/A | Yes | -| NO_EXPLICIT_TEST | 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 | N/A | PSA IPC tests partition files are provided with these fields. Access permission behaviour related to these fields will be verified as part of tests covering isolation level rules. | N/A | Yes | -| NO_EXPLICIT_TEST | Manifest Parameter - description (optional)
This attribute contains a human-readable description and comments for the Secure Partition. | N/A | Test suite manifests are provided with these field with adhere to manifest rules. Manifest build tool parser must parse this field without any compilation fail. | N/A | Yes | -| 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. | N/A | No explicit test written to cover this rule. Test suite manifests are provided with tests partition entry_point. A successful launch and run of tests partition code indirectly verify this field. | N/A | Yes | -| NO_EXPLICIT_TEST | Manifest Parameter- linker_pattern(Required)
This attribute contains the minimum information required to link a Secure Partition¿s compiled static objects. It contains a mandatory sub attribute called object_list.The object_list is an explicit list of the expected binary objects of a Secure Partition once it is compiled. For example, this list may contain the names of the main entry-point binary and any other static library objects that the Secure Partition contains. | N/A | No explicit test written to cover this rule. Test suite manifests are provided with linker_pattern filled with required tests partition object archives. A successful use of this field to launch the partitions and fullfilling the PSA defined isolation requirement which are also being verified in isolation tests, indirectly verify this field. | N/A | Yes | -| NO_EXPLICIT_TEST | The SPM must eventually deliver all signals and IPC messages. | N/A | No explicit test written to cover this rule. However all PSA IPC 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.
This rule is unbounded and cannot have full coverage. It is good to that things are delivered in a timely manner, however failure will not break compliance. | N/A | Yes | -| NO_EXPLICIT_TEST | A Secure Partition is guaranteed to be able to execute and read its own code regions and read its own read-only data regions. | N/A | No explicit test written to cover this rule. This is a minimum requirement to able to launch and run secure partition. Failing to provide this, will not be able to run IPC test suite. | N/A | Yes | -| NOT_COVERED | 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. | N/A | This scenario cannot be simulated as test can't generate stimulus where psa_get() API returns PSA_ERR_NOMSG. However every instances of psa_get() API call in test suite checks the API return value and re-waits for signal delivery if return value is PSA_ERR_NOMSG. | psa_get | No | -| NOT_COVERED | psa_call does not return if the connection is already handling a request. | N/A | This rule is not verified beacuse of following reasons:
- There is no common infrastructure to test this rule as this needs two OS specific tasks where one task interrupted during execution of psa_call or psa_close and second task scheduled to execute psa_call using same handle.
- It is hard to write test stimulus where this rule is always hit as it is highly dependent of platform software scheduling policy.
| psa_call | No | -| NOT_COVERED | psa_close does not return if the connection is already handling a request | N/A | This rule is not verified beacuse of following reasons:
- There is no common infrastructure to test this rule as this needs two OS specific tasks where one task interrupted during execution of psa_call or psa_close and second task scheduled to execute psa_close using same handle.
- It is hard to write test stimulus where this rule is always hit as it is highly dependent of platform software scheduling policy.
| psa_close | No | -| NOT_COVERED | Manifest Parameter - priority (required)
Partitions must be assigned one of the following priority groups:¿ HIGH¿ NORMAL¿ LOWLOW is the lowest priority. Priority is ignored by SPMs that do not implement any priority-based scheduling. | N/A | Use of these fields are highly dependent on type of SPM scheduling policy, hence can't test the behavious of this field. And test suite partition manifests are provided with priority field equal to LOW. | N/A | No | -| NOT_COVERED | Secure Partition IDs must be fixed across updates | N/A | Checking for Partition IDs across SPM updates is out of scope for PSA IPC tests. | N/A | No | +| Tests | Scenario Rules | Client- Server Test Functions Pair | Test Algorithm/Comments/Test Limitation | Rule optional for API compliance? | Is scenario implemented in this release? | +|------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------|------------------------------------------| +| test_i001 | 1. psa_framework_version(): Retrieve the version of the PSA Framework API that is implemented. Manifest field- psa_framework_version: Version of the PSA Firmware Framework specification this manifest conforms to. | [client/server]_test_psa_framework_version() | Call the psa_framework_version() API from both SPE and NSPE side and compare the return value with PSA_FRAMEWORK_VERSION. | Mandatory | Yes | +| | 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. | Mandatory | Yes | +| test_i002 | 1. psa_connect() returns the PSA_ERROR_CONNECTION_BUSY when RoT Service cannot make the connection at the moment (transient error).
2. psa_connect() returns the PSA_ERROR_CONNECTION_REFUSED when RoT Service has refused the connection
3. psa_wait() returns the Secure Partition interrupt signals that have been asserted from a subset of signals provided by the caller. 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_ERROR_CONNECTION_REFUSED (permanent error) or PSA_ERROR_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_ERROR_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_ERROR_CONNECTION_REFUSED)
Perform all above steps from NSPE and SPE both. | Mandatory | Yes | +| | 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. | Mandatory | Yes | +| | 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 euqal 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 establish:
- 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. | Mandatory | Yes | +| | 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, all status codes other than PSA_ERROR_PROGRAMMER_ERROR must be 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 connectionRepeat (1) to (5) for different status code, SUCCESS, +ve, -ve..
Perform all above steps from NSPE and SPE both. | Mandatory | Yes | +| | 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. | [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. | Mandatory | Yes | +| | 1. psa_connect() will return the PSA_ERROR_CONNECTION_REFUSED OR PSA_ERROR_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 to indicate end of concurrent connections limit.
Perform above step from NSPE and SPE both. | Mandatory | Yes | +| | 1. psa_wait(): When MODE(PSA_BLOCK) is one, the function will block the caller until one of the requested signals is asserted.
2. psa_wait(): Callers must set RES to zero, implementations must ignore the value of RES.
| [client/server]_test_psa_block_behave() | 1. Client connects to a RoT service mutilpe times.
2. RoT service psa_wait(PSA_BLOCK|(Non-zero value for timeout[30:0])) API is executed without while(1) loop.
3. RoT service serves the connections by rejecting them.
This is a sanity check, a successful handshaking between client and server for requested connection represents check pass.
Perform above steps from NSPE and SPE both. | Mandatory | Yes | +| | 1. psa_wait(): 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_poll_behave() | 1. Client connects to a RoT service mutilpe times.
2. RoT service executes psa_wait(PSA_POLL) in a while loop and checks the API's polling behaviour.
3. Call psa_wait(PSA_POLL) when no request is made by client and checks the return value.
Perform above steps from NSPE and SPE both. | Mandatory | Yes | +| 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_CONNECT2. 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. | Mandatory | Yes | +| | 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. | Mandatory | Yes | +| | 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. | Mandatory | Yes | +| | 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.A memory reference contains a start address and an associated length. A zero-length memory reference is one where the length is zero. The start address of a zero-length memory reference can safely take any value and must be ignored by the implementation.
2. psa_call(): If in_len is zero then in_vec is ignored
3. sa_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. | Mandatory | Yes | +| | 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. | Mandatory | Yes | +| test_i004 | The call to psa_connect() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour.
Perform above step from NSPE and SPE both. | Optional | Yes | +| test_i005 | The call to psa_connect() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour.
Perform above step from NSPE and SPE both. | Optional | Yes | +| test_i006 | The call to psa_connect() is PROGRAMMER ERROR 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. | Optional | Yes | +| test_i007 | The call to psa_connect() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour.
Perform above step from NSPE and SPE both. | Optional | Yes | +| test_i008 | 1. The call to psa_connect() is PROGRAMMER ERROR if service to non_secure_client is not available
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 and expect PROGRAMMER ERROR behaviour.
Perform above step from NSPE and SPE both. | Optional | Yes | +| test_i009 | 1. The call to psa_connect() is PROGRAMMER ERROR if SID is not mentioned in dependencies field.
2. Manifest parameter- dependencies (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 and expect PROGRAMMER ERROR behaviour. | Optional | Yes | +| 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". In this case, the call to psa_connect() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour.
Perform above step from NSPE and SPE both. | Optional | Yes | +| 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". In this case, the call to psa_connect() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour.
Perform above step from NSPE and SPE both. | Optional | Yes | +| test_i012 | The call to psa_close() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour.
Perform above step from NSPE and SPE both. | Optional | Yes | +| test_i013 | The call to psa_get() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour. | Optional | Yes | +| 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 and expect PROGRAMMER ERROR behaviour. | Optional | Yes | +| test_i015 | The call to psa_get() is PROGRAMMER ERROR if signal does not correspond to a RoT Service | [client/server]_test_psa_get_with_non_rot_signal() | Call psa_get with DOORBELL signal and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i016 | The call to psa_get() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| 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 and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i018 | The call to psa_set_rhandle() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour for API call | Optional | Yes | +| test_i019 | The call to psa_set_rhandle() is PROGRAMMER ERROR if Null handle was passed | [client/server]_test_psa_set_rhandle_with_null_handle() | Call psa_set_rhanlde with NULL handle and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i020 | If messgae type if is PSA_IPC_CONNECT then use of status values in psa_reply() other than PSA_SUCCESS, PSA_ERROR_CONNECTION_BUSY and PSA_ERROR_CONNECTION_REFUSED is a PROGRAMMER 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 and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i021 | 1. psa_wait() returns the Secure Partition interrupt signals that have been asserted from the subset of signals indicated in the bitmask provided. (psa_wait API to recieve interrupt signal)
2. psa_eoi():Informs the SPM that an interrupt has been handled (end of interrupt). This will re-enable the interrupt line.
3.psa_eoi(): A signal remains active until it is processed by psa_eoi
4. Manifest- irqs (optional, unique)This attribute is a list of IRQ lines which are assigned to the Secure Partition.A Secure Partition always has exclusive access to an assigned IRQ. Secure Partitions are not allowed to share IRQs with other Secure Partitions.Each IRQ specified must provide a signal field. This field contains a symbolic name for the signal, used by the SPM to indicate when the interrupt is asserted. Each IRQ line is declared as either of the following:¿ line_num: A valid IRQ number for the platform.¿ line_name: A named IRQ, represented by a string identifier. | | Generate interrupt for driver partition assigned irq number and checks that:
- interrupt is routed to driver patition
- psa_wait returns the required irq_signal value
- end of interrupt using psa_eoi
Test Limitation:
1. Rules around sharing of irq lines can't be tested as specifiying it can result into build error.
2. Test suite partition manifests are rely on only line_num for specifying irq line number as line_name is subject to resolved in Implementation defined manner. | Mandatory | Yes | +| test_i022 | The call to psa_reply() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i023 | The call to psa_reply() is PROGRAMMER ERROR if the message handle is Null handle | [client/server]_test_psa_reply_with_null_handle() | Call psa_reply with a NULL HANDLE | Optional | Yes | +| test_i024 | The call to psa_call() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour for API call.Perform this step from NSPE and SPE both. | Optional | Yes | +| test_i025 | The call to psa_call() is PROGRAMMER ERROR if an null handle was passed | [client/server]_test_psa_call_with_null_handle() | Call psa_call with a NULL HANDLE and expect PROGRAMMER ERROR behaviour for API call.
Perform this step from NSPE and SPE both. | Optional | Yes | +| test_i026 | The call to psa_call() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour for API call.
Perform this step from NSPE and SPE both. | Optional | Yes | +| test_i027 | 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_ERROR_PROGRAMMER_ERROR. 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.
If the SPM does not restart the system in response to the above PROGRAMMER ERROR, then termination of the connection has the following effects:
- No further PSA_IPC_CALL messages will be received by the RoT Service for the connection.
- The RoT Service will receive a PSA_IPC_DISCONNECT message for the connection to release resources and reset state associated with the connection.
- The failing call to psa_call() will return PSA_ERROR_PROGRAMMER_ERROR.
- Subsequent calls to psa_call() on the same connection will immediately return PSA_ERROR_PROGRAMMER_ERROR.
- The client must call psa_close() to close the connection. | [client/server]_test_psa_drop_connection() | RoT service executes psa_reply with status code equal to PSA_ERROR_PROGRAMMER_ERROR during PSA_IPC_CALL.Client expects either PSA_ERROR_PROGRAMMER_ERROR as returned status code for psa_call or system to get paniced
Perform above steps from NSPE and SPE both. | Optional | Yes | +| test_i028 | The call to psa_read() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i029 | The call to psa_read() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i030 | The call to psa_read() is PROGRAMMER ERROR if Null handle was passed | [client/server]_test_psa_read_with_null_handle() | Call psa_read with NULL handle and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i031 | The call to psa_read() is PROGRAMMER ERROR if Invalid handle was passed | [client/server]_test_psa_read_with_invalid_handle() | Calll psa_read with INVALID_HANDLE which is not NULL and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i032 | The call to psa_read() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i033 | The call to psa_read() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i034 | The call to psa_skip() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i035 | The call to psa_skip() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i036 | The call to psa_skip() is PROGRAMMER ERROR if Null handle was passed | [client/server]_test_psa_skip_with_null_handle() | Call psa_skipwith NULL handle and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i037 | The call to psa_skip() is PROGRAMMER ERROR if Invalid handle was passed | [client/server]_test_psa_skip_with_invalid_handle() | Calll psa_skip with INVALID_HANDLE which is not NULL and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i038 | The call to psa_skip() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i039 | The call to psa_skip() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i040 | The call to psa_write() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i041 | The call to psa_write() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i042 | The call to psa_write() is PROGRAMMER ERROR if Null handle was passed | [client/server]_test_psa_write_with_null_handle() | Call psa_write with NULL handle and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i043 | The call to psa_write() is PROGRAMMER ERROR if Invalid handle was passed | [client/server]_test_psa_write_with_invalid_handle() | Calll psa_write with INVALID_HANDLE which is not NULL and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i044 | The call to psa_write() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i045 | The call to psa_write() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i046 | The call to psa_write() is PROGRAMMER ERROR 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 and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i047 | The call to psa_get() is PROGRAMMER ERROR if The msg pointer provided is not a valid memory reference | [client/server]_test_psa_get_with_invalid_msg_pointer() | Call psa_get with invalid msg pointer and expect PROGRAMMER ERROR behaviour for API call.
Selection of invalid pointer is as below:
if (ISOLATION_LEVEL > 1)
// PSA RoT Pointer
psa_get(msg_pointer = driver_mmio_base);
else
psa_get(msg_pointer = NULL);
| Optional | Yes | +| test_i048 | The call to psa_call() is PROGRAMMER ERROR if address of in_vec is invalid for client | [client/server]_test_psa_call_with_invalid_invec_pointer | Call psa_call with invalid address for invec and expect PROGRAMMER ERROR behaviour for API call.
Selection of invalid invec pointer is as below:
if caller == NONSECURE
// PSA RoT pointer
invec_pointer = driver_mmio_base;

else

if (ISOLATION_LEVEL > 1)
invec_pointer = driver_mmio_base;
else
invec_pointer = NULL; | Optional | Yes | +| test_i049 | The call to psa_call() is PROGRAMMER ERROR if address of out_vec is invalid for client | [client/server]_test_psa_call_with_invalid_outvec_pointer() | Call psa_call with invalid address for outvec and expect PROGRAMMER ERROR behaviour for API call.
Selection of invalid outvec pointer is as below:
if caller == NONSECURE
// PSA RoT pointer
outvec_pointer = driver_mmio_base;

else

if (ISOLATION_LEVEL > 1)
outvec_pointer = driver_mmio_base;
else
outvec_pointer = NULL; | Optional | Yes | +| test_i050 | The call to psa_call() is PROGRAMMER ERROR if psa_invec.base address is invalid for client | [client/server]_test_psa_call_with_invalid_invec_base() | Call psa_call with invalid address for psa_invec.base and expect PROGRAMMER ERROR behaviour for API call.
Selection of invalid base is as below:
if caller == NONSECURE
// PSA RoT pointer
invalid_base = driver_mmio_base;

else

if (ISOLATION_LEVEL > 1)
invalid_base = driver_mmio_base;
else
invalid_base = NULL; | Optional | Yes | +| test_i051 | The call to psa_call() is PROGRAMMER ERROR if psa_outvec.base address is invalid for client | [client/server]_test_psa_call_with_invalid_outvec_base() | Call psa_call with invalid address for psa_outvec.base and expect PROGRAMMER ERROR behaviour for API call.
Selection of invalid base is as below:
if caller == NONSECURE
// PSA RoT pointer
invalid_base = driver_mmio_base;

else

if (ISOLATION_LEVEL > 1)
invalid_base = driver_mmio_base;
else
invalid_base = NULL; | Optional | Yes | +| test_i052 | The call to psa_call() is PROGRAMMER ERROR if psa_invec.base addr is valid but psa_invec.base+size address is invalid for client | [client/server]_test_psa_call_with_invalid_invec_end_addr() | Call psa_call with valid address for psa_invec.base but (psa_invec.base + psa_invec.size) pointing to invalid address and expect PROGRAMMER ERROR behaviour for API call.
Selection of base and size are as below:
if caller == NONSECURE
valid_base = nspe_mmio_region_base;
invalid_size = (driver_mmio_region_base - nspe_mmio_region_base + 1);

else

if (ISOLATION_LEVEL > 1)
valid_base = server_mmio_region_base;
invalid_size = (driver_mmio_region_base - server_mmio_region_base + 1);
| Optional | Yes | +| test_i053 | The call to psa_call() is PROGRAMMER ERROR if psa_outvec.base addr is valid but psa_invec.base+sizeaddress is invalid for client | [client/server]_test_psa_call_with_invalid_outvec_end_addr() | Call psa_call with valid address for psa_outvec.base but (psa_outvec.base + psa_outvec.size) pointing to invalid address and expect PROGRAMMER ERROR behaviour for API call.
Selection of base and size are as below:
if caller == NONSECURE
valid_base = nspe_mmio_region_base;
invalid_size = (driver_mmio_region_base - nspe_mmio_region_base + 1);

else

if (ISOLATION_LEVEL > 1)
valid_base = server_mmio_region_base;
invalid_size = (driver_mmio_region_base - server_mmio_region_base + 1);
| Optional | Yes | +| test_i054 | The call to psa_call() is PROGRAMMER ERROR if psa_outvec.base is not writable | [client/server]_test_psa_call_with_not_writable_outvec_base() | Call psa_call with not writable (code address) psa_outvec.base and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i055 | The call to psa_read() is PROGRAMMER ERROR if the memory reference for buffer is invalid | [client/server]_test_psa_read_with_invalid_buffer_addr() | Call psa_read with invalid buffer addr and expect PROGRAMMER ERROR behaviour for API call.
Selection of buffer address is as below:
if (ISOLATION_LEVEL > 1)
buffer = driver_mmio_region_base;
else
buffer = NULL;
| Optional | Yes | +| test_i056 | The call to psa_read() is PROGRAMMER ERROR if the memory reference for buffer is not writable | [client/server]_test_psa_read_with_not_writable_buffer_addr() | Call psa_read with not writable address (function address- code memory) and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i057 | The call to psa_write() is PROGRAMMER ERROR if the memory reference for buffer is invalid | [client/server]_test_psa_write_with_invalid_buffer_addr() | Call psa_write with invalid buffer addr and expect PROGRAMMER ERROR behaviour for API call.
Selection of buffer address is as below:
if (ISOLATION_LEVEL > 1)
buffer = driver_mmio_region_base;
else
buffer = NULL;
| Optional | Yes | +| test_i058 | 1. psa_notify() is used to asynchronously wake up another Secure Partition. The receiving partition uses psa_wait() to detect, or wait for, assertion of its PSA_DOORBELL signal. The value of partition_id must be greater than zero as the target of notification must be a Secure Partition.
2. psa_clear() clears the PSA_DOORBELL signal.
3. psa_clear(): The target Partition doorbell will remain asserted until it calls psa_clear(). | [client/server]_test_psa_doorbell_signal() | 1. Client connects to RoT service.
2. RoT services executes asserts PSA_DOORBELL singal back to client after accepting the connection.
3. Client checks the delivery of PSA_DOORBELL singal using psa_wait().
4. Client clears the doorbell and closes the connection.
5. RoT service receives the closing connection request.
| Mandatory | Yes | +| test_i059 | The call to psa_notify() is PROGRAMMER ERROR if Partition ID does not correspond to a Secure Partition | [client/server]_test_psa_notify_with_neg_part_id() | Call psa_notify with negative partition id and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i060 | The call to psa_notify() is PROGRAMMER ERROR if Partition ID does not correspond to a Secure Partition | [client/server]_test_psa_notify_with_invalid_pos_part_id() | Call psa_notify with positive partition id which does not exist in the platform and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i061 | The call to psa_clear() is PROGRAMMER ERROR if The Secure Partition¿s doorbell signal is not currently asserted | [client/server]_test_psa_clear_at_unasserted_doorbell_sig() | Call psa_clear when doorbell signal is not asserted and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i062 | The call to psa_wait() is PROGRAMMER ERROR if signal_mask does not include any assigned signals. | [client/server_test_psa_wait_with_unassigned_signal() | Call psa_wait with signal mask that doesn't include any assigned signal and expect PROGRAMMER ERROR behaviour for API call. | Optional | Yes | +| test_i063 | psa_wait() 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 signal_mask should be ignored. | [client/server]_test_psa_wait_signal_mask() | 1. Select signal_mask = (SERVER_UNSPECIFED_MINOR_V_SIG | SERVER_RELAX_MINOR_VERSION_SIG);
2. Server partition requests the client partition to make connection using sid=SERVER_SECURE_CONNECT_ONLY_SID. This connection request act as irritator to psa_wait(signal_mask) call and it is used to cover the rule - Signals that are not in signal_mask should be ignored by psa_wait.
3. NSPE client connects to a server partition using SID whose signal are part of signal_mask
4. Server partition executes psa_wait with necessary signal_mask. RoT service checks that returned signal value is subset of signals indicated in the signal_mask
5. At the end, server partition completes the starved (irritator) connection request of SERVER_SECURE_CONNECT_ONLY_SID. | Mandatory | Yes | +| test_i064 | The call to psa_eoi() is PROGRAMMER ERROR if irq_signal is not an interrupt signal | driver_test_psa_eoi_with_non_intr_signal() | Call to psa_eoi with non-interrupt signal(PSA_DOORBELL) and expect PROGRAMMER ERROR behaviour for API call.
Note: The interrupt related test check is captured in driver_partition.c as this is the only partition in test suite that holds the interrupt line. | Optional | Yes | +| test_i065 | The call to psa_eoi() is PROGRAMMER ERROR if irq_signal is not currently asserted | driver_test_psa_eoi_with_unasserted_signal() | Call to psa_eoi with interrupt signal which is currently not asserted and expect PROGRAMMER ERROR behaviour for API call.
Note: The interrupt related test check is captured in driver_partition.c as this is the only partition in test suite that holds the interrupt line. | Optional | Yes | +| test_i066 | The call to psa_eoi() is PROGRAMMER ERROR if irq_signal indicates more than one signal | driver_test_psa_eoi_with_multiple_signals() | Call to psa_eoi with irq_signal provided with multiple signals and expect PROGRAMMER ERROR behaviour for API call.
Note: The interrupt related test check is captured in driver_partition.c as this is the only partition in test suite that holds the interrupt line. | Optional | Yes | +| test_i067 | A Secure Partition is guaranteed to be able to read and write its private heap regions.
Manifest Parameter- heap_size. Properties: Optional.
This attribute indicates the Secure Partition¿s heap size in bytes.The size value is represented either as a positive integer or as a hexadecimal string.If this field is specified in the manifest then the value must be greater than 0. If this field is not specified in the manifest then the SPM can assume the size is 0. | [client/server]_test_dynamic_mem_alloc_fn() | Test dynamic memory service functions-malloc(), free() and realloc behaviour defined in the specification if these APIs are available to secure partition. Otherwise skip the test.
| Mandatory | Yes | +| test_i068 | Only Code is executable in secure partition. Writable region must not be executable. | client_test_instr_exec_from_writable_mem() | Execute set of instructions from writable memory and expect internal fault. | Optional | Yes | +| test_i069 | Only Private data is writable in secure partition. Code space must not be writable. | client_test_write_to_code_space() | Write to code space from secure partition and expect internal fault | Optional | Yes | +| test_i070 | Only Private data is writable in secure partition. Constant data space must not be writable. | client_test_write_to_const_data() | Write to constant data space and expect internal fault | Optional | Yes | +| test_i071 | The following memory manipulation functions from must be implemented with standard C99 definitions:
memcmp()
memcpy()
memmove()
memset() | client_test_mem_manipulation_fn() | 1 .Set buffer content using memset(). Check that content is set as expected.
2. Copy one buffer to another buffer using memcpy() and check that buffer is copied correctly.
3. Compare two buffers two times, once with equal data and once with unequal data.
2. Copy one buffer to another buffer using memmove() and check that buffer is copied correctly.
| Mandatory | Yes | +| test_i072 | If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B.
Where A is Application RoT, B is NSPE and memory asset to be protected is Application RoT variables- Global variable. | [client/server]_test_nspe_read_app_rot_variable()
[client/server]_test_nspe_write_app_rot_variable() | Access Application RoT global variable address from NSPE and expect internal fault behavior | Optional | Yes | +| test_i073 | If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B.
Where A is Application RoT, B is NSPE and memory asset to be protected is Application RoT execution stack. | [client/server]_test_nspe_read_app_rot_stack()
[client/server]_test_nspe_write_app_rot_stack() | Access Application RoT local variable(stack region) address from NSPE and expect internal fault behavior | Optional | Yes | +| test_i074 | If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B.
Where A is Application RoT, B is NSPE and memory asset to be protected is Application RoT heap memory. | [client/server]_test_nspe_read_app_rot_heap()
[client/server]_test_nspe_write_app_rot_heap() | Access Application RoT heap memory address from NSPE and expect internal fault behavior | Optional | Yes | +| test_i075 | If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B.
Where A is Application RoT, B is NSPE and memory asset to be protected is Application RoT MMIO region. | [client/server]_test_nspe_read_app_rot_mmio()
[client/server]_test_nspe_write_app_rot_mmio() | Access Application RoT MMIO address from NSPE and expect internal fault behavior | Optional | Yes | +| test_i076 | If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B.
Where A is PSA RoT, B is NSPE and memory asset to be protected is PSA RoT variables- Global variable. | client_test_nspe_read_psa_rot_variable()
driver_test_isolation_psa_rot_data_rd()
client_test_nspe_write_psa_rot_variable()
driver_test_isolation_psa_rot_data_wr() | Access PSA RoT global variable address from NSPE and expect internal fault behavior | Optional | Yes | +| test_i077 | If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B.
Where A is PSA RoT, B is NSPE and memory asset to be protected is PSA RoT execution stack. | client_test_nspe_read_psa_rot_stack()
driver_test_isolation_psa_rot_stack_rd()
client_test_nspe_write_psa_stack_variable()
driver_test_isolation_psa_rot_stack_wr() | Access PSA RoT local variable(stack region) address from NSPE and expect internal fault behavior | Optional | Yes | +| test_i078 | If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B.
Where A is PSA RoT, B is NSPE and memory asset to be protected is PSA RoT heap memory. | client_test_nspe_read_psa_rot_heap()
driver_test_isolation_psa_rot_heap_rd()
client_test_nspe_write_psa_rot_heap()
driver_test_isolation_psa_rot_heap_wr() | Access PSA RoT partition heap memory address from NSPE and expect internal fault behavior | Optional | Yes | +| test_i079 | If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B.
Where A is PSA RoT, B is NSPE and memory asset to be protected is PSA RoT MMIO region. | client_test_nspe_read_psa_rot_mmio()
driver_test_isolation_psa_rot_mmio_rd()
client_test_nspe_write_psa_rot_mmio()
driver_test_isolation_psa_rot_mmio_wr() | Access PSA RoT partition MMIO address from NSPE and expect internal fault behavior | Optional | Yes | +| test_i080 | If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B.
Where A is PSA RoT, B is Application RoT and memory asset to be protected is PSA RoT variables- Global variable. | client_test_app_rot_read_psa_rot_variable()
driver_test_isolation_psa_rot_data_rd()
client_test_app_rot_write_psa_rot_variable()
driver_test_isolation_psa_rot_data_wr() | Access PSA RoT global variable address from Application RoT and expect internal fault behavior | Optional | Yes | +| test_i081 | If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B.
Where A is PSA RoT, B is Application RoT and memory asset to be protected is PSA RoT execution stack. | client_test_app_rot_read_psa_rot_stack()
driver_test_isolation_psa_rot_stack_rd()
client_test_app_rot_write_psa_stack_variable()
driver_test_isolation_psa_rot_stack_wr() | Access PSA RoT local variable(stack region) address from Application RoT and expect internal fault behavior | Optional | Yes | +| test_i082 | If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B.
Where A is PSA RoT, B is Application RoT and memory asset to be protected is PSA RoT heap memory. | client_test_app_rot_read_psa_rot_heap()
driver_test_isolation_psa_rot_heap_rd()
client_test_app_rot_write_psa_rot_heap()
driver_test_isolation_psa_rot_heap_wr() | Access PSA RoT partition heap memory address from Application RoT and expect internal fault behavior | Optional | Yes | +| test_i083 | If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B.
Where A is PSA RoT, B is Application RoT and memory asset to be protected is PSA RoT MMIO region. | client_test_app_rot_read_psa_rot_mmio()
driver_test_isolation_psa_rot_mmio_rd()
client_test_app_rot_write_psa_rot_mmio()
driver_test_isolation_psa_rot_mmio_wr() | Access PSA RoT partition MMIO address from Application RoT and expect internal fault behavior | Optional | Yes | +| test_i084 | If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B.
Where A is Application RoT Partition, B is other Application RoT partition and memory asset to be protected is A- Application RoT partition variables- Global variable. | [client/server]_test_sp_read_other_sp_variable()
[client/server]_test_sp_write_other_sp_variable() | Access Application RoT global variable address from other RoT and expect internal fault behavior | Optional | Yes | +| test_i085 | If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B.
Where A is Application RoT Partition, B is other Application RoT partition and memory asset to be protected is A- Application RoT partition execution stack. | [client/server]_test_sp_read_other_sp_stack()
[client/server]_test_sp_write_other_sp_stack() | Access Application RoT local variable(stack region) address from other Application RoT and expect internal fault behavior | Optional | Yes | +| test_i086 | If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B.
Where A is Application RoT Partition, B is other Application RoT partition and memory asset to be protected is A- Application RoT partition heap memory. | [client/server]_test_sp_read_other_sp_heap()
[client/server]_test_sp_write_other_sp_heap() | Access Application RoT partition heap memory address from other Application RoT and expect internal fault behavior | Optional | Yes | +| test_i087 | If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B.
Where A is Application RoT Partition, B is other Application RoT partition and memory asset to be protected is A- Application RoT partition MMIO region. | [client/server]_test_sp_read_other_sp_mmio()
[client/server]_test_sp_write_other_sp_mmio() | Access Application RoT partition MMIO address from other Application RoT and expect internal fault behavior | Optional | Yes | +| test_l088 | psa_rot_lifecycle_state() function retrieves the current PSA RoT lifecycle state. | server_test_psa_rot_lifecycle_state() | Call psa_rot_lifecycle_state() from secure side and check that return value is within the allowed range. | Mandatory | Yes | +| | If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B. From B access below asserts of A:
- Variables
- Execution stacks
- Allocation heap
- Memory-mapped I/O regions

Where, A & B combination are: A=SPM & B=NSPE | N/A | Future release of test suite will be updated to cover this rule. | N/A | No | +| | If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B. From B access below asserts of A:
- Variables
- Execution stacks
- Allocation heap
- Memory-mapped I/O regions

Where, A & B combination are: A=SPM & B=Application RoT partition | N/A | Future release of test suite will be updated to cover this rule. | N/A | No | +| | If domain A needs protection from domain B, then Private data in domain A cannot be accessed by domain B. From B access below asserts of A:
- Variables
- Execution stacks
- Allocation heap
- Memory-mapped I/O regions

Where, A & B combination are: A=SPM & B=PSA RoT partition | N/A | Future release of test suite will be updated to cover this rule. | N/A | No | +| NO_EXPLICIT_TEST | A Secure Partition is guaranteed to be able to read and write its private stack.
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. | N/A | No explicit test written to cover this rule. PSA IPC tests manifests are provided with tests partition required stack_size. A successful execution of tests partition code without stack access related faults, indirectly verify this field. | N/A | Yes | +| NO_EXPLICIT_TEST | mmio_regions (optional, unique):
List of memory-mapped I/O region objects which the Secure Partition needs access to. 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
A numbered region consists of a base address and a size. The size must be represented either as a positive integer or as a hexadecimal string. The base address must be represented as a hexadecimal string.
MMIO regions must not overlap.
An MMIO region must include a permission attribute. The following permissions are available:
READ-ONLY
READ-WRITE | N/A | Comments:
1. 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.
2. Rules around sharing of MMIO regions is covered as part of isolation tests.
3. Rules around overlapping of MMIO regions can't be tested as specifying that into manifest results into compilation fail.
4. Test suite partition manifests are rely on numbered_region only as named_region is subject to resolved in Implementation defined manner. | N/A | Yes | +| NO_EXPLICIT_TEST | 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 | N/A | PSA IPC tests partition files are provided with these fields. Access permission behaviour related to these fields will be verified as part of tests covering isolation level rules. | N/A | Yes | +| NO_EXPLICIT_TEST | Manifest Parameter - description (optional)
This attribute contains a human-readable description and comments for the Secure Partition. | N/A | Test suite manifests are provided with these field with adhere to manifest rules. Manifest build tool parser must parse this field without any compilation fail. | N/A | Yes | +| 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 and it must have the following signature:
void entry_point(void); | N/A | No explicit test written to cover this rule. Test suite manifests are provided with tests partition entry_point. A successful launch and run of tests partition code indirectly verify this field. | N/A | Yes | +| NO_EXPLICIT_TEST | The SPM must eventually deliver all signals and IPC messages. | N/A | No explicit test written to cover this rule. However all PSA IPC 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.
This rule is unbounded and cannot have full coverage. It is good to that things are delivered in a timely manner, however failure will not break compliance. | N/A | Yes | +| NO_EXPLICIT_TEST | A Secure Partition is guaranteed to be able to execute and read its own code regions and read its own read-only data regions. | N/A | No explicit test written to cover this rule. This is a minimum requirement to able to launch and run secure partition. Failing to provide this, will not be able to run IPC test suite. | N/A | Yes | +| NOT_COVERED | psa_get returns PSA_ERROR_DOES_NOT_EXIST if the SPM cannot deliver a message to the Secure Partition following the assertion of the RoT Service signal. | N/A | This scenario cannot be simulated as test can't generate stimulus where psa_get() API returns PSA_ERROR_DOES_NOT_EXIST. However every instances of psa_get() API call in test suite checks the API return value and re-waits for signal delivery if return value is PSA_ERROR_DOES_NOT_EXIST. | N/A | No | +| NOT_COVERED | The call to psa_call is PROGRAMMER ERROR if the connection is already handling a request. | N/A | This rule is not verified beacuse of following reasons:
- There is no common infrastructure to test this rule as this needs two OS specific tasks where one task interrupted during execution of psa_call or psa_close and second task scheduled to execute psa_call using same handle.
- It is hard to write test stimulus where this rule is always hit as it is highly dependent of platform software scheduling policy.
| N/A | No | +| NOT_COVERED | The call to psa_close is PROGRAMMER ERROR if the connection is already handling a request | N/A | This rule is not verified beacuse of following reasons:
- There is no common infrastructure to test this rule as this needs two OS specific tasks where one task interrupted during execution of psa_call or psa_close and second task scheduled to execute psa_close using same handle.
- It is hard to write test stimulus where this rule is always hit as it is highly dependent of platform software scheduling policy.
| N/A | No | +| NOT_COVERED | Manifest Parameter - priority (required)
Partitions must be assigned one of the following priority groups:¿ HIGH¿ NORMAL¿ LOWLOW is the lowest priority. Priority is ignored by SPMs that do not implement any priority-based scheduling. | N/A | Use of these fields are highly dependent on type of SPM scheduling policy, hence can't test the behavious of this field. And test suite partition manifests are provided with priority field equal to LOW. | N/A | No | +| NOT_COVERED | Secure Partition IDs must be fixed across updates | N/A | Checking for Partition IDs across SPM updates is out of scope for PSA IPC tests. | N/A | No | ## License Arm PSA test suite is distributed under Apache v2.0 License. diff --git a/api-tests/docs/psa_its_testlist.md b/api-tests/docs/psa_its_testlist.md index 61a86d5b..f169577a 100644 --- a/api-tests/docs/psa_its_testlist.md +++ b/api-tests/docs/psa_its_testlist.md @@ -8,19 +8,19 @@ Following are the requirements of the Storage Test Suite.
2. Storage Test Cases use UID value starting from 1 onwards. These UID needs to be free for successfull test execution.
3. UID values 1 and 2 are reserved as WRITE_ONCE UID.These UID can't be free from testcase. Make sure these are free.
- -| Test | Return Value | API Verified | Test Algorithm | UID Usage | -|-----------|--------------------------------------|------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| test_s001 | PSA_ITS_ERROR_UID_NOT_FOUND | psa_its_get
psa_its_get_info
psa_its_remove
| 1. Call get API with UID for which no UID/Data pair is created
2. Call get_info API for which no UID/Data pair is created
3. Call remove API for which no UID/Data pair is created
4. Set valid UID/Data pair with uid1
5. Set one more set of UID/Data pair, with different uid, than previous
6. Remove the uid of step 4.
7. Call get API for removed UID/data pair
8. Call get_info API for removed UID/Data pair
9. Call remove API for removed UID/Data pair
10. Set valid UID/Data pair
11. Call get API for different uid , then created
12. Call get_info API for different uid, then created
13. Call remove API for different uid, then created
14. Remove the created UID/Data pair.
15. Remove the stray uid.
| UID value used are 5,6,7 | -| test_s002 | PSA_ITS_ERROR_WRITE_ONCE | psa_its_set
psa_its_remove
| 1. Set valid UID/data value pair , with create flag value none.2. Call get and get_info API to validate the data, attributes associated with data
3. Call set API again with same uid and create flag PSA_PS_WRITE_ONCE_FLAG
4. Call get and get_info API to validate the data, attributes associated with data is not changed after second set operation
5. try to remove the UID/data pair.
6. Create new UID/data value pair, with create flag PSA_PS_WRITE_ONCE_FLAG
7. Try to remove the created UID.
8. Call get and get_info API to validate the data, attributes associated with data
9. Again call SET with same UID , create flag PSA_PS_WRITE_ONCE_FLAG but different data length
10. Try to remove the UID, PSA_ITS_ERROR_WRITE_ONCE error should be returned
11. Call get and get_info API to validate the data, attributes associated with data
| UID value used are 1 and 2 | -| test_s003 | PSA_ITS_ERROR_INSUFFICIENT_SPACE | psa_its_set
| 1. Create UID/data pairs, with data_len 256 bytes. Do this with incrementing uid values till we have INSUFFICENT_SPACE.
2. Remove all the UID/data pairs created.
3. Repeat the steps once more, to check all previous uid are removed successfully
| UID value starts from 5 and keep on incrementing till all space is exhausted | -| test_s004 | PSA_ITS_SUCCESS | psa_its_set
psa_its_get
psa_its_get_info
psa_its_remove
| 1. Set a valid uid/data pair
2. Validate the data using get api
3. Change the data length to half of previous.
4. Call GET api with original data length , error should be returned and also the return buffer should be empty
5. Call GET api with correct data_len and validate the data received.
6. Check old data cannot be accessed.
7. Call REMOVE api to delete the UID/data pair
| UID value used is 5 | -| test_s005 | PSA_ITS_SUCCESS | psa_its_set
psa_its_get
psa_its_get_info
psa_its_remove
| 1. Set valid UID/data pair with varying uid and data_len
2. Call GET api and validate the set data
3. Call GET info api and validate the data attributes
4. Call REMOVE api to delete the UID/data pair
| UID value used are 4 | -| test_s006 | PSA_ITS_ERROR_FLAGS_NOT_SUPPORTED | psa_its_set
| 1. Call the SET_INFO with minimum flag value to max flag value
2. Call GET_INFO api and validate the flag value
3. Remove the uid/data pair
| UID value used is 5 | -| test_s007 | PSA_ITS_ERROR_INCORRECT_SIZE | psa_its_set
| 1. Create valid uid/data pair.
2. Increase the length of storage.
3. Try to access the old length using get api.
4. Try to access with valid length less than stored size.
5. Decrease the length of storage.
6. Try to access the old length.
7. Remove the uid
| UID value used is 5 | -| test_s008 | PSA_ITS_ERROR_OFFSET_INVALID | psa_its_get
| 1. Set valid UID/data pair
2. Call GET api with valid offset and offset + data_len equal to stored data size.
3. Call GET api with valid offset and offset + data_len less than stored data size.
4. Call get api with invalid offset.
5. Call get api with zero offset , but data len greater than data size.
6. Remove the uid.
| UID value used is 5 | -| test_s009 | PSA_ITS_ERROR_INVALID_ARGUMENTS | psa_its_get
psa_its_set
psa_its_get_info
| 1. Call the SET API with NULL pointer and data_len zero
2. Validate using get_info api storage should not be present.
3. Set storage entity with valid write_buffer , but length zero.
4. Again try to set for same uid with NULL write_buffer.
5. Call get and get_info api with NULL pointer and valid uid.
6. Remove the uid
| UID value used is 5
| -| test_s010 | PSA_ITS_ERROR_STORAGE_FAILURE
| psa_its_set
| 1. Call the SET API with UID value 0.
2. Check that storage creation fails.
| UID value used is 0
| +| Test | Test Scenario | API Verified | Return Value | Test Algorithm | UID Usage | +|-----------|--------------------------------------|-------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------| +| test_s001 | Get,get_info and remove API's call for
non-existent and removed assest | psa_its_get
psa_its_get_info
psa_its_remove
| PSA_ITS_ERROR_UID_NOT_FOUND | 1. Call get API with UID for which no UID/Data pair is created
2. Call get_info API for which no UID/Data pair is created
3. Call remove API for which no UID/Data pair is created
4. Set valid UID/Data pair with uid1
5. Set one more set of UID/Data pair, with different uid, than previous
6. Remove the uid of step 4.
7. Call get API for removed UID/data pair
8. Call get_info API for removed UID/Data pair
9. Call remove API for removed UID/Data pair
10. Set valid UID/Data pair
11. Call get API for different uid , then created
12. Call get_info API for different uid, then created
13. Call remove API for different uid, then created
14. Remove the created UID/Data pair.
15. Remove the stray uid.
| UID value used are 5,6,7 | +| test_s002 | Overwriting data for asset created with
WRITE_ONCE flag | psa_its_set
psa_its_remove
| PSA_ITS_ERROR_WRITE_ONCE | 1. Set valid UID/data value pair , with create flag value none.
2. Call get and get_info API to validate the data, attributes associated with data
3. Call set API again with same uid and create flag PSA_PS_WRITE_ONCE_FLAG
4. Call get and get_info API to validate the data, attributes associated with data
is not changed after second set operation
5. try to remove the UID/data pair.
6. Create new UID/data value pair, with create flag PSA_PS_WRITE_ONCE_FLAG
7. Try to remove the created UID.
8. Call get and get_info API to validate the data, attributes associated with data
9. Again call SET with same UID , create flag PSA_PS_WRITE_ONCE_FLAG but
different data length
10. Try to remove the UID, PSA_ITS_ERROR_WRITE_ONCE error should be returned
11. Call get and get_info API to validate the data, attributes associated with data
| UID value used are 1 and 2 | +| test_s003 | Exhaust storage space | psa_its_set
|PSA_ITS_ERROR_INSUFFICIENT_SPACE | 1. Create UID/data pairs, with data_len 256 bytes. Do this with incrementing
uid values till we have INSUFFICENT_SPACE.
2. Remove all the UID/data pairs created.
3. Repeat the steps once more, to check all previous uid are removed successfully
| UID value starts from 5 and keep on incrementing till all space is exhausted | +| test_s004 | Overwriting data for asset created without WRITE_ONCE flag | psa_its_set
psa_its_get
psa_its_get_info
psa_its_remove
| PSA_ITS_SUCCESS | 1. Set a valid uid/data pair
2. Validate the data using get api
3. Change the data length to half of previous.
4. Call GET api with original data length , error should be returned and also
the return buffer should be empty
5. Call GET api with correct data_len and validate the data received.
6. Check old data cannot be accessed.
7. Call REMOVE api to delete the UID/data pair
| UID value used is 5 | +| test_s005 | Get, get_info and remove API call for valid assest | psa_its_set
psa_its_get
psa_its_get_info
psa_its_remove
| PSA_ITS_SUCCESS |1. Set valid UID/data pair with varying uid and data_len
2. Call GET api and validate the set data
3. Call GET info api and validate the data attributes
4. Call REMOVE api to delete the UID/data pair
| UID value used are 4 | +| test_s006 | Storage asset creation with unsupported
create_flag value | psa_its_set
| PSA_ITS_ERROR_FLAGS_NOT_SUPPORTED | 1. Call the SET_INFO with minimum flag value to max flag value
2. Call GET_INFO api and validate the flag value
3. Remove the uid/data pair
| UID value used is 5 | +| test_s007 | Get API call with length different than asset
data length | psa_its_set
| PSA_ITS_ERROR_INCORRECT_SIZE | 1. Create valid uid/data pair.
2. Increase the length of storage.
3. Try to access the old length using get api.
4. Try to access with valid length less than stored size.
5. Decrease the length of storage.
6. Try to access the old length.
7. Remove the uid
| UID value used is 5 | +| test_s008 | Get API call with invalid offset | psa_its_get
|PSA_ITS_ERROR_OFFSET_INVALID | 1. Set valid UID/data pair
2. Call GET api with valid offset and offset + data_len equal to stored data size.
3. Call GET api with valid offset and offset + data_len less than stored data size.
4. Call get api with invalid offset.
5. Call get api with zero offset , but data len greater than data size.
6. Remove the uid.
| UID value used is 5 | +| test_s009 | API call with NULL pointer and zero length | psa_its_get
psa_its_set
psa_its_get_info
| PSA_ITS_SUCCESS | 1. Call the SET API with NULL pointer and data_len zero
2. Validate using get_info api storage should be present.
3. Call get API with NULL pointer.
4. Remove the UID.
5. Call get_info API to validate storage is removed.
6. Set storage entity with valid write_buffer , but length zero.
7. Call get_info API to validate storage attributes.
8. Call get_info api with NULL pointer and valid uid.
9. Remove the uid
| UID value used is 5
| +| test_s010 | Storage assest creation with UID value 0
| psa_its_set
| PSA_ITS_ERROR_INVALID_ARGUMENTS | 1. Call the SET API with UID value 0.
2. Check that storage creation fails.
| UID value used is 0
+| NA | Storage Failure
| NA
| PSA_ITS_ERROR_STORAGE_FAILURE
| 1. The failure cause will depend on the underlying
platform and vary for each implementation.
It is skipped in current suite
| NA
| ## License Arm PSA test suite is distributed under Apache v2.0 License. diff --git a/api-tests/docs/psa_ps_testlist.md b/api-tests/docs/psa_ps_testlist.md index 6e10a9c2..2675c7fc 100644 --- a/api-tests/docs/psa_ps_testlist.md +++ b/api-tests/docs/psa_ps_testlist.md @@ -9,23 +9,28 @@ Following are the requirements of the Storage Test Suite.
3. UID values 1 and 2 are reserved as WRITE_ONCE UID.These UID can't be free from testcase. Make sure these are free.
-| Test | Return Value | API Verified | Test Algorithm | UID Usage | -|-----------|--------------------------------------|------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| test_s001 | PSA_PS_ERROR_UID_NOT_FOUND | psa_ps_get
psa_ps_get_info
psa_ps_remove
| 1. Call get API with UID for which no UID/Data pair is created.
2. Call get_info API for which no UID/Data pair is created.
3. Call remove API for which no UID/Data pair is created.
4. Set valid UID/Data pair with uid1.
5. Set one more set of UID/Data pair, with different uid, than previous.
6. Remove the uid of step 4.
7. Call get API for removed UID/data pair.
8. Call get_info API for removed UID/Data pair.
9. Call remove API for removed UID/Data pair.
10. Set valid UID/Data pair.
11. Call get API for different uid , then created.
12. Call get_info API for different uid, then created.
13. Call remove API for different uid, then created.
14. Remove the created UID/Data pair.
15. Remove the stray uid.
| UID value used are 5, 6, 7 | -| test_s002 | PSA_PS_ERROR_WRITE_ONCE | psa_ps_set
psa_ps_remove
| 1. Set valid UID/data value pair , with create flag value none.
2. Call get and get_info API to validate the data, attributes associated with data.
3. Call set API again with same uid and create flag PSA_PS_WRITE_ONCE_FLAG.
4. Call get and get_info API to validate the data, attributes associated with data is not changed after second set operation
5. Try to remove the UID/data pair.
6. Create new UID/data value pair, with create flag PSA_PS_WRITE_ONCE_FLAG.
7. Try to remove the created UID.
8. Call get and get_info API to validate the data, attributes associated with data.
9. Again call SET with same UID , create flag PSA_PS_WRITE_ONCE_FLAG but different data length.
10. Try to remove the UID, PSA_PS_ERROR_WRITE_ONCE error should be returned.
11. Call get and get_info API to validate the data, attributes associated with data.
| UID value used are 1 and 2 | -| test_s003 | PSA_PS_ERROR_INSUFFICIENT_SPACE | psa_ps_set
| 1. Create UID/data pairs, with data_len 256 bytes. Do this with incrementing uid values till we have INSUFFICENT_SPACE.
2. Remove all the UID/data pairs created.
3. Repeat the steps once more, to check all previous uid are removed successfully.
| UID value starts from 5 and keep on incrementing till all space is exhausted | -| test_s004 | PSA_PS_SUCCESS | psa_ps_set
psa_ps_get
psa_ps_get_info
psa_ps_remove
| 1. Set a valid uid/data pair.
2. Validate the data using get api.
3. Change the data length to half of previous.
4. Call GET api with original data length , error should be returned and also the return buffer should be empty.
5. Call GET api with correct data_len and validate the data received.
6. Check old data cannot be accessed.
7. Call REMOVE api to delete the UID/data pair.
| UID value used is 5 | -| test_s005 | PSA_PS_SUCCESS | psa_ps_set
psa_ps_get
psa_ps_get_info
psa_ps_remove
| 1. Set valid UID/data pair with varying uid and data_len.
2. Call GET api and validate the set data.
3. Call GET info api and validate the data attributes.
4. Call REMOVE api to delete the UID/data pair.
| UID value used are 4 | -| test_s006 | PSA_PS_ERROR_FLAGS_NOT_SUPPORTED | psa_ps_set
| 1. Call the SET_INFO with minimum flag value to max flag value.
2. Call GET_INFO api and validae the flag value.
3. Remove the uid/data pair.
| UID value used is 5 | -| test_s007 | PSA_PS_ERROR_INCORRECT_SIZE | psa_ps_set
| 1. Create valid uid/data pair.
2. Increase the length of storage.
3. Try to access the old length using get api.
4. Try to access with valid length less than stored size.
5. Decrease the length of storage.
6. Try to access the old length.
7. Remove the uid.
| UID value used is 5 | -| test_s008 | PSA_PS_ERROR_OFFSET_INVALID | psa_ps_get
| 1. Set valid UID/data pair.
2. Call GET api with valid offset and offset + data_len equal to stored data size.
3. Call GET api with valid offset and offset + data_len less than stored data size.
4. Call get api with invalid offset.
5. Call get api with zero offset , but data len greater than data size.
6. Remove the uid.
| UID value used is 5 | -| test_s009 | PSA_PS_ERROR_INVALID_ARGUMENT | psa_ps_get
psa_ps_set
psa_ps_get_info
| 1. Call the SET API with NULL pointer and data_len zero.
2. Validate using get_info api storage should not be present.
3. Set storage entity with valid write_buffer , but length zero.
4. Again try to set for same uid with NULL write_buffer.
5. Call get and get_info api with NULL pointer and valid uid.
6. Remove the uid.
| UID value used is 5
| -| test_s010 | PSA_PS_ERROR_STORAGE_FAILURE
| psa_ps_set
| 1. Call the SET API with UID value 0.
2. Check that storage creation fails.
| UID value used is 0
| -| test_p011 | PSA_PS_ERROR_UID_NOT_FOUND | psa_ps_create
psa_ps_set_extended
| 1. Call the SET Extended API when no uid present.
2. Create a valid storage using set.
3. Call create api with different length for existing uid.
4. Call create api to set WRITE_ONCE flag.
5. Validate data attributes are maintained.
6. Remove the uid.
7. Create valid storage using create api.
8. Try to change length using create api.
9. Validate storage is empty.
10. Again call create api with original parameters.
11. Remove the uid.
12. Check no duplicate entry present.
| UID value used is 5
| -| test_p012 | PSA_PS_ERROR_INVALID_ARGUMENT
PSA_PS_ERROR_OFFSET_INVALID
| psa_ps_create
psa_ps_set_extended
| 1. Create a valid storage using set.
2. Set data on first half of buffer.
3. Try to set data at incorrect offset +length.
4. Try to set data at incorrect offset.
5. Try to set at correct offset but zero length buffer.
6. Try to set data at incorrect length and valid offset.
7. Overwrite the storage using set api.
8. Validate data is correctly written.
9. Call set_extended with NULL write buffer.
10. Overwrite storage using set_extended api.
11. Remove the uid.
| UID value used is 6
-| test_p013 | PSA_PS_SUCCESS | psa_ps_set_extended
| 1. Create Storage of zero length using create.
2. Try to set some data in the storage created.
3. Validate the storage attributes.
4. Remove the storage.
5. Create a valid storage with non-zero length.
6. Set data in the buffer.
7. Validate the data attributes.
8. Overwrite data using set api.
9. Validate the data.
10. Call create api for existing uid with same parameters.
11. Remove the uid.
12. Check with set_extended no duplicate uid exists.
| UID value used is 4 -| test_p014 | PSA_PS_ERROR_NOT_SUPPORTED | psa_ps_create
psa_ps_set_extended
| Below Steps will be run only if optional API are not supported.
1. Create API call should fail.
2. Check the UID should not exist.
3. Create storage using set API.
4. Try to partially write using set_extended API.
5. Validate data is not modified.
6. Remove the uid.
| UID value used is 5 -| test_p015 | PSA_PS_ERROR_FLAGS_NOT_SUPPORTED | psa_ps_create
| Below Step will be run only if optional API are supported.
1. Create API call with WRITE_ONCE flag should fail.
| UID value used is 5 +| Test | Test Scenario | API Verified | Return Value | Test Algorithm | UID Usage | +|-----------|--------------------------------------|------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------| +| test_s001 | Get,get_info and remove API's call for
non-existent and removed assest | psa_ps_get
psa_ps_get_info
psa_ps_remove
|PSA_PS_ERROR_UID_NOT_FOUND | 1. Call get API with UID for which no UID/Data pair is created.
2. Call get_info API for which no UID/Data pair is created.
3. Call remove API for which no UID/Data pair is created.
4. Set valid UID/Data pair with uid1.
5. Set one more set of UID/Data pair, with different uid, than previous.
6. Remove the uid of step 4.
7. Call get API for removed UID/data pair.
8. Call get_info API for removed UID/Data pair.
9. Call remove API for removed UID/Data pair.
10. Set valid UID/Data pair.
11. Call get API for different uid , then created.
12. Call get_info API for different uid, then created.
13. Call remove API for different uid, then created.
14. Remove the created UID/Data pair.
15. Remove the stray uid.
| UID value used are 5, 6, 7 | +| test_s002 | Overwriting data for asset created with
WRITE_ONCE flag | psa_ps_set
psa_ps_remove
| PSA_PS_ERROR_WRITE_ONCE | 1. Set valid UID/data value pair , with create flag value none.
2. Call get and get_info API to validate the data, attributes associated with data.
3. Call set API again with same uid and create flag PSA_PS_WRITE_ONCE_FLAG.
4. Call get and get_info API to validate the data, attributes associated with data is not changed after second set operation
5. Try to remove the UID/data pair.
6. Create new UID/data value pair, with create flag PSA_PS_WRITE_ONCE_FLAG.
7. Try to remove the created UID.
8. Call get and get_info API to validate the data, attributes associated with data.
9. Again call SET with same UID , create flag PSA_PS_WRITE_ONCE_FLAG but different data length.
10. Try to remove the UID, PSA_PS_ERROR_WRITE_ONCE error should be returned.
11. Call get and get_info API to validate the data, attributes associated with data.
| UID value used are 1 and 2 | +| test_s003 | Exhaust storage space | psa_ps_set
| PSA_PS_ERROR_INSUFFICIENT_SPACE | 1. Create UID/data pairs, with data_len 256 bytes. Do this with incrementing uid values till we have INSUFFICENT_SPACE.
2. Remove all the UID/data pairs created.
3. Repeat the steps once more, to check all previous uid are removed successfully.
| UID value starts from 5 and keep on incrementing till all space is exhausted | +| test_s004 | Overwriting data for asset created without
WRITE_ONCE flag | psa_ps_set
psa_ps_get
psa_ps_get_info
psa_ps_remove
| PSA_PS_SUCCESS | 1. Set a valid uid/data pair.
2. Validate the data using get api.
3. Change the data length to half of previous.
4. Call GET api with original data length , error should be returned and also the return buffer should be empty.
5. Call GET api with correct data_len and validate the data received.
6. Check old data cannot be accessed.
7. Call REMOVE api to delete the UID/data pair.
| UID value used is 5 | +| test_s005 | Get, get_info and remove API call for valid assest | psa_ps_set
psa_ps_get
psa_ps_get_info
psa_ps_remove
| PSA_PS_SUCCESS | 1. Set valid UID/data pair with varying uid and data_len.
2. Call GET api and validate the set data.
3. Call GET info api and validate the data attributes.
4. Call REMOVE api to delete the UID/data pair.
| UID value used are 4 | +| test_s006 | Storage asset creation with unsupported | psa_ps_set
| PSA_PS_ERROR_FLAGS_NOT_SUPPORTED | 1. Call the SET_INFO with minimum flag value to max flag value.
2. Call GET_INFO api and validae the flag value.
3. Remove the uid/data pair.
| UID value used is 5 | +| test_s007 | Get API call with invalid length | psa_ps_get
| PSA_PS_ERROR_INCORRECT_SIZE | 1. Create valid uid/data pair.
2. Increase the length of storage.
3. Try to access the old length using get api.
4. Try to access with valid length less than stored size.
5. Decrease the length of storage.
6. Try to access the old length.
7. Remove the uid.
| UID value used is 5 | +| test_s008 | Get API call with invalid offset | psa_ps_get
| PSA_PS_ERROR_INCORRECT_SIZE | 1. Set valid UID/data pair.
2. Call GET api with valid offset and offset + data_len equal to stored data size.
3. Call GET api with valid offset and offset + data_len less than stored data size.
4. Call get api with invalid offset.
5. Call get api with zero offset , but data len greater than data size.
6. Remove the uid.
| UID value used is 5 | +| test_s009 | API call with NULL pointer and zero length | psa_ps_get
psa_ps_set
psa_ps_get_info
| PSA_PS_SUCCESS | 1. Call the SET API with NULL pointer and data_len zero
2. Validate using get_info api storage should be present.
3. Call get API with NULL pointer.
4. Remove the UID.
5. Call get_info API to validate storage is removed.
6. Set storage entity with valid write_buffer , but length zero.
7. Call get_info API to validate storage attributes.
8. Call get_info api with NULL pointer and valid uid.
9. Remove the uid.
| UID value used is 5
| +| test_s010 | Storage assest creation with UID value 0
| psa_ps_set
| PSA_PS_ERROR_INVALID_ARGUMENTS
| 1. Call the SET API with UID value 0.
2. Check that storage creation fails.
| UID value used is 0
| +| test_p011 | Set_extended API's call for
non-existent and removed assest | psa_ps_set_extended
| PSA_PS_ERROR_UID_NOT_FOUND
| 1. Call the SET Extended API when no uid present.
2. Create a valid storage using set.
3. Call create api with different length for existing uid.
4. Call create api to set WRITE_ONCE flag.
5. Validate data attributes are maintained.
6. Remove the uid.
7. Create valid storage using create api.
8. Try to change length using create api.
9. Validate storage is empty.
10. Again call create api with original parameters.
11. Remove the uid.
12. Check no duplicate entry present.
| UID value used is 5
| +| test_p012 | Set_extended API's call
with invalid offset | psa_ps_set_extended
| PSA_PS_ERROR_OFFSET_INVALID
| 1. Create a valid storage using set.
2. Set data on first half of buffer.
3. Try to set data at incorrect offset +length.
4. Try to set data at incorrect offset.
5. Try to set at correct offset but zero length buffer.
6. Try to set data at incorrect length and valid offset.
7. Overwrite the storage using set api.
8. Validate data is correctly written.
9. Call set_extended with NULL write buffer.
10. Overwrite storage using set_extended api.
11. Remove the uid.
| UID value used is 6
+| test_p013 | Create and set_extended API call for valid assest | psa_ps_set_extended
psa_ps_create | PSA_PS_SUCCESS | 1. Create Storage of zero length using create.
2. Try to set some data in the storage created.
3. Validate the storage attributes.
4. Remove the storage.
5. Create a valid storage with non-zero length.
6. Set data in the buffer.
7. Validate the data attributes.
8. Overwrite data using set api.
9. Validate the data.
10. Call create api for existing uid with same parameters.
11. Remove the uid.
12. Check with set_extended no duplicate uid exists.
| UID value used is 4 +| test_p014 | Create and set_extended API call
when API's not supported | psa_ps_create
psa_ps_set_extended
| PSA_PS_ERROR_NOT_SUPPORTED | Below Steps will be run only if optional API are not supported.
1. Create API call should fail.
2. Check the UID should not exist.
3. Create storage using set API.
4. Try to partially write using set_extended API.
5. Validate data is not modified.
6. Remove the uid.
| UID value used is 5 +| test_p015 | Create API call with
WRITE_ONCE flag | psa_ps_create
|PSA_PS_ERROR_FLAGS_NOT_SUPPORTED | Below Step will be run only if optional API are supported.
1. Create API call with WRITE_ONCE flag should fail.
| UID value used is 5 +| NA | Storage Failure
| NA
| PSA_PS_ERROR_STORAGE_FAILURE
| 1. The failure cause will depend on the underlying
platform and vary for each implementation.
It is skipped in current suite
| NA
| +| NA | Operation Failure
| NA
| PSA_PS_ERROR_OPERATION_FAILED
| 1. The failure cause will depend on the underlying
platform and vary for each implementation.
It is skipped in current suite
| NA
| +| NA | Authentication Failure
| NA
| PSA_PS_ERROR_AUTH_FAILED
| 1. The failure cause will depend on the underlying
platform and vary for each implementation.
It is skipped in current suite
| NA
| +| NA | Data Corruption
| NA
| PSA_PS_ERROR_DATA_CORRUPT
| 1. The failure cause will depend on the underlying
platform and vary for each implementation.
It is skipped in current suite
| NA
| + ## License Arm PSA test suite is distributed under Apache v2.0 License. diff --git a/api-tests/docs/sw_requirements.md b/api-tests/docs/sw_requirements.md index 3150044d..7a747254 100644 --- a/api-tests/docs/sw_requirements.md +++ b/api-tests/docs/sw_requirements.md @@ -1,24 +1,23 @@ -# Architecture Test Suite Software requirements +# Architecture Test Suite Software Requirements -## 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 +- Scripting tools : Perl 5.12.3, Python 3.1.7 - Compiler toolchain : GNU Arm Embedded Toolchain 6.3.1, Arm Compiler v6.7 -*Note*: To compile Test Suite code, at least one of the above supported compiler toolchains - have to be available in the build environment. +**Note**: To compile the test suite code, at least one of the above supported compiler toolchains + must be available in the build environment. -### Setup a shell to enable compiler toolchain after installation +### Setting up a shell to enable compiler toolchain after installation -To import GNU Arm in your bash shell console: +To import GNU Arm in your bash shell console, execute: ~~~ export PATH=/bin:$PATH ~~~ -To import Arm Compiler in your bash shell console: +To import Arm Compiler in your bash shell console, execute: ~~~ export PATH=/bin:$PATH ~~~ @@ -30,15 +29,28 @@ To download the master branch of the repository, type the following command:
/nspe/initial_attestation/ext -cd ./platform/targets//nspe/initial_attestation/ext; git checkout 01168ef3f20e81d5db1ebd0cfa9a70055ee5b155 +cd ./platform/targets//nspe/initial_attestation/ext; git checkout da53227db1488dde0952bdff66c3d904dce270b3 +~~~ + +## To build on Cygwin(32-bit) + +To build test suite on Cygwin ensure all the above prerequisite in place. + +**Note**: Downloading the test suite in Window platform can have extra +newline chars than Unix. Therefore, it is recommended to execute the +following command to change the newline char format before running +any test suite command.
+ +~~~ +dos2unix ./tools/scripts/setup.sh ~~~ ## License diff --git a/api-tests/ff/README.md b/api-tests/ff/README.md index d24bef35..8c820865 100644 --- a/api-tests/ff/README.md +++ b/api-tests/ff/README.md @@ -1,43 +1,35 @@ -# PSA Firmware Framework : Architecture Test Suite +# PSA Firmware Framework Architecture Test Suite -## Introduction +## PSA Firmware Framework -### PSA Firmware Framework (PSA-FF) - -The PSA-FF defines a standard interface and framework, to isolate trusted functionality withinconstrained IoT devices. +*PSA Firmware Framework* (PSA-FF) defines a standard interface and framework to isolate trusted functionality within constrained IoT devices. The framework provides: - Architecture that describes isolated runtime environments (partitions) for trusted and untrusted firmware. - A standard model for describing the functionality and resources in each partition. -- A secure IPC interface to request services from other partitions. -- A model that describes how the partitions can interact with one another, as well as the hardware and the firmware framework implementation, itself. -- A standard interfaces for the PSA RoT Services. - -This specification enables the development of secure firmware functionality that can be reused on different devices that use any conforming implementation of the Firmware Framework. For more information, download the [PSA FF Specification](https://pages.arm.com/psa-resources-ff.html?_ga=2.97388575.1220230133.1540547473-1540784585.1540547382) - -### Architecture Test Suite +- A Secure IPC interface to request services from other partitions. +- A model that describes how the partitions can interact with one another, as well as the hardware and the firmware framework implementation itself. +- A standard interface for the PSA RoT services such as PSA RoT lifecycle service. -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. +This specification enables the development of Secure firmware functionality which can be reused on different devices that use any conforming implementation of the Firmware Framework. For more information, download the [PSA-FF Specification](https://pages.arm.com/psa-resources-ff.html?_ga=2.97388575.1220230133.1540547473-1540784585.1540547382). -The Architecture Test Suite contains the tests that are self-checking, portable C-based tests with directed stimulus. +### Architecture test suite -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. 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. +The architecture test suite is a set of examples of the invariant behaviors that are specified by the PSA-FF specification. Use this suite to verify whether these behaviors are implemented correctly in your system. This suite contains self-checking and 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. -For more information on Architecture Test Suite specification, refer the [Validation Methodology](../docs/Arm_PSA_APIs_Arch_Test_Validation_Methodology.pdf) document. +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. -## Tests Scenarios +For more information on architecture test suite specification, refer to the [Validation Methodology](../docs/Arm_PSA_APIs_Arch_Test_Validation_Methodology.pdf) document. -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. +## Tests 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.
+## Getting started -### Prerequisite - -Please make sure you have all required software installed as explained in the [software requirements](../docs/sw_requirements.md). +Follow the instructions in the subsequent sections to get a copy of the source code on your local machine and build the tests. Make sure you have all required software installed as explained in the [Software Requirements](../docs/sw_requirements.md). ### Porting steps @@ -45,64 +37,69 @@ Refer to the [PSA-FF Test Suite Porting Guide](../docs/porting_guide_ff.md) docu ### Build steps -To build test suite for a given platform, execute the following commands: +To build the test suite for your target platform, perform the following steps. -1. cd api-tests +1. Execute `cd api-tests`. -2. Using your secure partition build tool, parse following test suite partition manifest files and generate manifest output files. The manifest parsing tool must be compliant to manifest rules defined in the PSA FF specification.
-
Test suite manifest to be parsed:
- - platform/targets//manifests/common/driver_partition_psa.json - - platform/targets//manifests/ipc/client_partition_psa.json - - platform/targets//manifests/ipc/server_partition_psa.json +2. Using your Secure partition build tool, parse the following test suite partition manifest files and generate manifest output files. The manifest parsing tool must be compliant with the manifest rules defined in the PSA FF specification.
+
The test suite manifests to be parsed are:
+ - **platform/targets//manifests/common/driver_partition_psa.json** + - **platform/targets//manifests/ipc/client_partition_psa.json** + - **platform/targets//manifests/ipc/server_partition_psa.json** -3. Compile tests
+3. Compile the tests as shown below.
``` - ./tools/scripts/setup.sh --target --cpu_arch --suite --build --include + ./tools/scripts/setup.sh --target --cpu_arch --suite --build --include --archive_tests ```
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 test binaries should be compiled. 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. Note- To compile ipc tests, include path must point to path where "psa/client.h", "psa/service.h" and test partition manifest output files(``"psa_manifest/sid.h" and "psa_manifest/.h"``) are located in your build system.
-- is an output directory to keep build files. +- 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 the tests should be compiled. For example, Armv7M, Armv8M-Baseline and Armv8M-Mainline Architecture.
+- is the suite name which is the same as the suite name available in **ff/** directory.
+- is a directory to store the build output files.
+- is an additional directory to be included into the compiler search path.
+Note: To compile IPC tests, the include path must point to the path where **psa/client.h**, **psa/service.h**, **psa/lifecycle.h** and test partition manifest output files(**psa_manifest/sid.h**, **psa_manifest/pid.h** and **psa_manifest/.h**) are located in your build system.
+- Use **--archive_tests** option to create a combined test archive(test_combine.a) file by combining the available test objects files. Not using this option will create a combined test binary(test_elf_combine.bin) by combining the available test ELFs. -Refer ./tools/scripts/setup.sh --help to know more about options. +For more information about options, refer to **./tools/scripts/setup.sh --help**. -*To compile ipc tests for tgt_ff_mbedos_fvp_mps2_m4 platform* +To compile IPC tests for **tgt_ff_mbedos_fvp_mps2_m4** platform, execute the following commands: ``` cd api-tests -./tools/scripts/setup.sh --target tgt_ff_mbedos_fvp_mps2_m4 --cpu_arch armv7m --suite ipc --build BUILD_IPC --include --include +./tools/scripts/setup.sh --target tgt_ff_mbedos_fvp_mps2_m4 --cpu_arch armv7m --suite ipc --build BUILD_IPC --include --include --archive_tests ``` +**Note**: The default compilation flow includes the functional API tests to build the test suite. It does not include panic tests that check for the API's PROGRAMMER ERROR conditions as defined in the PSA-FF specification. You can include the panic tests for building the test suite just by passing **--include_panic_tests** option to script. ### Build output -Test suite build generates following binaries:
+The test suite build generates the following binaries:
NSPE libraries:
-1. /BUILD/val/val_nspe.a -2. /BUILD/platform/pal_nspe.a -3. /BUILD/ff//test_elf_combine.bin +1. **/BUILD/val/val_nspe.a** +2. **/BUILD/platform/pal_nspe.a** +3. **/BUILD/ff//test_combine.a** -SPE libraries:
-1. /BUILD/partition/driver_partition.a -2. /BUILD/partition/client_partition.a -3. /BUILD/partition/server_partition.a +SPE libraries explicitly for IPC test suite:
+1. **/BUILD/partition/driver_partition.a** +2. **/BUILD/partition/client_partition.a** +3. **/BUILD/partition/server_partition.a** -### Binaries integration into your platform +### Integrating the libraries into your target platform -1. Integrate test partition (SPE archives) with your software stack containing SPM so that partition code get access to PSA defined client and secure partition APIs. This forms a SPE binary. -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. 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 +1. Integrate the test partition (SPE archives) with your software stack containing SPM so that the partition code gets access to PSA-defined client and Secure partition APIs. This forms an SPE binary. +2. Integrate **val_nspe.a**, **pal_nspe.a** and **test_combine.a** libraries with your Non-secure OS so that these libraries get access to PSA client APIs. This forms an NSPE binary. +3. Load NSPE binary into Non-secure memory. +4. Load SPE binary into Secure memory. -## Test Suite Execution -The following steps describe the execution flow prior to the start of test execution:
+## Test suite execution +The following steps describe the execution flow before the 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 test suite partitions are ready to accept requests.
-3. On the non-secure side, the SUT - boot software would give control to the test suite entry point- *void val_entry(void);* as an application entry point.
+1. The target platform must load the above binaries into appropriate memory.
+2. The *System Under Test* (SUT) boots to an environment that initializes the SPM and the test suite partitions are ready to accept requests.
+3. On the Non-secure side, the SUT boot software gives control to the test suite 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.
+For details on test suite integration, refer to the **Integrating the test suite with the SUT** section of [Validation Methodology](../docs/Arm_PSA_APIs_Arch_Test_Validation_Methodology.pdf). + ## License Arm PSA test suite is distributed under Apache v2.0 License. @@ -118,4 +115,3 @@ Arm PSA test suite is distributed under Apache v2.0 License. -------------- *Copyright (c) 2018-2019, Arm Limited and Contributors. All rights reserved.* - diff --git a/api-tests/ff/ipc/test_i001/test_i001.c b/api-tests/ff/ipc/test_i001/test_i001.c index d1ecc470..80185ad5 100644 --- a/api-tests/ff/ipc/test_i001/test_i001.c +++ b/api-tests/ff/ipc/test_i001/test_i001.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i001.h" @@ -36,7 +36,7 @@ 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); + val->print(PRINT_TEST, "[Check 1] psa_framework_version\n", 0); /* Retrieve the version of the PSA Framework API that is implemented.*/ if (psa->framework_version() != PSA_FRAMEWORK_VERSION) @@ -54,7 +54,7 @@ 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); + val->print(PRINT_TEST, "[Check 2] 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 */ diff --git a/api-tests/ff/ipc/test_i001/test_supp_i001.c b/api-tests/ff/ipc/test_i001/test_supp_i001.c index ce6de385..2a33cfaa 100644 --- a/api-tests/ff/ipc/test_i001/test_supp_i001.c +++ b/api-tests/ff/ipc/test_i001/test_supp_i001.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_framework_version(void); int32_t server_test_psa_version(void); @@ -30,12 +35,12 @@ server_test_t test_i001_server_tests_list[] = { int32_t server_test_psa_framework_version(void) { - val_err_check_set(TEST_CHECKPOINT_NUM(201), VAL_STATUS_SUCCESS); + 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); + val->err_check_set(TEST_CHECKPOINT_NUM(202), VAL_STATUS_SUCCESS); return VAL_STATUS_SUCCESS; } diff --git a/api-tests/ff/ipc/test_i002/test_i002.c b/api-tests/ff/ipc/test_i002/test_i002.c index 705ee2b9..abd023f4 100644 --- a/api-tests/ff/ipc/test_i002/test_i002.c +++ b/api-tests/ff/ipc/test_i002/test_i002.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i002.h" @@ -43,27 +43,30 @@ 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); + val->print(PRINT_TEST, "[Check 1] 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. + /* + * The RoT Service can't make connection at this moment. It sends + * PSA_ERROR_CONNECTION_BUSY in psa_reply. */ - if (handle != PSA_CONNECTION_BUSY) + if (handle != PSA_ERROR_CONNECTION_BUSY) { - val->print(PRINT_ERROR, "Expected handle=PSA_CONNECTION_BUSY but handle=0x%x\n", handle); + val->print(PRINT_ERROR, + "Expected handle=PSA_ERROR_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 + * Expect PSA_ERROR_CONNECTION_REFUSED as return */ - if (handle != PSA_CONNECTION_REFUSED) + if (handle != PSA_ERROR_CONNECTION_REFUSED) { - val->print(PRINT_ERROR, "Expected handle=PSA_CONNECTION_REFUSED but handle=0x%x\n", handle); + val->print(PRINT_ERROR, + "Expected handle=PSA_ERROR_CONNECTION_REFUSED but handle=0x%x\n", handle); status = VAL_STATUS_INVALID_HANDLE; } @@ -74,7 +77,7 @@ 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); + val->print(PRINT_TEST, "[Check 2] Accept and close connection\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); /* RoT service accepts the connection. Expecting positive handle */ @@ -101,7 +104,7 @@ int32_t client_test_connect_with_allowed_minor_version_policy(security_t caller) 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); + val->print(PRINT_TEST, "[Check 3] 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: @@ -156,7 +159,7 @@ int32_t client_test_psa_call_with_allowed_status_code(security_t caller) 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); + val->print(PRINT_TEST, "[Check 4] Test psa_call with allowed status code\n", 0); for (i = 0; i < (sizeof(expected_status_code)/sizeof(expected_status_code[0])); i++) { @@ -179,7 +182,7 @@ int32_t client_test_identity(security_t caller) 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); + val->print(PRINT_TEST, "[Check 5] Test client_id\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); if (handle < 0) @@ -220,16 +223,16 @@ int32_t client_test_spm_concurrent_connect_limit(security_t caller) psa_handle_t handle[CONNECT_LIMIT] = {0}; int i= 0, signture = 0; - val->print(PRINT_TEST, "[Check6] Test connect limit\n", 0); + val->print(PRINT_TEST, "[Check 6] Test connect limit\n", 0); /* Execute psa_connect in a loop until it returns - * PSA_CONNECTION_REFUSED OR PSA_CONNECTION_BUSY + * PSA_ERROR_CONNECTION_REFUSED OR PSA_ERROR_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)) + if ((handle[i] == PSA_ERROR_CONNECTION_REFUSED) || (handle[i] == PSA_ERROR_CONNECTION_BUSY)) { signture = 1; break; @@ -264,7 +267,7 @@ int32_t client_test_psa_wait(void) for (i = 0; i < CONNECT_NUM; i++) { handle[i] = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); - if (handle[i] != PSA_CONNECTION_REFUSED) + if (handle[i] != PSA_ERROR_CONNECTION_REFUSED) { return VAL_STATUS_INVALID_HANDLE; } @@ -275,12 +278,12 @@ int32_t client_test_psa_wait(void) int32_t client_test_psa_block_behave(security_t caller) { - val->print(PRINT_TEST, "[Check7] Test PSA_BLOCK\n", 0); + val->print(PRINT_TEST, "[Check 7] Test PSA_BLOCK\n", 0); return (client_test_psa_wait()); } int32_t client_test_psa_poll_behave(security_t caller) { - val->print(PRINT_TEST, "[Check8] Test PSA_POLL\n", 0); + val->print(PRINT_TEST, "[Check 8] Test PSA_POLL\n", 0); return (client_test_psa_wait()); } diff --git a/api-tests/ff/ipc/test_i002/test_i002.h b/api-tests/ff/ipc/test_i002/test_i002.h index c4eb205b..a0767873 100644 --- a/api-tests/ff/ipc/test_i002/test_i002.h +++ b/api-tests/ff/ipc/test_i002/test_i002.h @@ -28,7 +28,7 @@ #define psa CONCAT(psa,_client_sp) #endif -#define CONNECT_LIMIT 20 +#define CONNECT_LIMIT 50 #define CONNECT_NUM 2 extern val_api_t *val; diff --git a/api-tests/ff/ipc/test_i002/test_supp_i002.c b/api-tests/ff/ipc/test_i002/test_supp_i002.c index 4358e240..274452b7 100644 --- a/api-tests/ff/ipc/test_i002/test_supp_i002.c +++ b/api-tests/ff/ipc/test_i002/test_supp_i002.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; #define CONNECT_NUM 2 @@ -47,28 +52,29 @@ int32_t server_test_connection_busy_and_reject(void) int32_t status = VAL_STATUS_SUCCESS; psa_msg_t msg = {0}; - /* Checks performed: - * psa_wait()- Returns > 0 when at least one signal is asserted + /* + * Checks performed: + * psa->wait()- 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)) + 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); + 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)) + 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_REFUSED); + /* Rejecting connection to check behaviour of PSA_ERROR_CONNECTION_REFUSED */ + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } @@ -77,30 +83,30 @@ 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)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } /* Accepting connection to check behaviour of PSA_SUCCESS */ - psa_reply(msg.handle, 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)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); /* Debug print for sanity check */ - val_err_check_set(TEST_CHECKPOINT_NUM(205), status); + val->err_check_set(TEST_CHECKPOINT_NUM(205), status); return status; } @@ -116,20 +122,20 @@ int32_t server_test_connect_with_allowed_minor_version_policy(void) for (i = 0; i < 4; i++) { - status = ((val_process_connect_request(signal[i], &msg)) + status = ((val->process_connect_request(signal[i], &msg)) ? VAL_STATUS_ERROR : status); - if (val_err_check_set(TEST_CHECKPOINT_NUM(206), status)) + if (val->err_check_set(TEST_CHECKPOINT_NUM(206), status)) { - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); - status = ((val_process_disconnect_request(signal[i], &msg)) + 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); + val->err_check_set(TEST_CHECKPOINT_NUM(207), status); + psa->reply(msg.handle, PSA_SUCCESS); } return status; } @@ -143,33 +149,33 @@ int32_t server_test_psa_call_with_allowed_status_code(void) for (i = 0; i < (sizeof(status_code)/sizeof(status_code[0])); i++) { - status = ((val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + 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)) + if (val->err_check_set(TEST_CHECKPOINT_NUM(208), status)) { - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + psa->reply(msg.handle, -3); } else { /* Send various status code available in status_code[] */ - psa_reply(msg.handle, status_code[i]); + psa->reply(msg.handle, status_code[i]); } - status = ((val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + 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); + psa->reply(msg.handle, PSA_SUCCESS); + val->err_check_set(TEST_CHECKPOINT_NUM(210), status); } return status; } @@ -180,38 +186,38 @@ int32_t server_test_identity(void) 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); + status = val->process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(211), status)) + if (val->err_check_set(TEST_CHECKPOINT_NUM(211), status)) { - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } /* Client ID during connect */ id_at_connect = msg.client_id; - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + 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); + 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); + 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); + val->print(PRINT_ERROR, "\tmsg.client_id failed for IPC_DISCONNECT", 0); status = VAL_STATUS_WRONG_IDENTITY; } @@ -223,10 +229,10 @@ int32_t server_test_identity(void) */ if ((msg.client_id > 0) && (msg.client_id != CLIENT_PARTITION)) { - val_print(PRINT_ERROR, "\tmsg.client_id failed for CLIENT_PARTITION", 0); + val->print(PRINT_ERROR, "\tmsg.client_id failed for CLIENT_PARTITION", 0); status = VAL_STATUS_WRONG_IDENTITY; } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); return status; } @@ -239,15 +245,15 @@ int32_t server_test_spm_concurrent_connect_limit(void) while (1) { - signals = psa_wait(PSA_WAIT_ANY, PSA_BLOCK); + signals = psa->wait(PSA_WAIT_ANY, PSA_BLOCK); if ((signals & SERVER_UNSPECIFED_MINOR_V_SIG) == 0) { - val_print(PRINT_ERROR, + val->print(PRINT_ERROR, "psa_wait returned with invalid signal value = 0x%x\n", signals); return VAL_STATUS_ERROR; } - if (psa_get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) + if (psa->get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) continue; switch(msg.type) @@ -255,12 +261,12 @@ int32_t server_test_spm_concurrent_connect_limit(void) case PSA_IPC_CONNECT: /* serve bulk connect cases to reach connect limit */ count++; - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); break; case PSA_IPC_DISCONNECT: /* serve bulk disconnect cases */ count--; - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); } if (count == 0) { @@ -277,39 +283,40 @@ int32_t server_test_psa_block_behave(void) psa_msg_t msg = {0}; int i = 0; - /* This is a sanity check - a successful handshaking between client and + /* + * This is a sanity check - a successful handshaking between client and * server for requested connection represents check pass. */ /* Debug print */ - val_err_check_set(TEST_CHECKPOINT_NUM(214), VAL_STATUS_SUCCESS); + val->err_check_set(TEST_CHECKPOINT_NUM(214), VAL_STATUS_SUCCESS); for (i = 0; i < CONNECT_NUM; i++) { wait: /* PSA_BLOCK ored with 0xFF to check timeout[30:0]=RES is ignored by implementation */ - signals = psa_wait(PSA_WAIT_ANY, PSA_BLOCK); + signals = psa->wait(PSA_WAIT_ANY, PSA_BLOCK); /* When MODE is one(PSA_BLOCK), the psa_wait must return non-zero signal value */ if ((signals & SERVER_UNSPECIFED_MINOR_V_SIG) == 0) { - val_print(PRINT_ERROR, + val->print(PRINT_ERROR, "psa_wait returned with invalid signal value = 0x%x\n", signals); return VAL_STATUS_ERROR; } else { - if (psa_get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) + if (psa->get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) { goto wait; } - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); } } /* Debug print */ - val_err_check_set(TEST_CHECKPOINT_NUM(215), VAL_STATUS_SUCCESS); + val->err_check_set(TEST_CHECKPOINT_NUM(215), VAL_STATUS_SUCCESS); return VAL_STATUS_SUCCESS; } @@ -323,37 +330,38 @@ int32_t server_test_psa_poll_behave(void) while (1) { /* Debug print */ - val_err_check_set(TEST_CHECKPOINT_NUM(216), VAL_STATUS_SUCCESS); + val->err_check_set(TEST_CHECKPOINT_NUM(216), VAL_STATUS_SUCCESS); /* Loop to receive client request */ while (signals == 0) { - signals = psa_wait(PSA_WAIT_ANY, PSA_POLL); + signals = psa->wait(PSA_WAIT_ANY, PSA_POLL); } - /* When MODE is zero(PSA_POLL), the psa_wait will return immediately with the current + /* + * When MODE is zero(PSA_POLL), the psa_wait 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(PSA_WAIT_ANY, PSA_POLL); + signals_temp = psa->wait(PSA_WAIT_ANY, PSA_POLL); if (signals_temp == 0) { - val_print(PRINT_ERROR, + val->print(PRINT_ERROR, "psa_wait 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, + val->print(PRINT_ERROR, "psa_wait returned with invalid signal value = 0x%x\n", signals); return VAL_STATUS_ERROR; } else { - if (psa_get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) + if (psa->get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) continue; - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); count++; signals = 0; } diff --git a/api-tests/ff/ipc/test_i003/test_i003.c b/api-tests/ff/ipc/test_i003/test_i003.c index 17b1bdb9..a6ac294c 100644 --- a/api-tests/ff/ipc/test_i003/test_i003.c +++ b/api-tests/ff/ipc/test_i003/test_i003.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,7 +21,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i003.h" @@ -43,7 +43,7 @@ int32_t client_test_zero_length_invec(security_t caller) psa_handle_t handle = 0; int data[4] = {0x11, 0x22}; - val->print(PRINT_TEST, "[Check1] Test zero length invec\n", 0); + val->print(PRINT_TEST, "[Check 1] Test zero length invec\n", 0); if (val->ipc_connect(SERVER_UNSPECIFED_MINOR_V_SID, 1, &handle)) { @@ -97,7 +97,7 @@ int32_t client_test_zero_length_outvec(security_t caller) psa_handle_t handle = 0; int data[4] = {0x11}; - val->print(PRINT_TEST, "[Check2] Test zero length outvec\n", 0); + val->print(PRINT_TEST, "[Check 2] Test zero length outvec\n", 0); if (val->ipc_connect(SERVER_UNSPECIFED_MINOR_V_SID, 1, &handle)) { @@ -155,7 +155,7 @@ int32_t client_test_call_read_and_skip(security_t caller) uint64_t data3 = 0x1020304050607080; psa_handle_t handle = 0; - val->print(PRINT_TEST, "[Check3] Test psa_write, psa_read and psa_skip\n", 0); + val->print(PRINT_TEST, "[Check 3] Test psa_write, psa_read and psa_skip\n", 0); if (val->ipc_connect(SERVER_UNSPECIFED_MINOR_V_SID, 1, &handle)) { @@ -192,7 +192,7 @@ int32_t client_test_call_and_write(security_t caller) 2}; psa_handle_t handle = 0; - val->print(PRINT_TEST, "[Check4] Test psa_call and psa_write\n", 0); + val->print(PRINT_TEST, "[Check 4] Test psa_call and psa_write\n", 0); if (val->ipc_connect(SERVER_UNSPECIFED_MINOR_V_SID, 1, &handle)) { @@ -248,7 +248,7 @@ int32_t client_test_psa_set_rhandle(security_t caller) psa_handle_t handle = 0; int i = 0; - val->print(PRINT_TEST, "[Check5] Test psa_set_rhandle API\n", 0); + val->print(PRINT_TEST, "[Check 5] 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)) @@ -281,7 +281,7 @@ int32_t client_test_overlapping_vectors(security_t caller) psa_handle_t handle = 0; uint8_t data = 0x11, expected_data[] = {0x22, 0x33}; - val->print(PRINT_TEST, "[Check6] Test overlapping vectors\n", 0); + val->print(PRINT_TEST, "[Check 6] Test overlapping vectors\n", 0); if (val->ipc_connect(SERVER_UNSPECIFED_MINOR_V_SID, 1, &handle)) { diff --git a/api-tests/ff/ipc/test_i003/test_supp_i003.c b/api-tests/ff/ipc/test_i003/test_supp_i003.c index cf4144fa..eddefced 100644 --- a/api-tests/ff/ipc/test_i003/test_supp_i003.c +++ b/api-tests/ff/ipc/test_i003/test_supp_i003.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_set_rhandle(void); int32_t server_test_call_read_and_skip(void); @@ -43,19 +48,19 @@ static void exit_graceful(psa_handle_t msg_handle, int status_code, 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); + 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); + psa->reply(msg_handle, status_code); - if (val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + if (val->process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) { - val_print(PRINT_ERROR, "\tdisconnect failed in exit_graceful func\n", 0); + val->print(PRINT_ERROR, "\tdisconnect failed in exit_graceful func\n", 0); } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); } int32_t server_test_zero_length_invec(void) @@ -64,14 +69,14 @@ int32_t server_test_zero_length_invec(void) psa_msg_t msg = {0}; int data[5] = {0}, actual_data = 0x22; - if (val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + if (val->process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) { - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return VAL_STATUS_CONNECTION_FAILED; } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); - if (val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + if (val->process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) { exit_graceful(msg.handle, -2, 0, 0, 0); return VAL_STATUS_CALL_FAILED; @@ -87,7 +92,7 @@ int32_t server_test_zero_length_invec(void) } if ((msg.in_size[2] <= sizeof(data[2])) && - (psa_read(msg.handle, 2, &data[2], msg.in_size[2]) != msg.in_size[2])) + (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; @@ -108,11 +113,11 @@ int32_t server_test_zero_length_invec(void) return VAL_STATUS_MSG_OUTSIZE_FAILED; } - psa_write(msg.handle, 0, &data[2], msg.out_size[0]); - psa_reply(msg.handle, PSA_SUCCESS); + 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); + status = val->process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + psa->reply(msg.handle, PSA_SUCCESS); return status; } @@ -122,14 +127,14 @@ int32_t server_test_zero_length_outvec(void) psa_msg_t msg={0}; int data[5] ={0}, actual_data=0x11; - if (val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + if (val->process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) { - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return VAL_STATUS_CONNECTION_FAILED; } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); - if (val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + if (val->process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) { exit_graceful(msg.handle, -2, 0, 0, 0); return VAL_STATUS_CALL_FAILED; @@ -145,7 +150,7 @@ int32_t server_test_zero_length_outvec(void) } if ((msg.in_size[0] <= sizeof(data[0])) && - (psa_read(msg.handle, 0, &data[0], msg.in_size[0]) != msg.in_size[0])) + (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; @@ -165,14 +170,14 @@ int32_t server_test_zero_length_outvec(void) 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]); + 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); + 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); + status = val->process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + psa->reply(msg.handle, PSA_SUCCESS); return status; } @@ -184,14 +189,14 @@ int32_t server_test_call_read_and_skip(void) actual_data[4] = {0}, i; psa_msg_t msg = {0}; - if (val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + if (val->process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) { - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return VAL_STATUS_CONNECTION_FAILED; } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); - if (val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + if (val->process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) { exit_graceful(msg.handle, -2, 0, 0, 0); return VAL_STATUS_CALL_FAILED; @@ -211,7 +216,7 @@ int32_t server_test_call_read_and_skip(void) for (i = 0; i < 2 ; i++) { if ((msg.in_size[i] <= sizeof(actual_data[i])) && - (psa_read(msg.handle, i, &actual_data[i], msg.in_size[i]) != msg.in_size[i])) + (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; @@ -224,7 +229,7 @@ int32_t server_test_call_read_and_skip(void) } /* Inbound read of 2 bytes from invec 2 */ - if (psa_read(msg.handle, 2, &actual_data[0], 2) != 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; @@ -236,14 +241,14 @@ int32_t server_test_call_read_and_skip(void) } /* Inbound read of 3 bytes from invec 2 */ - if (psa_skip(msg.handle, 2, 3) != 3) + 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) + if (psa->read(msg.handle, 2, &actual_data[0], 2) != 2) { exit_graceful(msg.handle, -9, 0, 0, 0); return VAL_STATUS_READ_FAILED; @@ -258,7 +263,7 @@ int32_t server_test_call_read_and_skip(void) /* 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) + if (psa->read(msg.handle, 2, &actual_data[0], 3) != 1) { exit_graceful(msg.handle, -11, 0, 0, 0); return VAL_STATUS_READ_FAILED; @@ -275,29 +280,29 @@ int32_t server_test_call_read_and_skip(void) /* 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)) + 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)) + 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) + 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); + 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]); @@ -305,12 +310,12 @@ int32_t server_test_call_read_and_skip(void) } /* Outbound skip to invec 3 */ - if (psa_skip(msg.handle, 3, 5) != 4) + 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) + if (psa->skip(msg.handle, 3, 5) != 0) { exit_graceful(msg.handle, -18, 0, 0, 0); return VAL_STATUS_SKIP_FAILED; @@ -325,10 +330,10 @@ int32_t server_test_call_read_and_skip(void) exit_graceful(msg.handle, -14, 0, 0, 0); return VAL_STATUS_MSG_OUTSIZE_FAILED; } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); - status = val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - 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; } @@ -338,14 +343,14 @@ int32_t server_test_call_and_write(void) 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)) + if (val->process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) { - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return VAL_STATUS_CONNECTION_FAILED; } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); - if (val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + if (val->process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) { exit_graceful(msg.handle, -2, 0, 0, 0); return VAL_STATUS_CALL_FAILED; @@ -374,20 +379,20 @@ int32_t server_test_call_and_write(void) for (i = 0; i < 3 ; i++) { - psa_write(msg.handle, i, &data[i], msg.out_size[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); + 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->write(msg.handle, 3, &data[3], 1); + psa->write(msg.handle, 3, &data[4], 1); - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); - status = val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - 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; } @@ -395,45 +400,45 @@ int32_t server_test_psa_set_rhandle(void) { int32_t status = VAL_STATUS_SUCCESS; psa_msg_t msg = {0}; - int *num = NULL; + int num; /*rhandle value check when PSA_IPC_CONNECT */ - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + 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)) + if (val->err_check_set(TEST_CHECKPOINT_NUM(201), status)) { - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); /*rhandle value check when PSA_IPC_CALL */ - status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + 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)) + 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); + 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->process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (*(int *)(msg.rhandle) != num) { status = VAL_STATUS_INVALID_HANDLE; } - if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + if (val->err_check_set(TEST_CHECKPOINT_NUM(203), status)) { exit_graceful(msg.handle, -3, 0, 0, 0); return status; @@ -443,37 +448,37 @@ int32_t server_test_psa_set_rhandle(void) * 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); + 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->process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (*(int *)(msg.rhandle) != num) { status = VAL_STATUS_INVALID_HANDLE; } - if (val_err_check_set(TEST_CHECKPOINT_NUM(204), status)) + 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); + 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->process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (*(int *)(msg.rhandle) != num) { status = VAL_STATUS_INVALID_HANDLE; } - val_err_check_set(TEST_CHECKPOINT_NUM(205), status); + 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); + 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); + val->err_check_set(TEST_CHECKPOINT_NUM(206), status); + psa->reply(msg.handle, PSA_SUCCESS); return status; } @@ -485,39 +490,39 @@ int32_t server_test_overlapping_vectors(void) 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)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + 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]); + 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); + 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); + 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/api-tests/ff/ipc/test_i004/test_i004.c b/api-tests/ff/ipc/test_i004/test_i004.c index 790e91b9..7f19bab2 100644 --- a/api-tests/ff/ipc/test_i004/test_i004.c +++ b/api-tests/ff/ipc/test_i004/test_i004.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i004.h" @@ -36,18 +36,28 @@ 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); + val->print(PRINT_TEST, "[Check 1] 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ /* Setting boot.state before test check */ @@ -58,10 +68,20 @@ int32_t client_test_sid_does_not_exists(security_t caller) return VAL_STATUS_ERROR; } - /* Test check - psa_connect with INVALID_SID, call must not return */ + /* Test check - psa_connect with INVALID_SID */ handle = psa->connect(INVALID_SID, 1); - /* Control shouldn't have reached here */ + /* + * If the caller is in the NSPE, it is IMPLEMENTATION DEFINED whether + * a PROGRAMMER ERROR will panic or return PSA_ERROR_CONNECTION_REFUSED. + * For SPE caller, it must panic. + */ + if (caller == NONSECURE && handle == PSA_ERROR_CONNECTION_REFUSED) + { + return VAL_STATUS_SUCCESS; + } + + /* If PROGRAMMER ERROR results into panic then 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 */ diff --git a/api-tests/ff/ipc/test_i004/test_supp_i004.c b/api-tests/ff/ipc/test_i004/test_supp_i004.c index 8d1abd28..c0f484a9 100644 --- a/api-tests/ff/ipc/test_i004/test_supp_i004.c +++ b/api-tests/ff/ipc/test_i004/test_supp_i004.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_sid_does_not_exists(); @@ -28,6 +33,6 @@ server_test_t test_i004_server_tests_list[] = { int32_t server_test_sid_does_not_exists() { - val_err_check_set(TEST_CHECKPOINT_NUM(201), VAL_STATUS_SUCCESS); + val->err_check_set(TEST_CHECKPOINT_NUM(201), VAL_STATUS_SUCCESS); return VAL_STATUS_SUCCESS; } diff --git a/api-tests/ff/ipc/test_i005/test_i005.c b/api-tests/ff/ipc/test_i005/test_i005.c index 8e02e21c..2b1d22f1 100644 --- a/api-tests/ff/ipc/test_i005/test_i005.c +++ b/api-tests/ff/ipc/test_i005/test_i005.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i005.h" @@ -36,18 +36,28 @@ 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); + val->print(PRINT_TEST, "[Check 1] 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ /* Setting boot.state before test check */ @@ -63,7 +73,17 @@ int32_t client_test_strict_policy_higher_minor_version(security_t caller) */ handle = psa->connect(SERVER_STRICT_MINOR_VERSION_SID, 3); - /* Shouldn't have reached here */ + /* + * If the caller is in the NSPE, it is IMPLEMENTATION DEFINED whether + * a PROGRAMMER ERROR will panic or return PSA_ERROR_CONNECTION_REFUSED. + * For SPE caller, it must panic. + */ + if (caller == NONSECURE && handle == PSA_ERROR_CONNECTION_REFUSED) + { + return VAL_STATUS_SUCCESS; + } + + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ val->print(PRINT_ERROR, "\tSTRICT policy with higher minor version should have failed but succeeded\n", 0); @@ -74,6 +94,5 @@ int32_t client_test_strict_policy_higher_minor_version(security_t caller) return VAL_STATUS_ERROR; } - (void)(handle); return VAL_STATUS_SPM_FAILED; } diff --git a/api-tests/ff/ipc/test_i005/test_supp_i005.c b/api-tests/ff/ipc/test_i005/test_supp_i005.c index b50c5489..7e819f81 100644 --- a/api-tests/ff/ipc/test_i005/test_supp_i005.c +++ b/api-tests/ff/ipc/test_i005/test_supp_i005.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_strict_policy_higher_minor_version(void); @@ -31,14 +36,21 @@ 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); + 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)) + if (val->err_check_set(TEST_CHECKPOINT_NUM(202), status)) { return VAL_STATUS_INVALID; } - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + + /* 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_ERROR_CONNECTION_REFUSED); return VAL_STATUS_INVALID; } diff --git a/api-tests/ff/ipc/test_i006/test_i006.c b/api-tests/ff/ipc/test_i006/test_i006.c index 6356b9c9..3f1f6eb8 100644 --- a/api-tests/ff/ipc/test_i006/test_i006.c +++ b/api-tests/ff/ipc/test_i006/test_i006.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i006.h" @@ -36,18 +36,28 @@ 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); + val->print(PRINT_TEST, "[Check 1] 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ /* Setting boot.state before test check */ @@ -58,10 +68,20 @@ int32_t client_test_strict_policy_lower_minor_version(security_t caller) return VAL_STATUS_ERROR; } - /*Version policy is strict and requested version is smaller than the minimum version */ + /* 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 */ + /* + * If the caller is in the NSPE, it is IMPLEMENTATION DEFINED whether + * a PROGRAMMER ERROR will panic or return PSA_ERROR_CONNECTION_REFUSED. + * For SPE caller, it must panic. + */ + if (caller == NONSECURE && handle == PSA_ERROR_CONNECTION_REFUSED) + { + return VAL_STATUS_SUCCESS; + } + + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ val->print(PRINT_ERROR, "\tSTRICT policy with lower minor version should have failed but succeeded\n", 0); @@ -72,6 +92,5 @@ int32_t client_test_strict_policy_lower_minor_version(security_t caller) return VAL_STATUS_ERROR; } - (void)(handle); return VAL_STATUS_SPM_FAILED; } diff --git a/api-tests/ff/ipc/test_i006/test_supp_i006.c b/api-tests/ff/ipc/test_i006/test_supp_i006.c index b1cd95e9..7831d065 100644 --- a/api-tests/ff/ipc/test_i006/test_supp_i006.c +++ b/api-tests/ff/ipc/test_i006/test_supp_i006.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_strict_policy_lower_minor_version(void); @@ -31,14 +36,21 @@ 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); + 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)) + if (val->err_check_set(TEST_CHECKPOINT_NUM(202), status)) { return VAL_STATUS_INVALID; } - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + + /* 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_ERROR_CONNECTION_REFUSED); return VAL_STATUS_INVALID; } diff --git a/api-tests/ff/ipc/test_i007/test_i007.c b/api-tests/ff/ipc/test_i007/test_i007.c index 518c13b3..c90dffca 100644 --- a/api-tests/ff/ipc/test_i007/test_i007.c +++ b/api-tests/ff/ipc/test_i007/test_i007.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i007.h" @@ -36,18 +36,28 @@ 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); + val->print(PRINT_TEST, "[Check 1] 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ /* Setting boot.state before test check */ @@ -58,10 +68,20 @@ int32_t client_test_relax_policy_higher_minor_version(security_t caller) return VAL_STATUS_ERROR; } - /*Version policy is relaxed and requested version is higher than the minimum version */ + /* 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 */ + /* + * If the caller is in the NSPE, it is IMPLEMENTATION DEFINED whether + * a PROGRAMMER ERROR will panic or return PSA_ERROR_CONNECTION_REFUSED. + * For SPE caller, it must panic. + */ + if (caller == NONSECURE && handle == PSA_ERROR_CONNECTION_REFUSED) + { + return VAL_STATUS_SUCCESS; + } + + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ val->print(PRINT_ERROR, "\tRELAXED policy with higher minor version should have failed but succeeded\n", 0); @@ -72,6 +92,5 @@ int32_t client_test_relax_policy_higher_minor_version(security_t caller) return VAL_STATUS_ERROR; } - (void)(handle); return VAL_STATUS_SPM_FAILED; } diff --git a/api-tests/ff/ipc/test_i007/test_supp_i007.c b/api-tests/ff/ipc/test_i007/test_supp_i007.c index dd703f21..e20d4254 100644 --- a/api-tests/ff/ipc/test_i007/test_supp_i007.c +++ b/api-tests/ff/ipc/test_i007/test_supp_i007.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_relax_policy_higher_minor_version(void); @@ -31,14 +36,21 @@ 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); + 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)) + if (val->err_check_set(TEST_CHECKPOINT_NUM(202), status)) { return VAL_STATUS_INVALID; } - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + + /* 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_ERROR_CONNECTION_REFUSED); return VAL_STATUS_INVALID; } diff --git a/api-tests/ff/ipc/test_i008/test_i008.c b/api-tests/ff/ipc/test_i008/test_i008.c index 18907547..785026e7 100644 --- a/api-tests/ff/ipc/test_i008/test_i008.c +++ b/api-tests/ff/ipc/test_i008/test_i008.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i008.h" @@ -36,38 +36,59 @@ 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); + val->print(PRINT_TEST, "[Check 1] 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - /* Setting boot.state before test check for NS*/ - if(caller == NONSECURE) + /* Setting boot.state before test check for NS */ + if (caller == NONSECURE) { status = val->set_boot_flag(BOOT_EXPECTED_NS); } - if(VAL_ERROR(status)) + 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 + + /* + * It is PROGRAMMER ERROR to connect to secure only access service from nspe. + * Whereas call should succeed if called from spe. */ handle = psa->connect(SERVER_SECURE_CONNECT_ONLY_SID, 1); - if(caller == NONSECURE) + if (caller == NONSECURE) { - /* Shouldn't have reached here for NS run*/ + /* + * If the caller is in the NSPE, it is IMPLEMENTATION DEFINED whether + * a PROGRAMMER ERROR will panic or return PSA_ERROR_CONNECTION_REFUSED. + */ + if (handle == PSA_ERROR_CONNECTION_REFUSED) + { + return VAL_STATUS_SUCCESS; + } + + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ val->print(PRINT_ERROR, "\tSecure access only connection test failed for NS run\n", 0); /* Resetting boot.state to catch unwanted reboot */ @@ -76,11 +97,11 @@ int32_t client_test_secure_access_only_connection(security_t caller) val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); return VAL_STATUS_ERROR; } - status = VAL_STATUS_SPM_FAILED; + return VAL_STATUS_SPM_FAILED; } /* Should return positive handle for SPE connection */ - if(handle > 0) + if (handle > 0) { psa->close(handle); } diff --git a/api-tests/ff/ipc/test_i008/test_supp_i008.c b/api-tests/ff/ipc/test_i008/test_supp_i008.c index 711621e5..22868d3e 100644 --- a/api-tests/ff/ipc/test_i008/test_supp_i008.c +++ b/api-tests/ff/ipc/test_i008/test_supp_i008.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_secure_access_only_connection(void); @@ -31,20 +36,38 @@ 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)) + 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); + psa->reply(msg.handle, PSA_ERROR_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)) + if (msg.client_id < 0) + { + /* + * Control shouldn't come here. NS client should get panic. + * 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_ERROR_CONNECTION_REFUSED); + return VAL_STATUS_SPM_FAILED; + } + else + { + 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); + psa->reply(msg.handle, PSA_SUCCESS); return status; } diff --git a/api-tests/ff/ipc/test_i009/test_i009.c b/api-tests/ff/ipc/test_i009/test_i009.c index aa05a798..9bb7c5b7 100644 --- a/api-tests/ff/ipc/test_i009/test_i009.c +++ b/api-tests/ff/ipc/test_i009/test_i009.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i009.h" @@ -35,18 +35,28 @@ 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); + val->print(PRINT_TEST, "[Check 1] 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ /* Setting boot.state before test check */ @@ -56,10 +66,14 @@ int32_t client_test_unextern_sid_connection(security_t caller) return VAL_STATUS_ERROR; } + /* + * If access between a client Secure Partition and an RoT Service is not specified in + * the manifest, then the client is not allowed to connect to the RoT Service. + */ 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); + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ + val->print(PRINT_ERROR, "\tunextern SID connection should have failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) diff --git a/api-tests/ff/ipc/test_i009/test_supp_i009.c b/api-tests/ff/ipc/test_i009/test_supp_i009.c index 864f39b8..a0f1ccaa 100644 --- a/api-tests/ff/ipc/test_i009/test_supp_i009.c +++ b/api-tests/ff/ipc/test_i009/test_supp_i009.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_unextern_sid_connection(void); @@ -31,14 +36,21 @@ 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); + 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)) + if (val->err_check_set(TEST_CHECKPOINT_NUM(202), status)) { return VAL_STATUS_INVALID; } - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + + /* 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_ERROR_CONNECTION_REFUSED); return VAL_STATUS_INVALID; } diff --git a/api-tests/ff/ipc/test_i010/test_i010.c b/api-tests/ff/ipc/test_i010/test_i010.c index 95994859..65b06c10 100644 --- a/api-tests/ff/ipc/test_i010/test_i010.c +++ b/api-tests/ff/ipc/test_i010/test_i010.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i010.h" @@ -37,18 +37,28 @@ int32_t client_test_unspecified_policy_with_higher_minor_ver(security_t caller) boot_state_t boot_state; val->print(PRINT_TEST, - "[Check1] Test un-specified minor_policy with higher minor version\n", 0); + "[Check 1] 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ /* Setting boot.state before test check */ @@ -59,9 +69,24 @@ int32_t client_test_unspecified_policy_with_higher_minor_ver(security_t caller) return VAL_STATUS_ERROR; } + /* + * The minor_version and minor_policy attributes do not need 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". + */ handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 3); - /* Shouldn't have reached here */ + /* + * If the caller is in the NSPE, it is IMPLEMENTATION DEFINED whether + * a PROGRAMMER ERROR will panic or return PSA_ERROR_CONNECTION_REFUSED. + * For SPE caller, it must panic. + */ + if (caller == NONSECURE && handle == PSA_ERROR_CONNECTION_REFUSED) + { + return VAL_STATUS_SUCCESS; + } + + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ val->print(PRINT_ERROR, "\tun-specified policy with higher minor version should have failed but succeeded\n", 0); @@ -72,6 +97,5 @@ int32_t client_test_unspecified_policy_with_higher_minor_ver(security_t caller) return VAL_STATUS_ERROR; } - (void)(handle); return VAL_STATUS_SPM_FAILED; } diff --git a/api-tests/ff/ipc/test_i010/test_supp_i010.c b/api-tests/ff/ipc/test_i010/test_supp_i010.c index 406760c3..6f8ffb8c 100644 --- a/api-tests/ff/ipc/test_i010/test_supp_i010.c +++ b/api-tests/ff/ipc/test_i010/test_supp_i010.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_unspecified_policy_with_higher_minor_ver(void); @@ -31,14 +36,20 @@ 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); + 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)) + if (val->err_check_set(TEST_CHECKPOINT_NUM(202), status)) { return VAL_STATUS_INVALID; } - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + + /* 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_ERROR_CONNECTION_REFUSED); return VAL_STATUS_INVALID; } diff --git a/api-tests/ff/ipc/test_i011/test_i011.c b/api-tests/ff/ipc/test_i011/test_i011.c index 6965daa4..82c5e09a 100644 --- a/api-tests/ff/ipc/test_i011/test_i011.c +++ b/api-tests/ff/ipc/test_i011/test_i011.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i011.h" @@ -37,18 +37,28 @@ int32_t client_test_unspecified_policy_with_lower_minor_ver(security_t caller) boot_state_t boot_state; val->print(PRINT_TEST, - "[Check1] Test un-specified minor_policy with higher minor version\n", 0); + "[Check 1] 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ /* Setting boot.state before test check */ @@ -59,9 +69,24 @@ int32_t client_test_unspecified_policy_with_lower_minor_ver(security_t caller) return VAL_STATUS_ERROR; } + /* + * The minor_version and minor_policy attributes do not need 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". + */ handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 0); - /* Shouldn't have reached here */ + /* + * If the caller is in the NSPE, it is IMPLEMENTATION DEFINED whether + * a PROGRAMMER ERROR will panic or return PSA_ERROR_CONNECTION_REFUSED. + * For SPE caller, it must panic. + */ + if (caller == NONSECURE && handle == PSA_ERROR_CONNECTION_REFUSED) + { + return VAL_STATUS_SUCCESS; + } + + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ val->print(PRINT_ERROR, "\tun-specified policy with lower minor version should have failed but succeeded\n", 0); @@ -72,6 +97,5 @@ int32_t client_test_unspecified_policy_with_lower_minor_ver(security_t caller) return VAL_STATUS_ERROR; } - (void)(handle); return VAL_STATUS_SPM_FAILED; } diff --git a/api-tests/ff/ipc/test_i011/test_supp_i011.c b/api-tests/ff/ipc/test_i011/test_supp_i011.c index a4a5e21b..87d4a4da 100644 --- a/api-tests/ff/ipc/test_i011/test_supp_i011.c +++ b/api-tests/ff/ipc/test_i011/test_supp_i011.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_unspecified_policy_with_lower_minor_ver(void); @@ -31,14 +36,21 @@ 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); + 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)) + if (val->err_check_set(TEST_CHECKPOINT_NUM(202), status)) { return VAL_STATUS_INVALID; } - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + + /* 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_ERROR_CONNECTION_REFUSED); return VAL_STATUS_INVALID; } diff --git a/api-tests/ff/ipc/test_i012/test_i012.c b/api-tests/ff/ipc/test_i012/test_i012.c index 1623d77c..9ddb2fa0 100644 --- a/api-tests/ff/ipc/test_i012/test_i012.c +++ b/api-tests/ff/ipc/test_i012/test_i012.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i012.h" @@ -35,18 +35,28 @@ 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); + val->print(PRINT_TEST, "[Check 1] 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ /* Setting boot.state before test check */ @@ -60,7 +70,17 @@ int32_t client_test_psa_close_with_invalid_handle(security_t caller) /* psa_close() with invalid handle */ psa->close(INVALID_HANDLE); - /* Shouldn't have reached here */ + /* + * If the caller is in the NSPE, it is IMPLEMENTATION DEFINED whether + * a PROGRAMMER ERROR will panic or return with no effect. + * For SPE caller, it must panic. + */ + if (caller == NONSECURE) + { + return VAL_STATUS_SUCCESS; + } + + /* If PROGRAMMER ERROR results into panic then control 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 */ diff --git a/api-tests/ff/ipc/test_i012/test_supp_i012.c b/api-tests/ff/ipc/test_i012/test_supp_i012.c index 104552ce..f6d39b60 100644 --- a/api-tests/ff/ipc/test_i012/test_supp_i012.c +++ b/api-tests/ff/ipc/test_i012/test_supp_i012.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_close_with_invalid_handle(void); @@ -28,6 +33,6 @@ server_test_t test_i012_server_tests_list[] = { int32_t server_test_psa_close_with_invalid_handle(void) { - val_err_check_set(TEST_CHECKPOINT_NUM(201), VAL_STATUS_SUCCESS); + val->err_check_set(TEST_CHECKPOINT_NUM(201), VAL_STATUS_SUCCESS); return VAL_STATUS_SUCCESS; } diff --git a/api-tests/ff/ipc/test_i013/test_i013.c b/api-tests/ff/ipc/test_i013/test_i013.c index f0088a87..5666c16a 100644 --- a/api-tests/ff/ipc/test_i013/test_i013.c +++ b/api-tests/ff/ipc/test_i013/test_i013.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i013.h" @@ -35,12 +35,12 @@ 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); + val->print(PRINT_TEST, "[Check 1] 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); + val->print(PRINT_ERROR, "\tConnection should failed but succeed\n", 0); (void)(handle); return VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i013/test_supp_i013.c b/api-tests/ff/ipc/test_i013/test_supp_i013.c index 6d3c3b69..fb54c3fe 100644 --- a/api-tests/ff/ipc/test_i013/test_supp_i013.c +++ b/api-tests/ff/ipc/test_i013/test_supp_i013.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_get_with_more_than_one_signal(void); @@ -29,49 +34,56 @@ server_test_t test_i013_server_tests_list[] = { 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - if ((psa_wait(PSA_WAIT_ANY, PSA_BLOCK)) & SERVER_UNSPECIFED_MINOR_V_SIG) + if ((psa->wait(PSA_WAIT_ANY, PSA_BLOCK)) & SERVER_UNSPECIFED_MINOR_V_SIG) { /* Setting boot.state before test check */ - if (val_set_boot_flag(BOOT_EXPECTED_NS)) + if (val->set_boot_flag(BOOT_EXPECTED_NS)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + 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) + if (psa->get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) { - val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + val->process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); } - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return VAL_STATUS_ERROR; } /* multiple signals check */ - psa_get((SERVER_UNSPECIFED_MINOR_V_SIG | SERVER_RELAX_MINOR_VERSION_SID), &msg); + psa->get((SERVER_UNSPECIFED_MINOR_V_SIG | SERVER_RELAX_MINOR_VERSION_SIG), &msg); - /* shouldn't have reached here */ + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); } - status = VAL_STATUS_SPM_FAILED; - val_err_check_set(TEST_CHECKPOINT_NUM(203), status); - - return status; + val->err_check_set(TEST_CHECKPOINT_NUM(203), VAL_STATUS_SPM_FAILED); + return VAL_STATUS_SPM_FAILED; } diff --git a/api-tests/ff/ipc/test_i014/test_i014.c b/api-tests/ff/ipc/test_i014/test_i014.c index 2fde3e25..1590dd48 100644 --- a/api-tests/ff/ipc/test_i014/test_i014.c +++ b/api-tests/ff/ipc/test_i014/test_i014.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i014.h" @@ -35,12 +35,12 @@ 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); + val->print(PRINT_TEST, "[Check 1] 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); + val->print(PRINT_ERROR, "\tConnection should failed but succeed\n", 0); (void)(handle); return VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i014/test_supp_i014.c b/api-tests/ff/ipc/test_i014/test_supp_i014.c index 61ed0e09..fe062f4b 100644 --- a/api-tests/ff/ipc/test_i014/test_supp_i014.c +++ b/api-tests/ff/ipc/test_i014/test_supp_i014.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_get_called_twice(void); @@ -31,51 +36,62 @@ 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ wait: - if ((psa_wait(PSA_WAIT_ANY, PSA_BLOCK)) & SERVER_UNSPECIFED_MINOR_V_SIG) + if ((psa->wait(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) + if (psa->get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) { goto wait; } + /* Debug print */ - val_err_check_set(TEST_CHECKPOINT_NUM(201), status); + val->err_check_set(TEST_CHECKPOINT_NUM(201), status); /* Setting boot.state before test check */ - if (val_set_boot_flag(BOOT_EXPECTED_NS)) + if (val->set_boot_flag(BOOT_EXPECTED_NS)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); /* Unblock client */ - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return VAL_STATUS_ERROR; } - /* Second psa_get call. This should not return */ - psa_get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + /* Second psa_get call. This should panic */ + psa->get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - /* shouldn't have reached here */ + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); } status = VAL_STATUS_SPM_FAILED; - val_err_check_set(TEST_CHECKPOINT_NUM(202), status); + val->err_check_set(TEST_CHECKPOINT_NUM(202), status); return status; } diff --git a/api-tests/ff/ipc/test_i015/test_i015.c b/api-tests/ff/ipc/test_i015/test_i015.c index 44a0887d..f4868658 100644 --- a/api-tests/ff/ipc/test_i015/test_i015.c +++ b/api-tests/ff/ipc/test_i015/test_i015.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i015.h" @@ -35,12 +35,12 @@ 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); + val->print(PRINT_TEST, "[Check 1] 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); + val->print(PRINT_ERROR, "\tConnection should failed but succeed\n", 0); (void)(handle); return VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i015/test_supp_i015.c b/api-tests/ff/ipc/test_i015/test_supp_i015.c index 6d49f8f6..a044796b 100644 --- a/api-tests/ff/ipc/test_i015/test_supp_i015.c +++ b/api-tests/ff/ipc/test_i015/test_supp_i015.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_get_with_non_rot_signal(void); @@ -31,44 +36,54 @@ 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - if ((psa_wait(PSA_WAIT_ANY, PSA_BLOCK)) & SERVER_UNSPECIFED_MINOR_V_SIG) + if ((psa->wait(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)) + 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); + 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_ERROR_CONNECTION_REFUSED); return status; } - /* Call to psa_get should not return */ - psa_get(PSA_DOORBELL, &msg); + /* Call to psa_get should panic */ + psa->get(PSA_DOORBELL, &msg); - /* shouldn't have reached here */ + /* If PROGRAMMER ERROR results into panic then control 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)) + 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); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); } status = VAL_STATUS_SPM_FAILED; - val_err_check_set(TEST_CHECKPOINT_NUM(203), status); + val->err_check_set(TEST_CHECKPOINT_NUM(203), status); return status; } diff --git a/api-tests/ff/ipc/test_i016/test_i016.c b/api-tests/ff/ipc/test_i016/test_i016.c index a99fa3fe..5cc7a362 100644 --- a/api-tests/ff/ipc/test_i016/test_i016.c +++ b/api-tests/ff/ipc/test_i016/test_i016.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i016.h" @@ -35,12 +35,12 @@ 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); + val->print(PRINT_TEST, "[Check 1] 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); + val->print(PRINT_ERROR, "\tConnection should failed but succeed\n", 0); (void)(handle); return VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i016/test_supp_i016.c b/api-tests/ff/ipc/test_i016/test_supp_i016.c index 83afe384..7b4d5e1b 100644 --- a/api-tests/ff/ipc/test_i016/test_supp_i016.c +++ b/api-tests/ff/ipc/test_i016/test_supp_i016.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_get_with_unasserted_signal(void); @@ -31,44 +36,54 @@ 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - if ((psa_wait(PSA_WAIT_ANY, PSA_BLOCK)) & SERVER_UNSPECIFED_MINOR_V_SIG) + if ((psa->wait(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)) + 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); + 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_ERROR_CONNECTION_REFUSED); return status; } - /* Unasserted signal check, call to psa_get should not return */ - psa_get(SERVER_RELAX_MINOR_VERSION_SID, &msg); + /* Unasserted signal check, call to psa_get should panic */ + psa->get(SERVER_RELAX_MINOR_VERSION_SIG, &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)) + 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); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); } status = VAL_STATUS_SPM_FAILED; - val_err_check_set(TEST_CHECKPOINT_NUM(203), status); + val->err_check_set(TEST_CHECKPOINT_NUM(203), status); return status; } diff --git a/api-tests/ff/ipc/test_i017/test_i017.c b/api-tests/ff/ipc/test_i017/test_i017.c index 5a0590d2..2253d56d 100644 --- a/api-tests/ff/ipc/test_i017/test_i017.c +++ b/api-tests/ff/ipc/test_i017/test_i017.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i017.h" @@ -35,12 +35,12 @@ 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); + val->print(PRINT_TEST, "[Check 1] 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); + val->print(PRINT_ERROR, "\tConnection should failed but succeed\n", 0); (void)(handle); return VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i017/test_supp_i017.c b/api-tests/ff/ipc/test_i017/test_supp_i017.c index 50082602..6c729770 100644 --- a/api-tests/ff/ipc/test_i017/test_supp_i017.c +++ b/api-tests/ff/ipc/test_i017/test_supp_i017.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_partition_calling_its_own_rot_service(void); @@ -32,51 +37,61 @@ int32_t server_test_partition_calling_its_own_rot_service(void) 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)) + 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); + psa->reply(msg.handle, PSA_ERROR_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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ /* Setting boot.state before test check */ - status = val_set_boot_flag(BOOT_EXPECTED_NS); - if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_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); + /* Calling server partition RoT service using IPC and call should panic */ + 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); + val->print(PRINT_ERROR, "\tConnection should failed but succeed\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)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } status = VAL_STATUS_SPM_FAILED; - val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + val->err_check_set(TEST_CHECKPOINT_NUM(204), status); (void)(handle); - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } diff --git a/api-tests/ff/ipc/test_i018/test_i018.c b/api-tests/ff/ipc/test_i018/test_i018.c index d23bcc98..43521492 100644 --- a/api-tests/ff/ipc/test_i018/test_i018.c +++ b/api-tests/ff/ipc/test_i018/test_i018.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i018.h" @@ -35,12 +35,12 @@ 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); + val->print(PRINT_TEST, "[Check 1] 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); + val->print(PRINT_ERROR, "\tConnection should failed but succeed\n", 0); (void)(handle); return VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i018/test_supp_i018.c b/api-tests/ff/ipc/test_i018/test_supp_i018.c index e951455f..5a67aba0 100644 --- a/api-tests/ff/ipc/test_i018/test_supp_i018.c +++ b/api-tests/ff/ipc/test_i018/test_supp_i018.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_set_rhandle_with_invalid_handle(void); @@ -30,54 +35,64 @@ 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; + int num; - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ /* Setting boot.state before test check */ - status = val_set_boot_flag(BOOT_EXPECTED_NS); - if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + 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); + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - /* Call psa_set_rhandle with INVALID_HANLDE. Call shouldn't return */ - *num = 5; - psa_set_rhandle(INVALID_HANDLE, num); + /* Call psa_set_rhandle with INVALID_HANLDE. Call should panic */ + 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); + val->print(PRINT_ERROR, + "\tpsa_set_rhandle with invalid handle should failed but succeed\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)) + 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); + 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); + val->err_check_set(TEST_CHECKPOINT_NUM(204), status); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } diff --git a/api-tests/ff/ipc/test_i019/test_i019.c b/api-tests/ff/ipc/test_i019/test_i019.c index 3050ba72..35ba8b93 100644 --- a/api-tests/ff/ipc/test_i019/test_i019.c +++ b/api-tests/ff/ipc/test_i019/test_i019.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i019.h" @@ -35,12 +35,12 @@ 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); + val->print(PRINT_TEST, "[Check 1] 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); + val->print(PRINT_ERROR, "\tConnection should failed but succeed\n", 0); (void)(handle); return VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i019/test_supp_i019.c b/api-tests/ff/ipc/test_i019/test_supp_i019.c index a45d891e..af063a61 100644 --- a/api-tests/ff/ipc/test_i019/test_supp_i019.c +++ b/api-tests/ff/ipc/test_i019/test_supp_i019.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_set_rhandle_with_null_handle(void); @@ -30,43 +35,64 @@ 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; + int num; + + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. + * + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + * + * To decide, a reboot happened as intended by 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. + */ - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_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)) + /* 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); + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa->reply(msg.handle, PSA_ERROR_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); + /* Call psa_set_rhandle with PSA_NULL_HANDLE. Call should panic */ + 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); + val->print(PRINT_ERROR, + "\tpsa_set_rhandle with NULL handle should failed but succeed\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)) + 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); + 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); + val->err_check_set(TEST_CHECKPOINT_NUM(204), status); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } diff --git a/api-tests/ff/ipc/test_i020/test_i020.c b/api-tests/ff/ipc/test_i020/test_i020.c index 31f3b508..c316e3d6 100644 --- a/api-tests/ff/ipc/test_i020/test_i020.c +++ b/api-tests/ff/ipc/test_i020/test_i020.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i020.h" @@ -37,12 +37,12 @@ int32_t client_test_psa_reply_with_invalid_connect_status_code(security_t caller psa_handle_t handle = 0; val->print(PRINT_TEST, - "[Check1] Test psa_reply with invalid status code for PSA_IPC_CONNECT\n", 0); + "[Check 1] 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); + val->print(PRINT_ERROR, "\tConnection should failed but succeed\n", 0); status = VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i020/test_supp_i020.c b/api-tests/ff/ipc/test_i020/test_supp_i020.c index 17014597..816fd66b 100644 --- a/api-tests/ff/ipc/test_i020/test_supp_i020.c +++ b/api-tests/ff/ipc/test_i020/test_supp_i020.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_reply_with_invalid_connect_status_code(void); @@ -30,51 +35,61 @@ 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; + psa_status_t invalid_status_code = PSA_ERROR_CONNECTION_REFUSED + 0x10; - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ /* Setting boot.state before test check */ - status = val_set_boot_flag(BOOT_EXPECTED_NS); - if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + 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); + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa->reply(msg.handle, PSA_ERROR_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); + /* Call psa_reply with invalid status code for PSA_IPC_CONNECT. Call should panic */ + 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); + val->print(PRINT_ERROR, + "\tpsa_reply with invalid status code for connect should failed but succeed\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)) + 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); + 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); + val->err_check_set(TEST_CHECKPOINT_NUM(204), status); return status; } diff --git a/api-tests/ff/ipc/test_i021/test_entry.c b/api-tests/ff/ipc/test_i021/test_entry.c index 45de605d..d8650820 100644 --- a/api-tests/ff/ipc/test_i021/test_entry.c +++ b/api-tests/ff/ipc/test_i021/test_entry.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #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" +#define TEST_DESC "Testing irq routing\n" TEST_PUBLISH(TEST_NUM, test_entry); val_api_t *val = NULL; psa_api_t *psa = NULL; diff --git a/api-tests/ff/ipc/test_i021/test_i021.c b/api-tests/ff/ipc/test_i021/test_i021.c index 42b611e4..8927b140 100644 --- a/api-tests/ff/ipc/test_i021/test_i021.c +++ b/api-tests/ff/ipc/test_i021/test_i021.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,42 +20,48 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i021.h" client_test_t test_i021_client_tests_list[] = { NULL, - client_test_psa_reply_with_invalid_call_status_code, + client_test_irq_routing, NULL, }; -int32_t client_test_psa_reply_with_invalid_call_status_code(security_t caller) +int32_t client_test_irq_routing(security_t caller) { - int32_t status = VAL_STATUS_SUCCESS; - psa_handle_t handle = 0; - psa_status_t status_of_call; + psa_handle_t handle; + driver_test_fn_id_t driver_test_fn_id = TEST_INTR_SERVICE; - val->print(PRINT_TEST, - "[Check1] Test psa_reply with invalid status code for PSA_IPC_CALL\n", 0); + /* + * The interrupt related test check is captured in driver_partition.c as this is the + * only partition in test suite that holds the interrupt line. The interrupt test check + * is invoked by client by calling to DRIVER_TEST_SID RoT service of driver partition that + * hold the test check. + */ - handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); + val->print(PRINT_TEST, "[Check 1] Test irq routing\n", 0); + /* Connect to DRIVER_TEST_SID */ + handle = psa->connect(DRIVER_TEST_SID, 1); if (handle < 0) { - val->print(PRINT_ERROR, "\tConnection failed\n", 0); - return VAL_STATUS_INVALID_HANDLE; + val->print(PRINT_ERROR, "\t psa_connect failed. handle=0x%x\n", handle); + return VAL_STATUS_SPM_FAILED; } - status_of_call = psa->call(handle, NULL, 0, NULL, 0); + /* Execute driver function related to TEST_INTR_SERVICE */ + psa_invec invec = {&driver_test_fn_id, sizeof(driver_test_fn_id)}; - /* 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; + if (psa->call(handle, &invec, 1, NULL, 0) != PSA_SUCCESS) + { + psa->close(handle); + return VAL_STATUS_SPM_FAILED; + } psa->close(handle); - (void)(status_of_call); - return status; + return VAL_STATUS_SUCCESS; } diff --git a/api-tests/ff/ipc/test_i021/test_i021.h b/api-tests/ff/ipc/test_i021/test_i021.h index 40025635..cc76cafa 100644 --- a/api-tests/ff/ipc/test_i021/test_i021.h +++ b/api-tests/ff/ipc/test_i021/test_i021.h @@ -33,5 +33,5 @@ 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); +int32_t client_test_irq_routing(security_t); #endif diff --git a/api-tests/ff/ipc/test_i021/test_supp_i021.c b/api-tests/ff/ipc/test_i021/test_supp_i021.c index 9e2ec16e..672f3408 100644 --- a/api-tests/ff/ipc/test_i021/test_supp_i021.c +++ b/api-tests/ff/ipc/test_i021/test_supp_i021.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,78 +15,23 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" -int32_t server_test_psa_reply_with_invalid_call_status_code(void); +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_irq_routing(void); server_test_t test_i021_server_tests_list[] = { NULL, - server_test_psa_reply_with_invalid_call_status_code, + server_test_irq_routing, NULL, }; -int32_t server_test_psa_reply_with_invalid_call_status_code(void) +int32_t server_test_irq_routing(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; + return VAL_STATUS_SUCCESS; } diff --git a/api-tests/ff/ipc/test_i022/test_i022.c b/api-tests/ff/ipc/test_i022/test_i022.c index 6fc5ac5a..09548369 100644 --- a/api-tests/ff/ipc/test_i022/test_i022.c +++ b/api-tests/ff/ipc/test_i022/test_i022.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i022.h" @@ -35,12 +35,12 @@ 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); + val->print(PRINT_TEST, "[Check 1] 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); + val->print(PRINT_ERROR, "\tConnection should failed but succeed\n", 0); (void)(handle); return VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i022/test_supp_i022.c b/api-tests/ff/ipc/test_i022/test_supp_i022.c index 6e4f43ae..e2ba3915 100644 --- a/api-tests/ff/ipc/test_i022/test_supp_i022.c +++ b/api-tests/ff/ipc/test_i022/test_supp_i022.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_reply_with_invalid_handle(void); @@ -31,50 +36,60 @@ 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_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)) + 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); + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - /*Call psa_reply with INVALID_HANLDE. Call shouldn't return*/ - psa_reply(INVALID_HANDLE, PSA_CONNECTION_REFUSED); + /* Call psa_reply with INVALID_HANLDE. Call should panic */ + psa->reply(INVALID_HANDLE, PSA_ERROR_CONNECTION_REFUSED); /* shouldn't have reached here */ - val_print(PRINT_ERROR, - "\tpsa_reply with invalid handle should failed but successed\n", 0); + val->print(PRINT_ERROR, + "\tpsa_reply with invalid handle should failed but succeed\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)) + 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); + 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); + val->err_check_set(TEST_CHECKPOINT_NUM(204), status); return status; } diff --git a/api-tests/ff/ipc/test_i023/test_i023.c b/api-tests/ff/ipc/test_i023/test_i023.c index 164552ab..6c85db25 100644 --- a/api-tests/ff/ipc/test_i023/test_i023.c +++ b/api-tests/ff/ipc/test_i023/test_i023.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i023.h" @@ -35,12 +35,12 @@ 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); + val->print(PRINT_TEST, "[Check 1] 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); + val->print(PRINT_ERROR, "\tConnection should failed but succeed\n", 0); (void)(handle); return VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i023/test_supp_i023.c b/api-tests/ff/ipc/test_i023/test_supp_i023.c index d28aa448..4436e4ac 100644 --- a/api-tests/ff/ipc/test_i023/test_supp_i023.c +++ b/api-tests/ff/ipc/test_i023/test_supp_i023.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_reply_with_null_handle(void); @@ -31,52 +36,62 @@ 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_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)) + 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); + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa->reply(msg.handle, PSA_ERROR_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); + /* Test check - Call psa_reply with PSA_NULL_HANDLE. Call should panic */ + psa->reply(PSA_NULL_HANDLE, PSA_ERROR_CONNECTION_REFUSED); /* shouldn't have reached here */ - val_print(PRINT_ERROR, - "\tpsa_reply with NULL handle should failed but successed\n", 0); + val->print(PRINT_ERROR, + "\tpsa_reply with NULL handle should failed but succeed\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)) + 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); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); return status; } - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); status = VAL_STATUS_SPM_FAILED; - val_err_check_set(TEST_CHECKPOINT_NUM(204), status); + val->err_check_set(TEST_CHECKPOINT_NUM(204), status); return status; } diff --git a/api-tests/ff/ipc/test_i024/test_i024.c b/api-tests/ff/ipc/test_i024/test_i024.c index b8ffaaba..d589c896 100644 --- a/api-tests/ff/ipc/test_i024/test_i024.c +++ b/api-tests/ff/ipc/test_i024/test_i024.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i024.h" @@ -33,34 +33,34 @@ client_test_t test_i024_client_tests_list[] = { 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); + "[Check 1] 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - 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)) @@ -72,8 +72,18 @@ int32_t client_test_psa_call_with_invalid_handle(security_t caller) /* 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); + /* + * If the caller is in the NSPE, it is IMPLEMENTATION DEFINED whether + * a PROGRAMMER ERROR will panic or return PSA_ERROR_PROGRAMMER_ERROR. + * For SPE caller, it must panic. + */ + if (caller == NONSECURE && status_of_call == PSA_ERROR_PROGRAMMER_ERROR) + { + return VAL_STATUS_SUCCESS; + } + + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ + val->print(PRINT_ERROR, "\tpsa_call should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) @@ -82,8 +92,5 @@ int32_t client_test_psa_call_with_invalid_handle(security_t caller) return VAL_STATUS_ERROR; } - status = VAL_STATUS_SPM_FAILED; - psa->close(handle); - (void)(status_of_call); - return status; + return VAL_STATUS_SPM_FAILED; } diff --git a/api-tests/ff/ipc/test_i024/test_supp_i024.c b/api-tests/ff/ipc/test_i024/test_supp_i024.c index d3bbbf13..60d98953 100644 --- a/api-tests/ff/ipc/test_i024/test_supp_i024.c +++ b/api-tests/ff/ipc/test_i024/test_supp_i024.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_call_with_invalid_handle(void); @@ -28,25 +33,7 @@ server_test_t test_i024_server_tests_list[] = { 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; + val->err_check_set(TEST_CHECKPOINT_NUM(201), VAL_STATUS_SUCCESS); + return VAL_STATUS_SUCCESS; } diff --git a/api-tests/ff/ipc/test_i025/test_i025.c b/api-tests/ff/ipc/test_i025/test_i025.c index 495247f7..66d69dd2 100644 --- a/api-tests/ff/ipc/test_i025/test_i025.c +++ b/api-tests/ff/ipc/test_i025/test_i025.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i025.h" @@ -33,34 +33,34 @@ client_test_t test_i025_client_tests_list[] = { 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); + "[Check 1] 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - 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)) @@ -72,8 +72,18 @@ int32_t client_test_psa_call_with_null_handle(security_t caller) /* 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); + /* + * If the caller is in the NSPE, it is IMPLEMENTATION DEFINED whether + * a PROGRAMMER ERROR will panic or return PSA_ERROR_PROGRAMMER_ERROR. + * For SPE caller, it must panic. + */ + if (caller == NONSECURE && status_of_call == PSA_ERROR_PROGRAMMER_ERROR) + { + return VAL_STATUS_SUCCESS; + } + + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ + val->print(PRINT_ERROR, "\tCall should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) @@ -82,8 +92,5 @@ int32_t client_test_psa_call_with_null_handle(security_t caller) return VAL_STATUS_ERROR; } - status = VAL_STATUS_SPM_FAILED; - psa->close(handle); - (void)(status_of_call); - return status; + return VAL_STATUS_SPM_FAILED; } diff --git a/api-tests/ff/ipc/test_i025/test_supp_i025.c b/api-tests/ff/ipc/test_i025/test_supp_i025.c index 036a2c9e..20f15573 100644 --- a/api-tests/ff/ipc/test_i025/test_supp_i025.c +++ b/api-tests/ff/ipc/test_i025/test_supp_i025.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_call_with_null_handle(void); @@ -28,25 +33,6 @@ server_test_t test_i025_server_tests_list[] = { 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; + val->err_check_set(TEST_CHECKPOINT_NUM(201), VAL_STATUS_SUCCESS); + return VAL_STATUS_SUCCESS; } diff --git a/api-tests/ff/ipc/test_i026/test_i026.c b/api-tests/ff/ipc/test_i026/test_i026.c index cedc4d67..bafd8a91 100644 --- a/api-tests/ff/ipc/test_i026/test_i026.c +++ b/api-tests/ff/ipc/test_i026/test_i026.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i026.h" @@ -46,18 +46,28 @@ int32_t client_test_psa_call_with_iovec_more_than_max_limit(security_t caller) psa_outvec outvec[1] = {{&data, sizeof(data)}}; val->print(PRINT_TEST, - "[Check1] Test psa_call with IOVEC > PSA_MAX_IOVEC\n", 0); + "[Check 1] 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); @@ -79,8 +89,19 @@ int32_t client_test_psa_call_with_iovec_more_than_max_limit(security_t caller) /* 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); + /* + * If the caller is in the NSPE, it is IMPLEMENTATION DEFINED whether + * a PROGRAMMER ERROR will panic or return PSA_ERROR_PROGRAMMER_ERROR. + * For SPE caller, it must panic. + */ + if (caller == NONSECURE && status_of_call == PSA_ERROR_PROGRAMMER_ERROR) + { + psa->close(handle); + return VAL_STATUS_SUCCESS; + } + + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ + val->print(PRINT_ERROR, "\tCall should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) @@ -91,6 +112,5 @@ int32_t client_test_psa_call_with_iovec_more_than_max_limit(security_t caller) status = VAL_STATUS_SPM_FAILED; psa->close(handle); - (void)(status_of_call); return status; } diff --git a/api-tests/ff/ipc/test_i026/test_i026.h b/api-tests/ff/ipc/test_i026/test_i026.h index 41d14dbd..183a060e 100644 --- a/api-tests/ff/ipc/test_i026/test_i026.h +++ b/api-tests/ff/ipc/test_i026/test_i026.h @@ -28,11 +28,6 @@ #define psa CONCAT(psa,_client_sp) #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; diff --git a/api-tests/ff/ipc/test_i026/test_supp_i026.c b/api-tests/ff/ipc/test_i026/test_supp_i026.c index 0e5be8f0..4b1794b7 100644 --- a/api-tests/ff/ipc/test_i026/test_supp_i026.c +++ b/api-tests/ff/ipc/test_i026/test_supp_i026.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_call_with_iovec_more_than_max_limit(); @@ -30,23 +35,45 @@ int32_t server_test_psa_call_with_iovec_more_than_max_limit() { int32_t status = VAL_STATUS_SUCCESS; psa_msg_t msg = {0}; + psa_signal_t signals; - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); - val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); +wait: + signals = psa->wait(PSA_WAIT_ANY, PSA_BLOCK); + if (signals & SERVER_UNSPECIFED_MINOR_V_SIG) + { + if (psa->get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) + { + goto wait; + } - /* Control shouldn't have come here */ - val_print(PRINT_ERROR, "\tControl shouldn't have reached here\n", 0); - psa_reply(msg.handle, -2); + if (msg.type == PSA_IPC_CALL) + { + /* 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); + } + else if (msg.type == PSA_IPC_DISCONNECT) + { + psa->reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_SUCCESS; + } + } + else + { + val->print(PRINT_ERROR, "\tpsa_wait returned with invalid signal value = 0x%x\n", signals); + return VAL_STATUS_ERROR; + } - val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - psa_reply(msg.handle, PSA_SUCCESS); - return VAL_STATUS_SPM_FAILED; + return VAL_STATUS_ERROR; } diff --git a/api-tests/ff/ipc/test_i027/test_entry.c b/api-tests/ff/ipc/test_i027/test_entry.c index 3f09f0b9..ed921299 100644 --- a/api-tests/ff/ipc/test_i027/test_entry.c +++ b/api-tests/ff/ipc/test_i027/test_entry.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "test_i027.h" #define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 27) -#define TEST_DESC "Testing PSA_DROP_CONNECTION\n" +#define TEST_DESC "Testing connection termination at PSA_IPC_CALL\n" TEST_PUBLISH(TEST_NUM, test_entry); val_api_t *val = NULL; psa_api_t *psa = NULL; diff --git a/api-tests/ff/ipc/test_i027/test_i027.c b/api-tests/ff/ipc/test_i027/test_i027.c index 2c69f795..1f20adc4 100644 --- a/api-tests/ff/ipc/test_i027/test_i027.c +++ b/api-tests/ff/ipc/test_i027/test_i027.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i027.h" @@ -39,31 +39,29 @@ int32_t client_test_psa_drop_connection(security_t caller) boot_state_t boot_state; val->print(PRINT_TEST, - "[Check1] Test PSA_DROP_CONNECTION\n", 0); + "[Check 1] Test connection termination at PSA_IPC_CALL\n", 0); - /* Test algorithm: + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * RoT service will terminate the connection by completing the message with a call - * to psa_reply() using the status code PSA_DROP_CONNECTION. + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. * - * 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 - */ + * To decide, a reboot happened as intended by 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. + */ handle = psa->connect(SERVER_CONNECTION_DROP_SID, 1); if (handle < 0) @@ -72,9 +70,7 @@ int32_t client_test_psa_drop_connection(security_t caller) return VAL_STATUS_INVALID_HANDLE; } - /* Setting boot flag to BOOT_EXPECTED_* to help error recovery if SPM behaviour - * matches with 2nd case - */ + /* Setting boot.state before test check */ boot_state = (caller == NONSECURE) ? BOOT_EXPECTED_NS : BOOT_EXPECTED_S; if (val->set_boot_flag(boot_state)) { @@ -84,36 +80,49 @@ int32_t client_test_psa_drop_connection(security_t caller) 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)) + /* + * If the caller is in the NSPE, it is IMPLEMENTATION DEFINED whether + * a PROGRAMMER ERROR will panic or return PSA_ERROR_PROGRAMMER_ERROR. + * For SPE caller, it must panic. + */ + if (caller == NONSECURE && status_of_call == PSA_ERROR_PROGRAMMER_ERROR) { - val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); - goto exit; - } + /* Resetting boot.state to catch unwanted reboot */ + if (val->set_boot_flag(BOOT_NOT_EXPECTED)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + return VAL_STATUS_ERROR; + } - /* Case 1 implementation */ - if (status_of_call == PSA_DROP_CONNECTION) - { - val->print(PRINT_DEBUG, "\tRecieved PSA_DROP_CONNECTION\n", 0); + val->print(PRINT_DEBUG, "\tRecieved PSA_ERROR_PROGRAMMER_ERROR\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 this call returns PSA_ERROR_PROGRAMMER_ERROR, + * when a valid connection handle was provided, then + * all subsequent calls to psa_call() with the same connection + * handle will immediately return PSA_ERROR_PROGRAMMER_ERROR. + */ + status_of_call = psa->call(handle, NULL, 0, NULL, 0); + if (status_of_call != PSA_ERROR_PROGRAMMER_ERROR) + { + status = VAL_STATUS_SPM_FAILED; + val->print(PRINT_ERROR, + "\tCall should have returned PSA_ERROR_PROGRAMMER_ERROR. Status = 0x%x\n", + status_of_call); + } + psa->close(handle); + return status; } - /* If implementation is case 2, control shouldn't have come here*/ - else + + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ + val->print(PRINT_ERROR, "\tCall should failed but succeed\n", 0); + + /* Resetting boot.state to catch unwanted reboot */ + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - /* 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); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + return VAL_STATUS_ERROR; } -exit: psa->close(handle); - return status; + return VAL_STATUS_SPM_FAILED; } diff --git a/api-tests/ff/ipc/test_i027/test_supp_i027.c b/api-tests/ff/ipc/test_i027/test_supp_i027.c index 4c1ac218..c2eb4d3c 100644 --- a/api-tests/ff/ipc/test_i027/test_supp_i027.c +++ b/api-tests/ff/ipc/test_i027/test_supp_i027.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_drop_connection(void); @@ -31,35 +36,37 @@ 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)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + psa->reply(msg.handle, -2); goto exit; } else { - psa_reply(msg.handle, PSA_DROP_CONNECTION); + psa->reply(msg.handle, PSA_ERROR_PROGRAMMER_ERROR); } 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)) + /* + * SPM must deliver a PSA_IPC_DISCONNECT message for the connection to the RoT Service + * directly after receipt of the PSA_ERROR_PROGRAMMER_ERROR 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); + val->print(PRINT_ERROR, "\tDisconnect request failed\n", 0); } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); return status; } diff --git a/api-tests/ff/ipc/test_i028/test_entry.c b/api-tests/ff/ipc/test_i028/test_entry.c index 6126f471..c259efb8 100644 --- a/api-tests/ff/ipc/test_i028/test_entry.c +++ b/api-tests/ff/ipc/test_i028/test_entry.c @@ -34,7 +34,7 @@ void test_entry(val_api_t *val_api, psa_api_t *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())) + if (!IS_TEST_START(val->get_status())) { goto test_exit; } diff --git a/api-tests/ff/ipc/test_i028/test_i028.c b/api-tests/ff/ipc/test_i028/test_i028.c index 0e3539fc..a5be9d35 100644 --- a/api-tests/ff/ipc/test_i028/test_i028.c +++ b/api-tests/ff/ipc/test_i028/test_i028.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i028.h" @@ -35,7 +35,7 @@ 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); + val->print(PRINT_TEST, "[Check 1] Test psa_read at PSA_IPC_CONNECT\n", 0); handle = psa->connect(SERVER_RELAX_MINOR_VERSION_SID, 1); diff --git a/api-tests/ff/ipc/test_i028/test_supp_i028.c b/api-tests/ff/ipc/test_i028/test_supp_i028.c index 88df3c59..3c571e3c 100644 --- a/api-tests/ff/ipc/test_i028/test_supp_i028.c +++ b/api-tests/ff/ipc/test_i028/test_supp_i028.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; #define NUM_OF_BYTES 4 @@ -34,45 +39,55 @@ int32_t server_test_psa_read_at_ipc_connect() 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_RELAX_MINOR_VERSION_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_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)) + 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); + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - /*psa_read at PSA_IPC_CONNECT */ - psa_read(msg.handle, 0, (void *)data, 0); + /* psa_read at PSA_IPC_CONNECT, call should panic */ + 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); + 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)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return VAL_STATUS_INVALID; } diff --git a/api-tests/ff/ipc/test_i029/test_i029.c b/api-tests/ff/ipc/test_i029/test_i029.c index 69f3c515..51d199bd 100644 --- a/api-tests/ff/ipc/test_i029/test_i029.c +++ b/api-tests/ff/ipc/test_i029/test_i029.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i029.h" @@ -35,17 +35,16 @@ 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); + val->print(PRINT_TEST, "[Check 1] 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); } + /* Shouldn't have reached here */ + val->print(PRINT_ERROR, "\tCheck for psa_read at PSA_IPC_DISCONNECT failed\n", 0); + return VAL_STATUS_SPM_FAILED; } diff --git a/api-tests/ff/ipc/test_i029/test_supp_i029.c b/api-tests/ff/ipc/test_i029/test_supp_i029.c index 1738b102..0e7aac6b 100644 --- a/api-tests/ff/ipc/test_i029/test_supp_i029.c +++ b/api-tests/ff/ipc/test_i029/test_supp_i029.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; #define NUM_OF_BYTES 4 @@ -34,52 +39,63 @@ int32_t server_test_psa_read_at_ipc_disconnect(void) 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_RELAX_MINOR_VERSION_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + 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)) + status = val->set_boot_flag(BOOT_EXPECTED_NS); + if (val->err_check_set(TEST_CHECKPOINT_NUM(203), status)) { - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); return status; } - /* Test check - psa_read at PSA_IPC_DISCONNECT */ - psa_read(msg.handle, 0, (void *)data, 0); + /* Test check - psa_read at PSA_IPC_DISCONNECT, call should panic */ + 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); + 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)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } - psa_reply(msg.handle, PSA_SUCCESS); + + psa->reply(msg.handle, PSA_SUCCESS); return VAL_STATUS_INVALID; } diff --git a/api-tests/ff/ipc/test_i030/test_i030.c b/api-tests/ff/ipc/test_i030/test_i030.c index 3a4a767b..f6eef922 100644 --- a/api-tests/ff/ipc/test_i030/test_i030.c +++ b/api-tests/ff/ipc/test_i030/test_i030.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i030.h" @@ -38,7 +38,7 @@ int32_t client_test_psa_read_with_null_handle(security_t caller) psa_status_t status_of_call; val->print(PRINT_TEST, - "[Check1] Test psa_read with NULL handle\n", 0); + "[Check 1] Test psa_read with NULL handle\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); @@ -51,7 +51,7 @@ int32_t client_test_psa_read_with_null_handle(security_t caller) 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); + val->print(PRINT_ERROR, "\tCall should failed but succeed\n", 0); status = VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i030/test_supp_i030.c b/api-tests/ff/ipc/test_i030/test_supp_i030.c index fb3afd7c..6c67a002 100644 --- a/api-tests/ff/ipc/test_i030/test_supp_i030.c +++ b/api-tests/ff/ipc/test_i030/test_supp_i030.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; #define NUM_OF_BYTES 4 @@ -34,64 +39,74 @@ int32_t server_test_psa_read_with_null_handle(void) 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + 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)) + 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); + 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); + /* Test check- psa_read with PSA_NULL_HANDLE, call should panic */ + 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); + val->print(PRINT_ERROR, + "\tpsa_read with NULL handle should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } status = VAL_STATUS_SPM_FAILED; - psa_reply(msg.handle, -4); + 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->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); + psa->reply(msg.handle, PSA_SUCCESS); return status; } diff --git a/api-tests/ff/ipc/test_i031/test_i031.c b/api-tests/ff/ipc/test_i031/test_i031.c index ffa5a7c4..dc221519 100644 --- a/api-tests/ff/ipc/test_i031/test_i031.c +++ b/api-tests/ff/ipc/test_i031/test_i031.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i031.h" @@ -38,7 +38,7 @@ int32_t client_test_psa_read_with_invalid_handle(security_t caller) psa_status_t status_of_call; val->print(PRINT_TEST, - "[Check1] Test psa_read with invalid handle\n", 0); + "[Check 1] Test psa_read with invalid handle\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); if (handle < 0) @@ -50,7 +50,7 @@ int32_t client_test_psa_read_with_invalid_handle(security_t caller) 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); + val->print(PRINT_ERROR, "\tCall should failed but succeed\n", 0); status = VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i031/test_supp_i031.c b/api-tests/ff/ipc/test_i031/test_supp_i031.c index 3b41ba16..9bac47af 100644 --- a/api-tests/ff/ipc/test_i031/test_supp_i031.c +++ b/api-tests/ff/ipc/test_i031/test_supp_i031.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; #define NUM_OF_BYTES 4 @@ -34,64 +39,74 @@ int32_t server_test_psa_read_with_invalid_handle(void) 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + 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)) + 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); + 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); + /* Test check- psa_read with INVALID_HANDLE, call should panic */ + 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); + val->print(PRINT_ERROR, + "\tpsa_read with invalid handle should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } status = VAL_STATUS_SPM_FAILED; - psa_reply(msg.handle, -4); + 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->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); + psa->reply(msg.handle, PSA_SUCCESS); return status; } diff --git a/api-tests/ff/ipc/test_i032/test_i032.c b/api-tests/ff/ipc/test_i032/test_i032.c index 712254bd..f0527833 100644 --- a/api-tests/ff/ipc/test_i032/test_i032.c +++ b/api-tests/ff/ipc/test_i032/test_i032.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i032.h" @@ -38,7 +38,7 @@ int32_t client_test_psa_read_with_invec_equal_to_max_iovec(security_t caller) psa_status_t status_of_call; val->print(PRINT_TEST, - "[Check1] Test psa_read with invec_idx=PSA_MAX_IOVEC\n", 0); + "[Check 1] Test psa_read with invec_idx=PSA_MAX_IOVEC\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); if (handle < 0) @@ -50,7 +50,7 @@ int32_t client_test_psa_read_with_invec_equal_to_max_iovec(security_t caller) 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); + val->print(PRINT_ERROR, "\tCall should failed but succeed\n", 0); status = VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i032/test_supp_i032.c b/api-tests/ff/ipc/test_i032/test_supp_i032.c index 941615c3..e45d47b9 100644 --- a/api-tests/ff/ipc/test_i032/test_supp_i032.c +++ b/api-tests/ff/ipc/test_i032/test_supp_i032.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; #define NUM_OF_BYTES 4 @@ -34,64 +39,74 @@ int32_t server_test_psa_read_with_invec_equal_to_max_iovec(void) 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + 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)) + 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); + 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); + /* Test check- psa_read with invec_idx=PSA_MAX_IOVEC, call should panic */ + 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); + val->print(PRINT_ERROR, + "\tpsa_read with invec_idx=PSA_MAX_IOVEC should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } status = VAL_STATUS_SPM_FAILED; - psa_reply(msg.handle, -4); + 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->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); + psa->reply(msg.handle, PSA_SUCCESS); return status; } diff --git a/api-tests/ff/ipc/test_i033/test_i033.c b/api-tests/ff/ipc/test_i033/test_i033.c index 68e35bcf..1792dfa7 100644 --- a/api-tests/ff/ipc/test_i033/test_i033.c +++ b/api-tests/ff/ipc/test_i033/test_i033.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i033.h" @@ -38,7 +38,7 @@ int32_t client_test_psa_read_with_invec_greater_than_max_iovec(security_t caller psa_status_t status_of_call; val->print(PRINT_TEST, - "[Check1] Test psa_read with invec_idx > PSA_MAX_IOVEC\n", 0); + "[Check 1] Test psa_read with invec_idx > PSA_MAX_IOVEC\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); if (handle < 0) @@ -50,7 +50,7 @@ int32_t client_test_psa_read_with_invec_greater_than_max_iovec(security_t caller 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); + val->print(PRINT_ERROR, "\tCall should failed but succeed\n", 0); status = VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i033/test_supp_i033.c b/api-tests/ff/ipc/test_i033/test_supp_i033.c index 70f9308b..425c255a 100644 --- a/api-tests/ff/ipc/test_i033/test_supp_i033.c +++ b/api-tests/ff/ipc/test_i033/test_supp_i033.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; #define NUM_OF_BYTES 4 @@ -34,64 +39,74 @@ int32_t server_test_psa_read_with_invec_greater_than_max_iovec(void) 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + 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)) + 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); + 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); + /* Test check- psa_read with invec_idx > PSA_MAX_IOVEC, call should panic */ + 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); + val->print(PRINT_ERROR, + "\tpsa_read with invec_idx > PSA_MAX_IOVEC should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } status = VAL_STATUS_SPM_FAILED; - psa_reply(msg.handle, -4); + 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->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); + psa->reply(msg.handle, PSA_SUCCESS); return status; } diff --git a/api-tests/ff/ipc/test_i034/test_i034.c b/api-tests/ff/ipc/test_i034/test_i034.c index d57057b6..742c1aa0 100644 --- a/api-tests/ff/ipc/test_i034/test_i034.c +++ b/api-tests/ff/ipc/test_i034/test_i034.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i034.h" @@ -35,7 +35,7 @@ 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); + val->print(PRINT_TEST, "[Check 1] Test psa_skip at PSA_IPC_CONNECT\n", 0); handle = psa->connect(SERVER_RELAX_MINOR_VERSION_SID, 1); diff --git a/api-tests/ff/ipc/test_i034/test_supp_i034.c b/api-tests/ff/ipc/test_i034/test_supp_i034.c index 6f98c12d..2580d99f 100644 --- a/api-tests/ff/ipc/test_i034/test_supp_i034.c +++ b/api-tests/ff/ipc/test_i034/test_supp_i034.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_skip_at_ipc_connect(void); @@ -31,46 +36,56 @@ 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_RELAX_MINOR_VERSION_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_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)) + 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); + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - /* Test check- psa_skip at PSA_IPC_CONNECT */ - psa_skip(msg.handle, 0, 0); + /* Test check- psa_skip at PSA_IPC_CONNECT, call should panic */ + psa->skip(msg.handle, 0, 0); /* Shouldn't have reached here */ - val_print(PRINT_ERROR,"\tpsa_skip should failed but successed\n", 0); + val->print(PRINT_ERROR,"\tpsa_skip should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return VAL_STATUS_INVALID; } diff --git a/api-tests/ff/ipc/test_i035/test_i035.c b/api-tests/ff/ipc/test_i035/test_i035.c index 401d2929..ca89abef 100644 --- a/api-tests/ff/ipc/test_i035/test_i035.c +++ b/api-tests/ff/ipc/test_i035/test_i035.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i035.h" @@ -35,7 +35,7 @@ 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); + val->print(PRINT_TEST, "[Check 1] Test psa_skip at PSA_IPC_DISCONNECT\n", 0); handle = psa->connect(SERVER_RELAX_MINOR_VERSION_SID, 1); if (handle > 0) diff --git a/api-tests/ff/ipc/test_i035/test_supp_i035.c b/api-tests/ff/ipc/test_i035/test_supp_i035.c index 1bec82bf..a3551488 100644 --- a/api-tests/ff/ipc/test_i035/test_supp_i035.c +++ b/api-tests/ff/ipc/test_i035/test_supp_i035.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_skip_at_ipc_disconnect(); @@ -31,52 +36,62 @@ 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_RELAX_MINOR_VERSION_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + 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)) + 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); + 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); + /* Test check- psa_skip at PSA_IPC_DISCONNECT, call should panic */ + psa->skip(msg.handle, 0, 0); /* Shouldn't have reached here */ - val_print(PRINT_ERROR,"\tpsa_skip should failed but successed\n", 0); + val->print(PRINT_ERROR,"\tpsa_skip should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); return VAL_STATUS_INVALID; } diff --git a/api-tests/ff/ipc/test_i036/test_i036.c b/api-tests/ff/ipc/test_i036/test_i036.c index 0daf6352..75eea227 100644 --- a/api-tests/ff/ipc/test_i036/test_i036.c +++ b/api-tests/ff/ipc/test_i036/test_i036.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i036.h" @@ -38,7 +38,7 @@ int32_t client_test_psa_skip_with_null_handle(security_t caller) psa_status_t status_of_call; val->print(PRINT_TEST, - "[Check1] Test psa_skip with NULL handle\n", 0); + "[Check 1] Test psa_skip with NULL handle\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); if (handle < 0) @@ -50,7 +50,7 @@ int32_t client_test_psa_skip_with_null_handle(security_t caller) 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); + val->print(PRINT_ERROR, "\tCall should failed but succeed\n", 0); status = VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i036/test_supp_i036.c b/api-tests/ff/ipc/test_i036/test_supp_i036.c index e7039959..b61bbfe7 100644 --- a/api-tests/ff/ipc/test_i036/test_supp_i036.c +++ b/api-tests/ff/ipc/test_i036/test_supp_i036.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_skip_with_null_handle(void); @@ -31,64 +36,74 @@ 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if(val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + 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)) + 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); + 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); + /* Test check- psa_skip with PSA_NULL_HANDLE, call should panic */ + 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); + val->print(PRINT_ERROR, + "\tpsa_skip with NULL handle should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } status = VAL_STATUS_SPM_FAILED; - psa_reply(msg.handle, -4); + 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->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); + psa->reply(msg.handle, PSA_SUCCESS); return status; } diff --git a/api-tests/ff/ipc/test_i037/test_i037.c b/api-tests/ff/ipc/test_i037/test_i037.c index 8a0d8211..c6765a2a 100644 --- a/api-tests/ff/ipc/test_i037/test_i037.c +++ b/api-tests/ff/ipc/test_i037/test_i037.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i037.h" @@ -38,7 +38,7 @@ int32_t client_test_psa_skip_with_invalid_handle(security_t caller) psa_status_t status_of_call; val->print(PRINT_TEST, - "[Check1] Test psa_skip with invalid handle\n", 0); + "[Check 1] Test psa_skip with invalid handle\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); if (handle < 0) @@ -50,7 +50,7 @@ int32_t client_test_psa_skip_with_invalid_handle(security_t caller) 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); + val->print(PRINT_ERROR, "\tCall should failed but succeed\n", 0); status = VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i037/test_supp_i037.c b/api-tests/ff/ipc/test_i037/test_supp_i037.c index 870be92c..9ed99633 100644 --- a/api-tests/ff/ipc/test_i037/test_supp_i037.c +++ b/api-tests/ff/ipc/test_i037/test_supp_i037.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_skip_with_invalid_handle(void); @@ -31,52 +36,74 @@ 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)) + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. + * + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + * + * To decide, a reboot happened as intended by 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. + */ + + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + 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)) + 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); + 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); + /* Test check- psa_skip with INVALID_HANDLE, call should panic */ + 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); + val->print(PRINT_ERROR, + "\tpsa_skip with invalid handle should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } status = VAL_STATUS_SPM_FAILED; - psa_reply(msg.handle, -4); + 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->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); + psa->reply(msg.handle, PSA_SUCCESS); return status; } diff --git a/api-tests/ff/ipc/test_i038/test_i038.c b/api-tests/ff/ipc/test_i038/test_i038.c index 98dd4a05..a2cf2b8e 100644 --- a/api-tests/ff/ipc/test_i038/test_i038.c +++ b/api-tests/ff/ipc/test_i038/test_i038.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i038.h" @@ -38,7 +38,7 @@ int32_t client_test_psa_skip_with_invec_equal_to_max_iovec(security_t caller) psa_status_t status_of_call; val->print(PRINT_TEST, - "[Check1] Test psa_skip with invec_idx=PSA_MAX_IOVEC\n", 0); + "[Check 1] Test psa_skip with invec_idx=PSA_MAX_IOVEC\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); if (handle < 0) @@ -50,7 +50,7 @@ int32_t client_test_psa_skip_with_invec_equal_to_max_iovec(security_t caller) 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); + val->print(PRINT_ERROR, "\tCall should failed but succeed\n", 0); status = VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i038/test_supp_i038.c b/api-tests/ff/ipc/test_i038/test_supp_i038.c index 536af3a1..540440d1 100644 --- a/api-tests/ff/ipc/test_i038/test_supp_i038.c +++ b/api-tests/ff/ipc/test_i038/test_supp_i038.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_skip_with_invec_equal_to_max_iovec(void); @@ -31,66 +36,76 @@ 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + 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)) + 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); + 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); + /* Test check- psa_skip with invec_idx=PSA_MAX_IOVEC, call should panic */ + 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)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + 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); + val->print(PRINT_ERROR, + "\tpsa_skip with invec_idx=PSA_MAX_IOVEC should failed but succeed\n", 0); - psa_reply(msg.handle, -4); + 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->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); + psa->reply(msg.handle, PSA_SUCCESS); return status; } diff --git a/api-tests/ff/ipc/test_i039/test_i039.c b/api-tests/ff/ipc/test_i039/test_i039.c index 2db39ae4..b028f365 100644 --- a/api-tests/ff/ipc/test_i039/test_i039.c +++ b/api-tests/ff/ipc/test_i039/test_i039.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i039.h" @@ -38,7 +38,7 @@ int32_t client_test_psa_skip_with_invec_greater_than_max_iovec(security_t caller psa_status_t status_of_call; val->print(PRINT_TEST, - "[Check1] Test psa_skip with invec_idx > PSA_MAX_IOVEC\n", 0); + "[Check 1] Test psa_skip with invec_idx > PSA_MAX_IOVEC\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); if (handle < 0) @@ -50,7 +50,7 @@ int32_t client_test_psa_skip_with_invec_greater_than_max_iovec(security_t caller 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); + val->print(PRINT_ERROR, "\tCall should failed but succeed\n", 0); status = VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i039/test_supp_i039.c b/api-tests/ff/ipc/test_i039/test_supp_i039.c index 58ee41e0..9b4b3c6e 100644 --- a/api-tests/ff/ipc/test_i039/test_supp_i039.c +++ b/api-tests/ff/ipc/test_i039/test_supp_i039.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_skip_with_invec_greater_than_max_iovec(void); @@ -31,66 +36,76 @@ 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + 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)) + 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); + 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); + /* Test check- psa_skip with invec_idx > PSA_MAX_IOVEC, call should panic */ + 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)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + 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); + val->print(PRINT_ERROR, + "\tpsa_skip with invec_idx > PSA_MAX_IOVEC should failed but succeed\n", 0); - psa_reply(msg.handle, -4); + 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->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); + psa->reply(msg.handle, PSA_SUCCESS); return status; } diff --git a/api-tests/ff/ipc/test_i040/test_i040.c b/api-tests/ff/ipc/test_i040/test_i040.c index ec020670..c0746b1e 100644 --- a/api-tests/ff/ipc/test_i040/test_i040.c +++ b/api-tests/ff/ipc/test_i040/test_i040.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i040.h" @@ -35,7 +35,7 @@ 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); + val->print(PRINT_TEST, "[Check 1] Test psa_write at PSA_IPC_CONNECT\n", 0); handle = psa->connect(SERVER_RELAX_MINOR_VERSION_SID, 1); diff --git a/api-tests/ff/ipc/test_i040/test_supp_i040.c b/api-tests/ff/ipc/test_i040/test_supp_i040.c index 47433c3e..8ea608b1 100644 --- a/api-tests/ff/ipc/test_i040/test_supp_i040.c +++ b/api-tests/ff/ipc/test_i040/test_supp_i040.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; #define NUM_OF_BYTES 4 @@ -34,46 +39,56 @@ int32_t server_test_psa_write_at_ipc_connect(void) 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_RELAX_MINOR_VERSION_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_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)) + 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); + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - /* Test check- psa_write at PSA_IPC_CONNECT */ - psa_write(msg.handle, 0, (void *)data, 0); + /* Test check- psa_write at PSA_IPC_CONNECT, call should panic */ + 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); + val->print(PRINT_ERROR,"\tpsa_write should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return VAL_STATUS_INVALID; } diff --git a/api-tests/ff/ipc/test_i041/test_i041.c b/api-tests/ff/ipc/test_i041/test_i041.c index a4275a9f..cead0f0f 100644 --- a/api-tests/ff/ipc/test_i041/test_i041.c +++ b/api-tests/ff/ipc/test_i041/test_i041.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i041.h" @@ -35,7 +35,7 @@ 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); + val->print(PRINT_TEST, "[Check 1] Test psa_write at PSA_IPC_DISCONNECT\n", 0); handle = psa->connect(SERVER_RELAX_MINOR_VERSION_SID, 1); if (handle > 0) diff --git a/api-tests/ff/ipc/test_i041/test_supp_i041.c b/api-tests/ff/ipc/test_i041/test_supp_i041.c index ed483778..8b3d2b31 100644 --- a/api-tests/ff/ipc/test_i041/test_supp_i041.c +++ b/api-tests/ff/ipc/test_i041/test_supp_i041.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; #define NUM_OF_BYTES 4 @@ -34,53 +39,63 @@ int32_t server_test_psa_write_at_ipc_disconnect(void) 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_RELAX_MINOR_VERSION_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + 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)) + 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); + 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); + /* Test check- psa_write at PSA_IPC_DISCONNECT, call should panic */ + 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); + val->print(PRINT_ERROR,"\tpsa_write should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); return VAL_STATUS_INVALID; } diff --git a/api-tests/ff/ipc/test_i042/test_i042.c b/api-tests/ff/ipc/test_i042/test_i042.c index 4c7652e8..65dc1539 100644 --- a/api-tests/ff/ipc/test_i042/test_i042.c +++ b/api-tests/ff/ipc/test_i042/test_i042.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i042.h" @@ -38,7 +38,7 @@ int32_t client_test_psa_write_with_null_handle(security_t caller) psa_status_t status_of_call; val->print(PRINT_TEST, - "[Check1] Test psa_write with NULL handle\n", 0); + "[Check 1] Test psa_write with NULL handle\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); if (handle < 0) @@ -50,7 +50,7 @@ int32_t client_test_psa_write_with_null_handle(security_t caller) 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); + val->print(PRINT_ERROR, "\tCall should failed but succeed\n", 0); status = VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i042/test_supp_i042.c b/api-tests/ff/ipc/test_i042/test_supp_i042.c index 712791e6..1257cdfc 100644 --- a/api-tests/ff/ipc/test_i042/test_supp_i042.c +++ b/api-tests/ff/ipc/test_i042/test_supp_i042.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_write_with_null_handle(); @@ -32,51 +37,72 @@ int32_t server_test_psa_write_with_null_handle() 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)) + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. + * + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + * + * To decide, a reboot happened as intended by 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. + */ + + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + 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)) + /* 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, -3); + psa->reply(msg.handle, -3); } else { - /*psa_write with PSA_NULL_HANDLE */ - psa_write(PSA_NULL_HANDLE, 0, (void *)data, 0); + /* psa_write with PSA_NULL_HANDLE, call should panic */ + 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); + 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); + val->print(PRINT_ERROR, + "\tpsa_write with NULL handle should failed but succeed\n", 0); - psa_reply(msg.handle, -4); + 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->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); + psa->reply(msg.handle, PSA_SUCCESS); return status; } diff --git a/api-tests/ff/ipc/test_i043/test_i043.c b/api-tests/ff/ipc/test_i043/test_i043.c index 6bebfe39..ea38f4c2 100644 --- a/api-tests/ff/ipc/test_i043/test_i043.c +++ b/api-tests/ff/ipc/test_i043/test_i043.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i043.h" @@ -38,7 +38,7 @@ int32_t client_test_psa_write_with_invalid_handle(security_t caller) psa_status_t status_of_call; val->print(PRINT_TEST, - "[Check1] Test psa_write with invalid handle\n", 0); + "[Check 1] Test psa_write with invalid handle\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); if (handle < 0) @@ -50,7 +50,7 @@ int32_t client_test_psa_write_with_invalid_handle(security_t caller) 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); + val->print(PRINT_ERROR, "\tCall should failed but succeed\n", 0); status = VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i043/test_supp_i043.c b/api-tests/ff/ipc/test_i043/test_supp_i043.c index 04053f20..51237539 100644 --- a/api-tests/ff/ipc/test_i043/test_supp_i043.c +++ b/api-tests/ff/ipc/test_i043/test_supp_i043.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; #define NUM_OF_BYTES 4 @@ -34,65 +39,75 @@ int32_t server_test_psa_write_with_invalid_handle(void) 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + 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)) + 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); + 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); + /* Test check- psa_write with INVALID_HANDLE, call should panic */ + 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)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + 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); + val->print(PRINT_ERROR, + "\tpsa_write with invalid handle should failed but succeed\n", 0); - psa_reply(msg.handle, -4); + 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->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); + psa->reply(msg.handle, PSA_SUCCESS); return status; } diff --git a/api-tests/ff/ipc/test_i044/test_i044.c b/api-tests/ff/ipc/test_i044/test_i044.c index 17289e1e..c22a18b8 100644 --- a/api-tests/ff/ipc/test_i044/test_i044.c +++ b/api-tests/ff/ipc/test_i044/test_i044.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i044.h" @@ -38,7 +38,7 @@ int32_t client_test_psa_write_with_invec_equal_to_max_iovec(security_t caller) psa_status_t status_of_call; val->print(PRINT_TEST, - "[Check1] Test psa_write with invec_idx=PSA_MAX_IOVEC\n", 0); + "[Check 1] Test psa_write with invec_idx=PSA_MAX_IOVEC\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); if (handle < 0) @@ -50,7 +50,7 @@ int32_t client_test_psa_write_with_invec_equal_to_max_iovec(security_t caller) 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); + val->print(PRINT_ERROR, "\tCall should failed but succeed\n", 0); status = VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i044/test_supp_i044.c b/api-tests/ff/ipc/test_i044/test_supp_i044.c index 1219b5ef..b4e4cae3 100644 --- a/api-tests/ff/ipc/test_i044/test_supp_i044.c +++ b/api-tests/ff/ipc/test_i044/test_supp_i044.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; #define NUM_OF_BYTES 4 @@ -34,53 +39,75 @@ int32_t server_test_psa_write_with_invec_equal_to_max_iovec(void) 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)) + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. + * + * If programmed timeout value isn't sufficient for your system, it can be reconfigured using + * timeout entries available in target.cfg. + * + * To decide, a reboot happened as intended by 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. + */ + + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + 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)) + 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); + 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); + /* Test check- psa_write with invec_idx=PSA_MAX_IOVEC, call should panic */ + 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)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + 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); + val->print(PRINT_ERROR, + "\tpsa_write with invec_idx=PSA_MAX_IOVEC should failed but succeed\n", 0); - psa_reply(msg.handle, -4); + 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->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); + psa->reply(msg.handle, PSA_SUCCESS); return status; } diff --git a/api-tests/ff/ipc/test_i045/test_i045.c b/api-tests/ff/ipc/test_i045/test_i045.c index 3b15f967..6448b6de 100644 --- a/api-tests/ff/ipc/test_i045/test_i045.c +++ b/api-tests/ff/ipc/test_i045/test_i045.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i045.h" @@ -38,7 +38,7 @@ int32_t client_test_psa_write_with_invec_greater_than_max_iovec(security_t calle psa_status_t status_of_call; val->print(PRINT_TEST, - "[Check1] Test psa_write with invec_idx > PSA_MAX_IOVEC\n", 0); + "[Check 1] Test psa_write with invec_idx > PSA_MAX_IOVEC\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); if (handle < 0) @@ -50,7 +50,7 @@ int32_t client_test_psa_write_with_invec_greater_than_max_iovec(security_t calle 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); + val->print(PRINT_ERROR, "\tCall should failed but succeed\n", 0); status = VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i045/test_supp_i045.c b/api-tests/ff/ipc/test_i045/test_supp_i045.c index a16d5a74..260e6ff7 100644 --- a/api-tests/ff/ipc/test_i045/test_supp_i045.c +++ b/api-tests/ff/ipc/test_i045/test_supp_i045.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; #define NUM_OF_BYTES 4 @@ -34,65 +39,75 @@ int32_t server_test_psa_write_with_invec_greater_than_max_iovec(void) 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + 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)) + 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); + 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); + /* Test check- psa_write with invec_idx > PSA_MAX_IOVEC, call should panic */ + 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)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + 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); + val->print(PRINT_ERROR, + "\tpsa_write with invec_idx > PSA_MAX_IOVEC should failed but succeed\n", 0); - psa_reply(msg.handle, -4); + 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->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); + psa->reply(msg.handle, PSA_SUCCESS); return status; } diff --git a/api-tests/ff/ipc/test_i046/test_i046.c b/api-tests/ff/ipc/test_i046/test_i046.c index 06139698..f6270239 100644 --- a/api-tests/ff/ipc/test_i046/test_i046.c +++ b/api-tests/ff/ipc/test_i046/test_i046.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i046.h" @@ -39,7 +39,7 @@ int32_t client_test_psa_write_with_size_overflow(security_t caller) uint8_t data = 0; val->print(PRINT_TEST, - "[Check1] Test psa_write with size overflow\n", 0); + "[Check 1] Test psa_write with size overflow\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); if (handle < 0) @@ -53,7 +53,7 @@ int32_t client_test_psa_write_with_size_overflow(security_t caller) 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); + val->print(PRINT_ERROR, "\tCall should failed but succeed\n", 0); status = VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i046/test_supp_i046.c b/api-tests/ff/ipc/test_i046/test_supp_i046.c index c5db835e..8a4513a2 100644 --- a/api-tests/ff/ipc/test_i046/test_supp_i046.c +++ b/api-tests/ff/ipc/test_i046/test_supp_i046.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; #define NUM_OF_BYTES 4 @@ -34,65 +39,75 @@ int32_t server_test_psa_write_with_size_overflow(void) 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + 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)) + 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); + 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)) + 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); + 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); + /* Test check- psa_write with size overflow, call should panic */ + 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)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + 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); + val->print(PRINT_ERROR, + "\tpsa_write with size overflow should failed but succeed\n", 0); - psa_reply(msg.handle, -4); + 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->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); + psa->reply(msg.handle, PSA_SUCCESS); return status; } diff --git a/api-tests/ff/ipc/test_i047/test_i047.c b/api-tests/ff/ipc/test_i047/test_i047.c index 989c6b05..ab98a951 100644 --- a/api-tests/ff/ipc/test_i047/test_i047.c +++ b/api-tests/ff/ipc/test_i047/test_i047.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i047.h" @@ -36,7 +36,7 @@ int32_t client_test_psa_get_with_invalid_msg_pointer(security_t caller) psa_handle_t handle = 0; val->print(PRINT_TEST, - "[Check1] Test psa_get with invalid msg pointer\n", 0); + "[Check 1] Test psa_get with invalid msg pointer\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); if (handle > 0) @@ -45,7 +45,7 @@ int32_t client_test_psa_get_with_invalid_msg_pointer(security_t caller) } /* Expectation is server test should hang and control shouldn't have come here */ - val->print(PRINT_ERROR, "\tpsa_connect should failed but successed\n", 0); + val->print(PRINT_ERROR, "\tpsa_connect should failed but succeed\n", 0); (void)(handle); return VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i047/test_supp_i047.c b/api-tests/ff/ipc/test_i047/test_supp_i047.c index b41aa97e..82e43df6 100644 --- a/api-tests/ff/ipc/test_i047/test_supp_i047.c +++ b/api-tests/ff/ipc/test_i047/test_supp_i047.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_get_with_invalid_msg_pointer(void); @@ -35,49 +40,61 @@ int32_t server_test_psa_get_with_invalid_msg_pointer(void) miscellaneous_desc_t *misc_desc; memory_desc_t *memory_desc; - /* 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - signals = psa_wait(SERVER_UNSPECIFED_MINOR_V_SIG, PSA_BLOCK); + signals = psa->wait(SERVER_UNSPECIFED_MINOR_V_SIG, PSA_BLOCK); if ((signals & SERVER_UNSPECIFED_MINOR_V_SIG) == 0) { - val_print(PRINT_ERROR, + val->print(PRINT_ERROR, "psa_wait returned with invalid signal value = 0x%x\n", signals); return VAL_STATUS_ERROR; } - /* Selection of invalid msg pointer: + /* + * Selection of invalid msg pointer: + * * if (ISOLATION_LEVEL > 1) * msg_pointer = driver_mmio_region_base; * else * msg_pointer = NULL; */ - status = val_target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_MISCELLANEOUS, + 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_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + if (val->err_check_set(TEST_CHECKPOINT_NUM(201), status)) { return status; } if (misc_desc->implemented_psa_firmware_isolation_level > LEVEL1) { - status = val_target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_MEMORY, + status = val->target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_MEMORY, MEMORY_DRIVER_PARTITION_MMIO, 0), (uint8_t **)&memory_desc, (uint32_t *)sizeof(memory_desc_t)); - if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + if (val->err_check_set(TEST_CHECKPOINT_NUM(202), status)) { return status; } @@ -86,26 +103,26 @@ int32_t server_test_psa_get_with_invalid_msg_pointer(void) } /* Setting boot.state before test check */ - if (val_set_boot_flag(BOOT_EXPECTED_NS)) + if (val->set_boot_flag(BOOT_EXPECTED_NS)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); return VAL_STATUS_ERROR; } - /* psa_get with invalid msg pointer */ - psa_get(SERVER_UNSPECIFED_MINOR_V_SIG, invalid_msg); + /* psa_get with invalid msg pointer, call should panic */ + psa->get(SERVER_UNSPECIFED_MINOR_V_SIG, invalid_msg); /* shouldn't have reached here */ - val_print(PRINT_ERROR, - "\tpsa_get with invalid msg pointer should failed but successed\n", 0); + val->print(PRINT_ERROR, + "\tpsa_get with invalid msg pointer should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } /* Reject the connection */ - psa_reply(invalid_msg->handle, PSA_CONNECTION_REFUSED); + psa->reply(invalid_msg->handle, PSA_ERROR_CONNECTION_REFUSED); return VAL_STATUS_SPM_FAILED; } diff --git a/api-tests/ff/ipc/test_i048/test_i048.c b/api-tests/ff/ipc/test_i048/test_i048.c index 718236f3..7e8bb6dc 100644 --- a/api-tests/ff/ipc/test_i048/test_i048.c +++ b/api-tests/ff/ipc/test_i048/test_i048.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i048.h" @@ -42,18 +42,28 @@ int32_t client_test_psa_call_with_invalid_invec_pointer(security_t caller) psa_invec *invalid_invec = NULL; val->print(PRINT_TEST, - "[Check1] Test psa_call with invalid address for in_vec\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. + "[Check 1] Test psa_call with invalid address for in_vec\n", 0); + + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); @@ -64,12 +74,15 @@ int32_t client_test_psa_call_with_invalid_invec_pointer(security_t caller) return VAL_STATUS_INVALID_HANDLE; } - /* Selection of invalid invec pointer: + /* + * Selection of invalid invec pointer: + * * if caller == NONSECURE * // PSA RoT pointer * invec_pointer = driver_mmio_region_base; * else * if (ISOLATION_LEVEL > 1) + * // PSA RoT pointer * invec_pointer = driver_mmio_region_base; * else * invec_pointer = NULL; @@ -115,8 +128,19 @@ int32_t client_test_psa_call_with_invalid_invec_pointer(security_t caller) /* Test check- psa_call with invalid address for in_vec */ status_of_call = psa->call(handle, invalid_invec, 1, 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); + /* + * If the caller is in the NSPE, it is IMPLEMENTATION DEFINED whether + * a PROGRAMMER ERROR will panic or return PSA_ERROR_PROGRAMMER_ERROR. + * For SPE caller, it must panic. + */ + if (caller == NONSECURE && status_of_call == PSA_ERROR_PROGRAMMER_ERROR) + { + psa->close(handle); + return VAL_STATUS_SUCCESS; + } + + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ + val->print(PRINT_ERROR, "\tpsa_call should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) @@ -127,6 +151,5 @@ int32_t client_test_psa_call_with_invalid_invec_pointer(security_t caller) status = VAL_STATUS_SPM_FAILED; psa->close(handle); - (void)(status_of_call); return status; } diff --git a/api-tests/ff/ipc/test_i048/test_supp_i048.c b/api-tests/ff/ipc/test_i048/test_supp_i048.c index 5384990d..51b11da5 100644 --- a/api-tests/ff/ipc/test_i048/test_supp_i048.c +++ b/api-tests/ff/ipc/test_i048/test_supp_i048.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_call_with_invalid_invec_pointer(void); @@ -30,23 +35,45 @@ int32_t server_test_psa_call_with_invalid_invec_pointer(void) { int32_t status = VAL_STATUS_SUCCESS; psa_msg_t msg = {0}; + psa_signal_t signals; - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); - val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); +wait: + signals = psa->wait(PSA_WAIT_ANY, PSA_BLOCK); + if (signals & SERVER_UNSPECIFED_MINOR_V_SIG) + { + if (psa->get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) + { + goto wait; + } - /* Control shouldn't have come here */ - val_print(PRINT_ERROR, "\tControl shouldn't have reached here\n", 0); - psa_reply(msg.handle, -2); + if (msg.type == PSA_IPC_CALL) + { + /* 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); + } + else if (msg.type == PSA_IPC_DISCONNECT) + { + psa->reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_SUCCESS; + } + } + else + { + val->print(PRINT_ERROR, "\tpsa_wait returned with invalid signal value = 0x%x\n", signals); + return VAL_STATUS_ERROR; + } - val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - psa_reply(msg.handle, PSA_SUCCESS); - return VAL_STATUS_SPM_FAILED; + return VAL_STATUS_ERROR; } diff --git a/api-tests/ff/ipc/test_i049/test_i049.c b/api-tests/ff/ipc/test_i049/test_i049.c index 01999dfa..f3f16646 100644 --- a/api-tests/ff/ipc/test_i049/test_i049.c +++ b/api-tests/ff/ipc/test_i049/test_i049.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i049.h" @@ -42,18 +42,28 @@ int32_t client_test_psa_call_with_invalid_outvec_pointer(security_t caller) psa_outvec *invalid_outvec = NULL; val->print(PRINT_TEST, - "[Check1] Test psa_call with invalid address for outvec\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. + "[Check 1] Test psa_call with invalid address for outvec\n", 0); + + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); @@ -64,12 +74,15 @@ int32_t client_test_psa_call_with_invalid_outvec_pointer(security_t caller) return VAL_STATUS_INVALID_HANDLE; } - /* Selection of invalid outvec pointer: + /* + * Selection of invalid outvec pointer: + * * if caller == NONSECURE * // PSA RoT pointer * outvec_pointer = driver_mmio_region_base; * else * if (ISOLATION_LEVEL > 1) + * // PSA RoT pointer * outvec_pointer = driver_mmio_region_base; * else * outvec_pointer = NULL; @@ -115,8 +128,19 @@ int32_t client_test_psa_call_with_invalid_outvec_pointer(security_t caller) /* Test check- psa_call with invalid address for outvec */ status_of_call = psa->call(handle, NULL, 0, invalid_outvec, 1); - /* 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); + /* + * If the caller is in the NSPE, it is IMPLEMENTATION DEFINED whether + * a PROGRAMMER ERROR will panic or return PSA_ERROR_PROGRAMMER_ERROR. + * For SPE caller, it must panic. + */ + if (caller == NONSECURE && status_of_call == PSA_ERROR_PROGRAMMER_ERROR) + { + psa->close(handle); + return VAL_STATUS_SUCCESS; + } + + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ + val->print(PRINT_ERROR, "\tpsa_call should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) @@ -127,6 +151,5 @@ int32_t client_test_psa_call_with_invalid_outvec_pointer(security_t caller) status = VAL_STATUS_SPM_FAILED; psa->close(handle); - (void)(status_of_call); return status; } diff --git a/api-tests/ff/ipc/test_i049/test_supp_i049.c b/api-tests/ff/ipc/test_i049/test_supp_i049.c index 73e5d689..23b52b8c 100644 --- a/api-tests/ff/ipc/test_i049/test_supp_i049.c +++ b/api-tests/ff/ipc/test_i049/test_supp_i049.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_call_with_invalid_outvec_pointer(void); @@ -30,23 +35,45 @@ int32_t server_test_psa_call_with_invalid_outvec_pointer(void) { int32_t status = VAL_STATUS_SUCCESS; psa_msg_t msg = {0}; + psa_signal_t signals; - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); - val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); +wait: + signals = psa->wait(PSA_WAIT_ANY, PSA_BLOCK); + if (signals & SERVER_UNSPECIFED_MINOR_V_SIG) + { + if (psa->get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) + { + goto wait; + } - /* Control shouldn't have come here */ - val_print(PRINT_ERROR, "\tControl shouldn't have reached here\n", 0); - psa_reply(msg.handle, -2); + if (msg.type == PSA_IPC_CALL) + { + /* 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); + } + else if (msg.type == PSA_IPC_DISCONNECT) + { + psa->reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_SUCCESS; + } + } + else + { + val->print(PRINT_ERROR, "\tpsa_wait returned with invalid signal value = 0x%x\n", signals); + return VAL_STATUS_ERROR; + } - val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - psa_reply(msg.handle, PSA_SUCCESS); - return VAL_STATUS_SPM_FAILED; + return VAL_STATUS_ERROR; } diff --git a/api-tests/ff/ipc/test_i050/test_i050.c b/api-tests/ff/ipc/test_i050/test_i050.c index bf5fd211..9f616b16 100644 --- a/api-tests/ff/ipc/test_i050/test_i050.c +++ b/api-tests/ff/ipc/test_i050/test_i050.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i050.h" @@ -42,18 +42,28 @@ int32_t client_test_psa_call_with_invalid_invec_base(security_t caller) addr_t *invalid_base = NULL; val->print(PRINT_TEST, - "[Check1] Test psa_call with invalid psa_invec.base\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. + "[Check 1] Test psa_call with invalid psa_invec.base\n", 0); + + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); @@ -64,12 +74,15 @@ int32_t client_test_psa_call_with_invalid_invec_base(security_t caller) return VAL_STATUS_INVALID_HANDLE; } - /* Selection of invalid invec pointer: + /* + * Selection of invalid invec pointer: + * * if caller == NONSECURE * // PSA RoT pointer * invalid_base = driver_mmio_region_base; * else * if (ISOLATION_LEVEL > 1) + * // PSA RoT pointer * invalid_base = driver_mmio_region_base; * else * invalid_base = NULL; @@ -117,8 +130,19 @@ int32_t client_test_psa_call_with_invalid_invec_base(security_t caller) /* Test check- psa_call with invalid address for psa_invec.base */ status_of_call = psa->call(handle, invec, 1, 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); + /* + * If the caller is in the NSPE, it is IMPLEMENTATION DEFINED whether + * a PROGRAMMER ERROR will panic or return PSA_ERROR_PROGRAMMER_ERROR. + * For SPE caller, it must panic. + */ + if (caller == NONSECURE && status_of_call == PSA_ERROR_PROGRAMMER_ERROR) + { + psa->close(handle); + return VAL_STATUS_SUCCESS; + } + + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ + val->print(PRINT_ERROR, "\tpsa_call should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) @@ -129,6 +153,5 @@ int32_t client_test_psa_call_with_invalid_invec_base(security_t caller) status = VAL_STATUS_SPM_FAILED; psa->close(handle); - (void)(status_of_call); return status; } diff --git a/api-tests/ff/ipc/test_i050/test_supp_i050.c b/api-tests/ff/ipc/test_i050/test_supp_i050.c index e1b29792..9c09445e 100644 --- a/api-tests/ff/ipc/test_i050/test_supp_i050.c +++ b/api-tests/ff/ipc/test_i050/test_supp_i050.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_call_with_invalid_invec_base(void); @@ -30,23 +35,45 @@ int32_t server_test_psa_call_with_invalid_invec_base(void) { int32_t status = VAL_STATUS_SUCCESS; psa_msg_t msg = {0}; + psa_signal_t signals; - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); - val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); +wait: + signals = psa->wait(PSA_WAIT_ANY, PSA_BLOCK); + if (signals & SERVER_UNSPECIFED_MINOR_V_SIG) + { + if (psa->get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) + { + goto wait; + } - /* Control shouldn't have come here */ - val_print(PRINT_ERROR, "\tControl shouldn't have reached here\n", 0); - psa_reply(msg.handle, -2); + if (msg.type == PSA_IPC_CALL) + { + /* 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); + } + else if (msg.type == PSA_IPC_DISCONNECT) + { + psa->reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_SUCCESS; + } + } + else + { + val->print(PRINT_ERROR, "\tpsa_wait returned with invalid signal value = 0x%x\n", signals); + return VAL_STATUS_ERROR; + } - val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - psa_reply(msg.handle, PSA_SUCCESS); - return VAL_STATUS_SPM_FAILED; + return VAL_STATUS_ERROR; } diff --git a/api-tests/ff/ipc/test_i051/test_i051.c b/api-tests/ff/ipc/test_i051/test_i051.c index 3b4a726c..6a1f875c 100644 --- a/api-tests/ff/ipc/test_i051/test_i051.c +++ b/api-tests/ff/ipc/test_i051/test_i051.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i051.h" @@ -42,18 +42,28 @@ int32_t client_test_psa_call_with_invalid_outvec_base(security_t caller) addr_t *invalid_base = NULL; val->print(PRINT_TEST, - "[Check1] Test psa_call with invalid psa_outvec.base\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. + "[Check 1] Test psa_call with invalid psa_outvec.base\n", 0); + + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); @@ -64,12 +74,15 @@ int32_t client_test_psa_call_with_invalid_outvec_base(security_t caller) return VAL_STATUS_INVALID_HANDLE; } - /* Selection of invalid outvec pointer: + /* + * Selection of invalid outvec pointer: + * * if caller == NONSECURE * // PSA RoT pointer * invalid_base = driver_mmio_region_base; * else * if (ISOLATION_LEVEL > 1) + * // PSA RoT pointer * invalid_base = driver_mmio_region_base; * else * invalid_base = NULL; @@ -115,10 +128,21 @@ int32_t client_test_psa_call_with_invalid_outvec_base(security_t caller) psa_outvec outvec[1] = {{invalid_base, sizeof(addr_t)}}; /* Test check- psa_call with invalid address for psa_outvec.base */ - status_of_call = psa->call(handle, NULL, 0, outvec, 0); + status_of_call = psa->call(handle, NULL, 0, outvec, 1); + + /* + * If the caller is in the NSPE, it is IMPLEMENTATION DEFINED whether + * a PROGRAMMER ERROR will panic or return PSA_ERROR_PROGRAMMER_ERROR. + * For SPE caller, it must panic. + */ + if (caller == NONSECURE && status_of_call == PSA_ERROR_PROGRAMMER_ERROR) + { + psa->close(handle); + return VAL_STATUS_SUCCESS; + } - /* 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); + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ + val->print(PRINT_ERROR, "\tpsa_call should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) @@ -129,6 +153,5 @@ int32_t client_test_psa_call_with_invalid_outvec_base(security_t caller) status = VAL_STATUS_SPM_FAILED; psa->close(handle); - (void)(status_of_call); return status; } diff --git a/api-tests/ff/ipc/test_i051/test_supp_i051.c b/api-tests/ff/ipc/test_i051/test_supp_i051.c index 8acb38a6..0d3be0b6 100644 --- a/api-tests/ff/ipc/test_i051/test_supp_i051.c +++ b/api-tests/ff/ipc/test_i051/test_supp_i051.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_call_with_invalid_outvec_base(void); @@ -30,23 +35,45 @@ int32_t server_test_psa_call_with_invalid_outvec_base(void) { int32_t status = VAL_STATUS_SUCCESS; psa_msg_t msg = {0}; + psa_signal_t signals; - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); - val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); +wait: + signals = psa->wait(PSA_WAIT_ANY, PSA_BLOCK); + if (signals & SERVER_UNSPECIFED_MINOR_V_SIG) + { + if (psa->get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) + { + goto wait; + } - /* Control shouldn't have come here */ - val_print(PRINT_ERROR, "\tControl shouldn't have reached here\n", 0); - psa_reply(msg.handle, -2); + if (msg.type == PSA_IPC_CALL) + { + /* 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); + } + else if (msg.type == PSA_IPC_DISCONNECT) + { + psa->reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_SUCCESS; + } + } + else + { + val->print(PRINT_ERROR, "\tpsa_wait returned with invalid signal value = 0x%x\n", signals); + return VAL_STATUS_ERROR; + } - val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - psa_reply(msg.handle, PSA_SUCCESS); - return VAL_STATUS_SPM_FAILED; + return VAL_STATUS_ERROR; } diff --git a/api-tests/ff/ipc/test_i052/test_i052.c b/api-tests/ff/ipc/test_i052/test_i052.c index 6b67f7ff..5b14c373 100644 --- a/api-tests/ff/ipc/test_i052/test_i052.c +++ b/api-tests/ff/ipc/test_i052/test_i052.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i052.h" @@ -43,18 +43,28 @@ int32_t client_test_psa_call_with_invalid_invec_end_addr(security_t caller) addr_t *valid_base = NULL; val->print(PRINT_TEST, - "[Check1] Test psa_call with invalid end_addr for psa_invec\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. + "[Check 1] Test psa_call with invalid end_addr for psa_invec\n", 0); + + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); @@ -65,14 +75,16 @@ int32_t client_test_psa_call_with_invalid_invec_end_addr(security_t caller) return VAL_STATUS_INVALID_HANDLE; } - /* Selection of invalid size for psa_invec: + /* + * Selection of invalid size for psa_invec: + * * if caller == NONSECURE * valid_base = nspe_mmio_region_base; * invalid_size = (driver_mmio_region_base - nspe_mmio_region_base + 1); * else * if (ISOLATION_LEVEL > 1) - * valid_base = client_mmio_region_base; - * invalid_size = (driver_mmio_region_base - client_mmio_region_base + 1); + * valid_base = server_mmio_region_base; + * invalid_size = (driver_mmio_region_base - server_mmio_region_base + 1); */ status = val->target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_MEMORY, @@ -88,7 +100,7 @@ int32_t client_test_psa_call_with_invalid_invec_end_addr(security_t caller) if (caller == NONSECURE) memory_cfg_id = MEMORY_NSPE_MMIO; else - memory_cfg_id = MEMORY_CLIENT_PARTITION_MMIO; + memory_cfg_id = MEMORY_SERVER_PARTITION_MMIO; status = val->target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_MEMORY, memory_cfg_id, 0), @@ -116,8 +128,19 @@ int32_t client_test_psa_call_with_invalid_invec_end_addr(security_t caller) /* Test check- psa_call with invalid end_addr for psa_invec */ status_of_call = psa->call(handle, invec, 1, 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); + /* + * If the caller is in the NSPE, it is IMPLEMENTATION DEFINED whether + * a PROGRAMMER ERROR will panic or return PSA_ERROR_PROGRAMMER_ERROR. + * For SPE caller, it must panic. + */ + if (caller == NONSECURE && status_of_call == PSA_ERROR_PROGRAMMER_ERROR) + { + psa->close(handle); + return VAL_STATUS_SUCCESS; + } + + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ + val->print(PRINT_ERROR, "\tpsa_call should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) @@ -128,6 +151,5 @@ int32_t client_test_psa_call_with_invalid_invec_end_addr(security_t caller) status = VAL_STATUS_SPM_FAILED; psa->close(handle); - (void)(status_of_call); return status; } diff --git a/api-tests/ff/ipc/test_i052/test_supp_i052.c b/api-tests/ff/ipc/test_i052/test_supp_i052.c index 0eeafcef..31bc32b4 100644 --- a/api-tests/ff/ipc/test_i052/test_supp_i052.c +++ b/api-tests/ff/ipc/test_i052/test_supp_i052.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_call_with_invalid_invec_end_addr(void); @@ -30,23 +35,45 @@ int32_t server_test_psa_call_with_invalid_invec_end_addr(void) { int32_t status = VAL_STATUS_SUCCESS; psa_msg_t msg = {0}; + psa_signal_t signals; - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); - val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); +wait: + signals = psa->wait(PSA_WAIT_ANY, PSA_BLOCK); + if (signals & SERVER_UNSPECIFED_MINOR_V_SIG) + { + if (psa->get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) + { + goto wait; + } - /* Control shouldn't have come here */ - val_print(PRINT_ERROR, "\tControl shouldn't have reached here\n", 0); - psa_reply(msg.handle, -2); + if (msg.type == PSA_IPC_CALL) + { + /* 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); + } + else if (msg.type == PSA_IPC_DISCONNECT) + { + psa->reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_SUCCESS; + } + } + else + { + val->print(PRINT_ERROR, "\tpsa_wait returned with invalid signal value = 0x%x\n", signals); + return VAL_STATUS_ERROR; + } - val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - psa_reply(msg.handle, PSA_SUCCESS); - return VAL_STATUS_SPM_FAILED; + return VAL_STATUS_ERROR; } diff --git a/api-tests/ff/ipc/test_i053/test_i053.c b/api-tests/ff/ipc/test_i053/test_i053.c index 9420ba18..108c24b9 100644 --- a/api-tests/ff/ipc/test_i053/test_i053.c +++ b/api-tests/ff/ipc/test_i053/test_i053.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i053.h" @@ -43,18 +43,28 @@ int32_t client_test_psa_call_with_invalid_outvec_end_addr(security_t caller) addr_t *valid_base = NULL; val->print(PRINT_TEST, - "[Check1] Test psa_call with invalid end_addr for psa_outvec\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. + "[Check 1] Test psa_call with invalid end_addr for psa_outvec\n", 0); + + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); @@ -65,14 +75,16 @@ int32_t client_test_psa_call_with_invalid_outvec_end_addr(security_t caller) return VAL_STATUS_INVALID_HANDLE; } - /* Selection of invalid size for psa_outvec: + /* + * Selection of invalid size for psa_outvec: + * * if caller == NONSECURE * valid_base = nspe_mmio_region_base; * invalid_size = (driver_mmio_region_base - nspe_mmio_region_base + 1); * else * if (ISOLATION_LEVEL > 1) - * valid_base = client_mmio_region_base; - * invalid_size = (driver_mmio_region_base - client_mmio_region_base + 1); + * valid_base = server_mmio_region_base; + * invalid_size = (driver_mmio_region_base - server_mmio_region_base + 1); */ status = val->target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_MEMORY, @@ -88,7 +100,7 @@ int32_t client_test_psa_call_with_invalid_outvec_end_addr(security_t caller) if (caller == NONSECURE) memory_cfg_id = MEMORY_NSPE_MMIO; else - memory_cfg_id = MEMORY_CLIENT_PARTITION_MMIO; + memory_cfg_id = MEMORY_SERVER_PARTITION_MMIO; status = val->target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_MEMORY, memory_cfg_id, 0), @@ -116,8 +128,19 @@ int32_t client_test_psa_call_with_invalid_outvec_end_addr(security_t caller) /* Test check- psa_call with invalid end_addr for psa_outvec */ status_of_call = psa->call(handle, NULL, 0, outvec, 1); - /* 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); + /* + * If the caller is in the NSPE, it is IMPLEMENTATION DEFINED whether + * a PROGRAMMER ERROR will panic or return PSA_ERROR_PROGRAMMER_ERROR. + * For SPE caller, it must panic. + */ + if (caller == NONSECURE && status_of_call == PSA_ERROR_PROGRAMMER_ERROR) + { + psa->close(handle); + return VAL_STATUS_SUCCESS; + } + + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ + val->print(PRINT_ERROR, "\tpsa_call should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) diff --git a/api-tests/ff/ipc/test_i053/test_supp_i053.c b/api-tests/ff/ipc/test_i053/test_supp_i053.c index c2d853bd..649e3c25 100644 --- a/api-tests/ff/ipc/test_i053/test_supp_i053.c +++ b/api-tests/ff/ipc/test_i053/test_supp_i053.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_call_with_invalid_outvec_end_addr(void); @@ -30,23 +35,45 @@ int32_t server_test_psa_call_with_invalid_outvec_end_addr(void) { int32_t status = VAL_STATUS_SUCCESS; psa_msg_t msg = {0}; + psa_signal_t signals; - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); - val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); +wait: + signals = psa->wait(PSA_WAIT_ANY, PSA_BLOCK); + if (signals & SERVER_UNSPECIFED_MINOR_V_SIG) + { + if (psa->get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) + { + goto wait; + } - /* Control shouldn't have come here */ - val_print(PRINT_ERROR, "\tControl shouldn't have reached here\n", 0); - psa_reply(msg.handle, -2); + if (msg.type == PSA_IPC_CALL) + { + /* 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); + } + else if (msg.type == PSA_IPC_DISCONNECT) + { + psa->reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_SUCCESS; + } + } + else + { + val->print(PRINT_ERROR, "\tpsa_wait returned with invalid signal value = 0x%x\n", signals); + return VAL_STATUS_ERROR; + } - val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - psa_reply(msg.handle, PSA_SUCCESS); - return VAL_STATUS_SPM_FAILED; + return VAL_STATUS_ERROR; } diff --git a/api-tests/ff/ipc/test_i054/test_entry.c b/api-tests/ff/ipc/test_i054/test_entry.c index 71e93557..d431d59a 100644 --- a/api-tests/ff/ipc/test_i054/test_entry.c +++ b/api-tests/ff/ipc/test_i054/test_entry.c @@ -39,13 +39,6 @@ void test_entry(val_api_t *val_api, psa_api_t *psa_api) 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_i054_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); diff --git a/api-tests/ff/ipc/test_i054/test_i054.c b/api-tests/ff/ipc/test_i054/test_i054.c index 894cb86e..80160cae 100644 --- a/api-tests/ff/ipc/test_i054/test_i054.c +++ b/api-tests/ff/ipc/test_i054/test_i054.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i054.h" @@ -35,22 +35,30 @@ int32_t client_test_psa_call_with_not_writable_outvec_base(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 not writable psa_outvec.base\n", 0); + "[Check 1] Test psa_call with not writable psa_outvec.base\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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); @@ -61,8 +69,7 @@ int32_t client_test_psa_call_with_not_writable_outvec_base(security_t caller) } /* Setting boot.state before test check */ - boot_state = (caller == NONSECURE) ? BOOT_EXPECTED_NS : BOOT_EXPECTED_S; - if (val->set_boot_flag(boot_state)) + 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; @@ -71,11 +78,11 @@ int32_t client_test_psa_call_with_not_writable_outvec_base(security_t caller) /* Using function address (code) as not writable address */ psa_outvec outvec[1] = {{&client_test_psa_call_with_not_writable_outvec_base, sizeof(char)}}; - /* Test check- psa_call with not writable psa_outvec.base */ - status_of_call = psa->call(handle, NULL, 0, outvec, 0); + /* Test check- psa_call with not writable psa_outvec.base, call should panic */ + psa->call(handle, NULL, 0, outvec, 1); - /* 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); + /* If PROGRAMMER ERROR results into panic then control shouldn't have reached here */ + val->print(PRINT_ERROR, "\tpsa_call should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) @@ -86,6 +93,5 @@ int32_t client_test_psa_call_with_not_writable_outvec_base(security_t caller) status = VAL_STATUS_SPM_FAILED; psa->close(handle); - (void)(status_of_call); return status; } diff --git a/api-tests/ff/ipc/test_i054/test_supp_i054.c b/api-tests/ff/ipc/test_i054/test_supp_i054.c index bdaae570..75442576 100644 --- a/api-tests/ff/ipc/test_i054/test_supp_i054.c +++ b/api-tests/ff/ipc/test_i054/test_supp_i054.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_call_with_not_writable_outvec_base(void); @@ -30,23 +35,45 @@ int32_t server_test_psa_call_with_not_writable_outvec_base(void) { int32_t status = VAL_STATUS_SUCCESS; psa_msg_t msg = {0}; + psa_signal_t signals; - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); - val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); +wait: + signals = psa->wait(PSA_WAIT_ANY, PSA_BLOCK); + if (signals & SERVER_UNSPECIFED_MINOR_V_SIG) + { + if (psa->get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) + { + goto wait; + } - /* Control shouldn't have come here */ - val_print(PRINT_ERROR, "\tControl shouldn't have reached here\n", 0); - psa_reply(msg.handle, -2); + if (msg.type == PSA_IPC_CALL) + { + /* 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); + } + else if (msg.type == PSA_IPC_DISCONNECT) + { + psa->reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_SUCCESS; + } + } + else + { + val->print(PRINT_ERROR, "\tpsa_wait returned with invalid signal value = 0x%x\n", signals); + return VAL_STATUS_ERROR; + } - val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - psa_reply(msg.handle, PSA_SUCCESS); - return VAL_STATUS_SPM_FAILED; + return VAL_STATUS_ERROR; } diff --git a/api-tests/ff/ipc/test_i055/test_i055.c b/api-tests/ff/ipc/test_i055/test_i055.c index 29616aa9..033a99a2 100644 --- a/api-tests/ff/ipc/test_i055/test_i055.c +++ b/api-tests/ff/ipc/test_i055/test_i055.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i055.h" @@ -39,7 +39,7 @@ int32_t client_test_psa_read_with_invalid_buffer_addr(security_t caller) psa_status_t status_of_call; val->print(PRINT_TEST, - "[Check1] Test psa_read with invalid buffer addr\n", 0); + "[Check 1] Test psa_read with invalid buffer addr\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); if (handle < 0) @@ -52,7 +52,7 @@ int32_t client_test_psa_read_with_invalid_buffer_addr(security_t caller) status_of_call = psa->call(handle, invec, 1, 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); + val->print(PRINT_ERROR, "\tCall should failed but succeed\n", 0); status = VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i055/test_supp_i055.c b/api-tests/ff/ipc/test_i055/test_supp_i055.c index 7cc450e9..24867959 100644 --- a/api-tests/ff/ipc/test_i055/test_supp_i055.c +++ b/api-tests/ff/ipc/test_i055/test_supp_i055.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_read_with_invalid_buffer_addr(void); @@ -34,52 +39,64 @@ int32_t server_test_psa_read_with_invalid_buffer_addr(void) miscellaneous_desc_t *misc_desc; memory_desc_t *memory_desc; - /* 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - /* Selection of invalid buffer addr: + /* + * Selection of invalid buffer addr: + * * if (ISOLATION_LEVEL > 1) * buffer = driver_mmio_region_base; * else * buffer = NULL; */ - status = val_target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_MISCELLANEOUS, + 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_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + if (val->err_check_set(TEST_CHECKPOINT_NUM(202), status)) { - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } if (misc_desc->implemented_psa_firmware_isolation_level > LEVEL1) { - status = val_target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_MEMORY, + status = val->target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_MEMORY, MEMORY_DRIVER_PARTITION_MMIO, 0), (uint8_t **)&memory_desc, (uint32_t *)sizeof(memory_desc_t)); - if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + if (val->err_check_set(TEST_CHECKPOINT_NUM(203), status)) { - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } @@ -87,46 +104,46 @@ int32_t server_test_psa_read_with_invalid_buffer_addr(void) } /* Accept the connection */ - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); - /* Server psa_call */ - status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(204), status)) + /* Serve psa_call */ + status = val->process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val->err_check_set(TEST_CHECKPOINT_NUM(204), status)) { - psa_reply(msg.handle, -2); + 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(205), status)) + status = val->set_boot_flag(BOOT_EXPECTED_NS); + if (val->err_check_set(TEST_CHECKPOINT_NUM(205), status)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); - psa_reply(msg.handle, -3); + 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 buffer addr */ - psa_read(msg.handle, 0, (void *)buffer, msg.in_size[0]); + /* Test check- psa_read with invalid buffer addr, call should panic */ + psa->read(msg.handle, 0, (void *)buffer, msg.in_size[0]); /* shouldn't have reached here */ - val_print(PRINT_ERROR, - "\tpsa_read with invalid buffer should failed but successed\n", 0); + val->print(PRINT_ERROR, + "\tpsa_read with invalid buffer should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } status = VAL_STATUS_SPM_FAILED; - psa_reply(msg.handle, -4); + psa->reply(msg.handle, -4); } } - val_err_check_set(TEST_CHECKPOINT_NUM(206), status); - status = ((val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + val->err_check_set(TEST_CHECKPOINT_NUM(206), status); + status = ((val->process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) ? VAL_STATUS_ERROR : status); - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); return status; } diff --git a/api-tests/ff/ipc/test_i056/test_i056.c b/api-tests/ff/ipc/test_i056/test_i056.c index bd5a7c59..7547d33e 100644 --- a/api-tests/ff/ipc/test_i056/test_i056.c +++ b/api-tests/ff/ipc/test_i056/test_i056.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i056.h" @@ -39,7 +39,7 @@ int32_t client_test_psa_read_with_not_writable_buffer_addr(security_t caller) psa_status_t status_of_call; val->print(PRINT_TEST, - "[Check1] Test psa_read with invalid buffer addr\n", 0); + "[Check 1] Test psa_read with invalid buffer addr\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); if (handle < 0) @@ -52,7 +52,7 @@ int32_t client_test_psa_read_with_not_writable_buffer_addr(security_t caller) status_of_call = psa->call(handle, invec, 1, 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); + val->print(PRINT_ERROR, "\tCall should failed but succeed\n", 0); status = VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i056/test_supp_i056.c b/api-tests/ff/ipc/test_i056/test_supp_i056.c index 9a47e3bd..3a863102 100644 --- a/api-tests/ff/ipc/test_i056/test_supp_i056.c +++ b/api-tests/ff/ipc/test_i056/test_supp_i056.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_read_with_not_writable_buffer_addr(void); @@ -32,70 +37,80 @@ int32_t server_test_psa_read_with_not_writable_buffer_addr(void) psa_msg_t msg = {0}; void *buffer = NULL; - /* 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } /* Accept the connection */ - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); /* Set buffer to point to not writable location (Code memory) */ buffer = (void *) &server_test_psa_read_with_not_writable_buffer_addr; - /* Server psa_call */ - status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + /* Serve psa_call */ + 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); + 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)) + 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); + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + psa->reply(msg.handle, -3); } else { - /* Test check- psa_read with not writable buffer addr */ - psa_read(msg.handle, 0, (void *)buffer, msg.in_size[0]); + /* Test check- psa_read with not writable buffer addr, call should panic */ + psa->read(msg.handle, 0, (void *)buffer, msg.in_size[0]); /* shouldn't have reached here */ - val_print(PRINT_ERROR, - "\tpsa_read with not writable buffer should failed but successed\n", 0); + val->print(PRINT_ERROR, + "\tpsa_read with not writable buffer should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } status = VAL_STATUS_SPM_FAILED; - psa_reply(msg.handle, -4); + 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->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); + psa->reply(msg.handle, PSA_SUCCESS); return status; } diff --git a/api-tests/ff/ipc/test_i057/test_i057.c b/api-tests/ff/ipc/test_i057/test_i057.c index 5aef674c..05967d4e 100644 --- a/api-tests/ff/ipc/test_i057/test_i057.c +++ b/api-tests/ff/ipc/test_i057/test_i057.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i057.h" @@ -39,7 +39,7 @@ int32_t client_test_psa_write_with_invalid_buffer_addr(security_t caller) psa_status_t status_of_call; val->print(PRINT_TEST, - "[Check1] Test psa_write with invalid buffer addr\n", 0); + "[Check 1] Test psa_write with invalid buffer addr\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); if (handle < 0) @@ -52,7 +52,7 @@ int32_t client_test_psa_write_with_invalid_buffer_addr(security_t caller) status_of_call = psa->call(handle, NULL, 0, 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); + val->print(PRINT_ERROR, "\tCall should failed but succeed\n", 0); status = VAL_STATUS_SPM_FAILED; diff --git a/api-tests/ff/ipc/test_i057/test_supp_i057.c b/api-tests/ff/ipc/test_i057/test_supp_i057.c index 54d6dada..2b0510e4 100644 --- a/api-tests/ff/ipc/test_i057/test_supp_i057.c +++ b/api-tests/ff/ipc/test_i057/test_supp_i057.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_write_with_invalid_buffer_addr(void); @@ -34,52 +39,64 @@ int32_t server_test_psa_write_with_invalid_buffer_addr(void) miscellaneous_desc_t *misc_desc; memory_desc_t *memory_desc; - /* 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. + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } - /* Selection of invalid buffer addr: + /* + * Selection of invalid buffer addr: + * * if (ISOLATION_LEVEL > 1) * buffer = driver_mmio_region_base; * else * buffer = NULL; */ - status = val_target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_MISCELLANEOUS, + 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_err_check_set(TEST_CHECKPOINT_NUM(202), status)) + if (val->err_check_set(TEST_CHECKPOINT_NUM(202), status)) { - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } if (misc_desc->implemented_psa_firmware_isolation_level > LEVEL1) { - status = val_target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_MEMORY, + status = val->target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_MEMORY, MEMORY_DRIVER_PARTITION_MMIO, 0), (uint8_t **)&memory_desc, (uint32_t *)sizeof(memory_desc_t)); - if (val_err_check_set(TEST_CHECKPOINT_NUM(203), status)) + if (val->err_check_set(TEST_CHECKPOINT_NUM(203), status)) { - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } @@ -87,46 +104,46 @@ int32_t server_test_psa_write_with_invalid_buffer_addr(void) } /* Accept the connection */ - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); /* Server psa_call */ - status = val_process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(204), status)) + status = val->process_call_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val->err_check_set(TEST_CHECKPOINT_NUM(204), status)) { - psa_reply(msg.handle, -2); + 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(205), status)) + status = val->set_boot_flag(BOOT_EXPECTED_NS); + if (val->err_check_set(TEST_CHECKPOINT_NUM(205), status)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); - psa_reply(msg.handle, -3); + 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 buffer addr */ - psa_write(msg.handle, 0, (void *)buffer, msg.in_size[0]); + /* Test check- psa_write with invalid buffer addr, call should panic */ + psa->write(msg.handle, 0, (void *)buffer, msg.out_size[0]); /* shouldn't have reached here */ - val_print(PRINT_ERROR, - "\tpsa_write with invalid buffer should failed but successed\n", 0); + val->print(PRINT_ERROR, + "\tpsa_write with invalid buffer should failed but succeed\n", 0); /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); } status = VAL_STATUS_SPM_FAILED; - psa_reply(msg.handle, -4); + psa->reply(msg.handle, -4); } } - val_err_check_set(TEST_CHECKPOINT_NUM(206), status); - status = ((val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + val->err_check_set(TEST_CHECKPOINT_NUM(206), status); + status = ((val->process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) ? VAL_STATUS_ERROR : status); - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); return status; } diff --git a/api-tests/ff/ipc/test_i058/test_i058.c b/api-tests/ff/ipc/test_i058/test_i058.c index 49e2a92a..6639cd07 100644 --- a/api-tests/ff/ipc/test_i058/test_i058.c +++ b/api-tests/ff/ipc/test_i058/test_i058.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i058.h" @@ -40,7 +40,7 @@ int32_t client_test_psa_doorbell_signal(security_t caller) #endif val->print(PRINT_TEST, - "[Check1] Test PSA_DOORBELL signal\n", 0); + "[Check 1] Test PSA_DOORBELL signal\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); if (handle < 0) @@ -60,7 +60,8 @@ int32_t client_test_psa_doorbell_signal(security_t caller) val->print(PRINT_ERROR, "\tpsa_wait didn't receive doorbell signal\n", 0); } - /* Wait for doorball notification again to check - + /* + * Wait for doorball notification again to check - * Doorbell should remain asserted until psa_clear is called. */ signals = psa_wait(PSA_DOORBELL, PSA_BLOCK); diff --git a/api-tests/ff/ipc/test_i058/test_supp_i058.c b/api-tests/ff/ipc/test_i058/test_supp_i058.c index e3ed3b9e..9a01ef73 100644 --- a/api-tests/ff/ipc/test_i058/test_supp_i058.c +++ b/api-tests/ff/ipc/test_i058/test_supp_i058.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_doorbell_signal(void); @@ -32,32 +37,32 @@ int32_t server_test_psa_doorbell_signal(void) psa_msg_t msg = {0}; /* Serve psa_connect */ - status = val_process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); - if (val_err_check_set(TEST_CHECKPOINT_NUM(201), status)) + 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); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return status; } /* Accept the connection */ - psa_reply(msg.handle, PSA_SUCCESS); + psa->reply(msg.handle, PSA_SUCCESS); if (msg.client_id > 0) { /* Doorbell signal to client partititon */ - psa_notify(msg.client_id); + psa->notify(msg.client_id); } else { status = VAL_STATUS_SPM_FAILED; - val_print(PRINT_ERROR, "Caller is from non-secure\n", 0); + val->print(PRINT_ERROR, "Caller is from non-secure\n", 0); } /* Serve psa_close */ - status = ((val_process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) + status = ((val->process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg)) ? VAL_STATUS_ERROR : status); - val_err_check_set(TEST_CHECKPOINT_NUM(202), status); - psa_reply(msg.handle, PSA_SUCCESS); + val->err_check_set(TEST_CHECKPOINT_NUM(202), status); + psa->reply(msg.handle, PSA_SUCCESS); return status; } diff --git a/api-tests/ff/ipc/test_i059/test_i059.c b/api-tests/ff/ipc/test_i059/test_i059.c index 6ebebf9a..0f4c3b78 100644 --- a/api-tests/ff/ipc/test_i059/test_i059.c +++ b/api-tests/ff/ipc/test_i059/test_i059.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i059.h" diff --git a/api-tests/ff/ipc/test_i059/test_supp_i059.c b/api-tests/ff/ipc/test_i059/test_supp_i059.c index 94d629d0..ed08784c 100644 --- a/api-tests/ff/ipc/test_i059/test_supp_i059.c +++ b/api-tests/ff/ipc/test_i059/test_supp_i059.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; #define NEG_PART_ID -10 @@ -31,37 +36,47 @@ server_test_t test_i059_server_tests_list[] = { int32_t server_test_psa_notify_with_neg_part_id(void) { - /* 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. + val->print(PRINT_TEST, "[Check 1] Test psa_notify with neg partition id\n", 0); + + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - val_print(PRINT_TEST, "[Check1] Test psa_notify with neg partition id\n", 0); - /* Setting boot.state before test check */ - if (val_set_boot_flag(BOOT_EXPECTED_NS)) + if (val->set_boot_flag(BOOT_EXPECTED_NS)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); return VAL_STATUS_ERROR; } - /* psa_notify_with_neg_part_id */ - psa_notify(NEG_PART_ID); + /* psa_notify_with_neg_part_id, call should panic */ + psa->notify(NEG_PART_ID); /* shouldn't have reached here */ - val_print(PRINT_ERROR, "\tpsa_notify(-ve_part_ip) check failed\n", 0); + val->print(PRINT_ERROR, "\tpsa->notify(-ve_part_ip) check failed\n", 0); /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); return VAL_STATUS_ERROR; } diff --git a/api-tests/ff/ipc/test_i060/test_i060.c b/api-tests/ff/ipc/test_i060/test_i060.c index 3c69b4cb..d2f17a41 100644 --- a/api-tests/ff/ipc/test_i060/test_i060.c +++ b/api-tests/ff/ipc/test_i060/test_i060.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i060.h" diff --git a/api-tests/ff/ipc/test_i060/test_supp_i060.c b/api-tests/ff/ipc/test_i060/test_supp_i060.c index 9ce7886c..05b3b7fb 100644 --- a/api-tests/ff/ipc/test_i060/test_supp_i060.c +++ b/api-tests/ff/ipc/test_i060/test_supp_i060.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; #define INVALID_POSITIVE_PART_ID 200 @@ -31,37 +36,47 @@ server_test_t test_i060_server_tests_list[] = { int32_t server_test_psa_notify_with_invalid_pos_part_id(void) { - /* 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. + val->print(PRINT_TEST, "[Check 1] Test psa_notify with invalid positive part-id\n", 0); + + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - val_print(PRINT_TEST, "[Check1] Test psa_notify with invalid positive part-id\n", 0); - /* Setting boot.state before test check */ - if (val_set_boot_flag(BOOT_EXPECTED_NS)) + if (val->set_boot_flag(BOOT_EXPECTED_NS)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); return VAL_STATUS_ERROR; } - /* psa_notify_with_invalid_pos_part_id */ - psa_notify(INVALID_POSITIVE_PART_ID); + /* psa_notify_with_invalid_pos_part_id, call should panic */ + psa->notify(INVALID_POSITIVE_PART_ID); /* shouldn't have reached here */ - val_print(PRINT_ERROR, "\tpsa_notify(invalid_+ve__part_ip) check failed\n", 0); + val->print(PRINT_ERROR, "\tpsa->notify(invalid_+ve__part_ip) check failed\n", 0); /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); return VAL_STATUS_ERROR; } diff --git a/api-tests/ff/ipc/test_i061/test_i061.c b/api-tests/ff/ipc/test_i061/test_i061.c index 1f9cd4e1..4be21a69 100644 --- a/api-tests/ff/ipc/test_i061/test_i061.c +++ b/api-tests/ff/ipc/test_i061/test_i061.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i061.h" diff --git a/api-tests/ff/ipc/test_i061/test_supp_i061.c b/api-tests/ff/ipc/test_i061/test_supp_i061.c index 720903f1..15421142 100644 --- a/api-tests/ff/ipc/test_i061/test_supp_i061.c +++ b/api-tests/ff/ipc/test_i061/test_supp_i061.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_clear_at_unasserted_doorbell_sig(void); @@ -29,37 +34,47 @@ server_test_t test_i061_server_tests_list[] = { int32_t server_test_psa_clear_at_unasserted_doorbell_sig(void) { - /* 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. + val->print(PRINT_TEST, "[Check 1] Test psa_clear at unasserted doorbell sig\n", 0); + + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - val_print(PRINT_TEST, "[Check1] Test psa_clear at unasserted doorbell sig\n", 0); - /* Setting boot.state before test check */ - if (val_set_boot_flag(BOOT_EXPECTED_NS)) + if (val->set_boot_flag(BOOT_EXPECTED_NS)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); return VAL_STATUS_ERROR; } - /* psa_clear when unasserted doorbell sig */ - psa_clear(); + /* psa_clear when unasserted doorbell sig, call should panic */ + psa->clear(); /* shouldn't have reached here */ - val_print(PRINT_ERROR, "\tpsa_clear() check failed for unasserted doorbel signal\n", 0); + val->print(PRINT_ERROR, "\tpsa->clear() check failed for unasserted doorbel signal\n", 0); /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); return VAL_STATUS_ERROR; } diff --git a/api-tests/ff/ipc/test_i062/test_i062.c b/api-tests/ff/ipc/test_i062/test_i062.c index fb28fe17..f9ff4cb4 100644 --- a/api-tests/ff/ipc/test_i062/test_i062.c +++ b/api-tests/ff/ipc/test_i062/test_i062.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i062.h" diff --git a/api-tests/ff/ipc/test_i062/test_supp_i062.c b/api-tests/ff/ipc/test_i062/test_supp_i062.c index 8029c0a9..30e2d5d0 100644 --- a/api-tests/ff/ipc/test_i062/test_supp_i062.c +++ b/api-tests/ff/ipc/test_i062/test_supp_i062.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; /* signal bit-31 should be unassigned to server partition */ #define INVALID_SIGNAL_MASK 0x80000000 @@ -32,37 +37,47 @@ server_test_t test_i062_server_tests_list[] = { int32_t server_test_psa_wait_with_unassigned_signal(void) { - /* 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. + val->print(PRINT_TEST, "[Check 1] Test psa_wait with unassigned signal\n", 0); + + /* + * This test checks for the PROGRAMMER ERROR condition for the PSA API. API's respond to + * PROGRAMMER ERROR could be either to return appropriate status code or panic the caller. + * When a Secure Partition panics, the SPE cannot continue normal execution, as defined + * in this specification. The behavior of the SPM following a Secure Partition panic is + * IMPLEMENTATION DEFINED- Arm recommends that the SPM causes the system to restart in + * this situation. Refer PSA-FF for more information on panic. + * For the cases where, SPM cannot capable to reboot the system (just hangs or power down), + * a watchdog timer set by val_test_init can reboot the system on timeout event. This will + * tests continuity and able to jump to next tests. Therefore, each test who checks for + * PROGRAMMER ERROR condition, expects system to get reset either by SPM or watchdog set by + * the test harness function. * - * 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. + * + * To decide, a reboot happened as intended by 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. */ - val_print(PRINT_TEST, "[Check1] Test psa_wait with unassigned signal\n", 0); - /* Setting boot.state before test check */ - if (val_set_boot_flag(BOOT_EXPECTED_NS)) + if (val->set_boot_flag(BOOT_EXPECTED_NS)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); return VAL_STATUS_ERROR; } - /* psa_wait with signal mask that doesn't include any assigned signal*/ - psa_wait(INVALID_SIGNAL_MASK, PSA_POLL); + /* psa_wait with signal mask that doesn't include any assigned signal, call should panic */ + psa->wait(INVALID_SIGNAL_MASK, PSA_POLL); /* shouldn't have reached here */ - val_print(PRINT_ERROR, "\tpsa_wait() check failed for unassigned signal\n", 0); + val->print(PRINT_ERROR, "\tpsa_wait() check failed for unassigned signal\n", 0); /* Resetting boot.state to catch unwanted reboot */ - if (val_set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) + if (val->set_boot_flag(BOOT_EXPECTED_BUT_FAILED)) { - val_print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); + val->print(PRINT_ERROR, "\tFailed to set boot flag after check\n", 0); return VAL_STATUS_ERROR; } diff --git a/api-tests/ff/ipc/test_i063/test_i063.c b/api-tests/ff/ipc/test_i063/test_i063.c index be8d79e6..8853f3d0 100644 --- a/api-tests/ff/ipc/test_i063/test_i063.c +++ b/api-tests/ff/ipc/test_i063/test_i063.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i063.h" @@ -35,11 +35,11 @@ int32_t client_test_psa_wait_signal_mask(security_t caller) { psa_handle_t handle = 0; - val->print(PRINT_TEST, "[Check1] Test psa_wait signal mask\n", 0); + val->print(PRINT_TEST, "[Check 1] Test psa_wait signal mask\n", 0); handle = psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1); - if (handle != PSA_CONNECTION_REFUSED) + if (handle != PSA_ERROR_CONNECTION_REFUSED) { val->print(PRINT_ERROR, "psa_connect failed -1\n", 0); return VAL_STATUS_INVALID_HANDLE; @@ -47,7 +47,7 @@ int32_t client_test_psa_wait_signal_mask(security_t caller) handle = psa->connect(SERVER_RELAX_MINOR_VERSION_SID, 1); - if (handle != PSA_CONNECTION_REFUSED) + if (handle != PSA_ERROR_CONNECTION_REFUSED) { val->print(PRINT_ERROR, "psa_connect failed -2\n", 0); return VAL_STATUS_INVALID_HANDLE; diff --git a/api-tests/ff/ipc/test_i063/test_supp_i063.c b/api-tests/ff/ipc/test_i063/test_supp_i063.c index 99990e20..d7555e97 100644 --- a/api-tests/ff/ipc/test_i063/test_supp_i063.c +++ b/api-tests/ff/ipc/test_i063/test_supp_i063.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_wait_signal_mask(void); @@ -34,68 +39,70 @@ int32_t server_test_psa_wait_signal_mask(void) psa_signal_t signal_mask = (SERVER_UNSPECIFED_MINOR_V_SIG | SERVER_RELAX_MINOR_VERSION_SIG); /* Debug print */ - val_err_check_set(TEST_CHECKPOINT_NUM(211), VAL_STATUS_SUCCESS); + val->err_check_set(TEST_CHECKPOINT_NUM(211), VAL_STATUS_SUCCESS); - /* Notify client partition to make SERVER_SECURE_CONNECT_ONLY_SID connection request. - * This connection request act as irritator to psa_wait(signal_mask) call and it is used + /* + * Notify client partition to make SERVER_SECURE_CONNECT_ONLY_SID connection request. + * This connection request act as irritator to psa->wait(signal_mask) call and it is used * to cover the rule - Signals that are not in signal_mask should be ignored by psa_wait. * This means, during the following while loop, returned signal vaule should not be * SERVER_SECURE_CONNECT_ONLY_SIG as this signal is not part of signal_mask. */ - psa_notify(CLIENT_PARTITION); + psa->notify(CLIENT_PARTITION); while (loop_cnt != 0) { - signals = psa_wait(signal_mask, PSA_BLOCK); + signals = psa->wait(signal_mask, PSA_BLOCK); - /* Rule - Returned signals value must be subset signals indicated in the signal_mask. - * + /* + * Rule - Returned signals value must be subset signals indicated in the signal_mask. * This mean signal value should be either SERVER_UNSPECIFED_MINOR_V_SIG * or SERVER_RELAX_MINOR_VERSION_SIG. */ if (((signals & signal_mask) == 0) && ((signals | signal_mask) != signal_mask)) { - val_print(PRINT_ERROR, + val->print(PRINT_ERROR, "psa_wait-1 returned with invalid signal value = 0x%x\n", signals); return VAL_STATUS_ERROR; } else if (signals & SERVER_UNSPECIFED_MINOR_V_SIG) { - if (psa_get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) + if (psa->get(SERVER_UNSPECIFED_MINOR_V_SIG, &msg) != PSA_SUCCESS) continue; loop_cnt--; - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); } else if (signals & SERVER_RELAX_MINOR_VERSION_SIG) { - if (psa_get(SERVER_RELAX_MINOR_VERSION_SIG, &msg) != PSA_SUCCESS) + if (psa->get(SERVER_RELAX_MINOR_VERSION_SIG, &msg) != PSA_SUCCESS) continue; loop_cnt--; - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); } } /* Debug print */ - val_err_check_set(TEST_CHECKPOINT_NUM(212), VAL_STATUS_SUCCESS); + val->err_check_set(TEST_CHECKPOINT_NUM(212), VAL_STATUS_SUCCESS); wait: - /* At the end, completes the starved connection + /* + * At the end, completes the starved connection * request of SERVER_SECURE_CONNECT_ONLY_SID. */ - signals = psa_wait(SERVER_SECURE_CONNECT_ONLY_SIG, PSA_BLOCK); + signals = psa->wait(SERVER_SECURE_CONNECT_ONLY_SIG, PSA_BLOCK); if ((signals & SERVER_SECURE_CONNECT_ONLY_SIG) == 0) { - val_print(PRINT_ERROR, + val->print(PRINT_ERROR, "psa_wait-2 returned with invalid signal value = 0x%x\n", signals); return VAL_STATUS_ERROR; } - if (psa_get(SERVER_SECURE_CONNECT_ONLY_SIG, &msg) != PSA_SUCCESS) + if (psa->get(SERVER_SECURE_CONNECT_ONLY_SIG, &msg) != PSA_SUCCESS) goto wait; - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); return VAL_STATUS_SUCCESS; } diff --git a/api-tests/ff/ipc/test_i064/test_i064.c b/api-tests/ff/ipc/test_i064/test_i064.c index 46fe3c99..f037cfa3 100644 --- a/api-tests/ff/ipc/test_i064/test_i064.c +++ b/api-tests/ff/ipc/test_i064/test_i064.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i064.h" @@ -33,20 +33,20 @@ client_test_t test_i064_client_tests_list[] = { int32_t client_test_psa_eoi_with_non_intr_signal(security_t caller) { - psa_handle_t handle; - test_intr_fn_id_t test_intr_fn_id = TEST_PSA_EOI_WITH_NON_INTR_SIGNAL; + psa_handle_t handle; + driver_test_fn_id_t driver_test_fn_id = TEST_PSA_EOI_WITH_NON_INTR_SIGNAL; /* * The interrupt related test check is captured in driver_partition.c as this is the * only partition in test suite that holds the interrupt line. The interrupt test check - * is invoked by client by calling to TEST_INTR_SID RoT service of driver partition that + * is invoked by client by calling to DRIVER_TEST_SID RoT service of driver partition that * hold the test check. */ - val->print(PRINT_TEST, "[Check1] Test psa_eoi with non-interrupt signal\n", 0); + val->print(PRINT_TEST, "[Check 1] Test psa_eoi with non-interrupt signal\n", 0); - /* Connect to TEST_INTR_SID */ - handle = psa->connect(TEST_INTR_SID, 1); + /* Connect to DRIVER_TEST_SID */ + handle = psa->connect(DRIVER_TEST_SID, 1); if (handle < 0) { val->print(PRINT_ERROR, "\t psa_connect failed. handle=0x%x\n", handle); @@ -54,11 +54,11 @@ int32_t client_test_psa_eoi_with_non_intr_signal(security_t caller) } /* Execute driver function related to TEST_PSA_EOI_WITH_NON_INTR_SIGNAL */ - psa_invec invec = {&test_intr_fn_id, sizeof(test_intr_fn_id)}; + psa_invec invec = {&driver_test_fn_id, sizeof(driver_test_fn_id)}; psa->call(handle, &invec, 1, NULL, 0); psa->close(handle); - /* The expectation is that driver partition hang and control never reaches here. */ + /* The expectation is that driver partition get panic and control never reaches here. */ return VAL_STATUS_SPM_FAILED; } diff --git a/api-tests/ff/ipc/test_i064/test_supp_i064.c b/api-tests/ff/ipc/test_i064/test_supp_i064.c index 5eb11439..5c486a74 100644 --- a/api-tests/ff/ipc/test_i064/test_supp_i064.c +++ b/api-tests/ff/ipc/test_i064/test_supp_i064.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_eoi_with_non_intr_signal(void); diff --git a/api-tests/ff/ipc/test_i065/test_i065.c b/api-tests/ff/ipc/test_i065/test_i065.c index 92cdeda2..15081fb2 100644 --- a/api-tests/ff/ipc/test_i065/test_i065.c +++ b/api-tests/ff/ipc/test_i065/test_i065.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i065.h" @@ -33,20 +33,20 @@ client_test_t test_i065_client_tests_list[] = { int32_t client_test_psa_eoi_with_unasserted_signal(security_t caller) { - psa_handle_t handle; - test_intr_fn_id_t test_intr_fn_id = TEST_PSA_EOI_WITH_UNASSERTED_SIGNAL; + psa_handle_t handle; + driver_test_fn_id_t driver_test_fn_id = TEST_PSA_EOI_WITH_UNASSERTED_SIGNAL; /* * The interrupt related test check is captured in driver_partition.c as this is the * only partition in test suite that holds the interrupt line. The interrupt test check - * is invoked by client by calling to TEST_INTR_SID RoT service of driver partition that + * is invoked by client by calling to DRIVER_TEST_SID RoT service of driver partition that * hold the test check. */ - val->print(PRINT_TEST, "[Check1] Test psa_eoi with multiple signal\n", 0); + val->print(PRINT_TEST, "[Check 1] Test psa_eoi with multiple signal\n", 0); - /* Connect to TEST_INTR_SID */ - handle = psa->connect(TEST_INTR_SID, 1); + /* Connect to DRIVER_TEST_SID */ + handle = psa->connect(DRIVER_TEST_SID, 1); if (handle < 0) { val->print(PRINT_ERROR, "\t psa_connect failed. handle=0x%x\n", handle); @@ -54,11 +54,11 @@ int32_t client_test_psa_eoi_with_unasserted_signal(security_t caller) } /* Execute driver function related to TEST_PSA_EOI_WITH_UNASSERTED_SIGNAL */ - psa_invec invec = {&test_intr_fn_id, sizeof(test_intr_fn_id)}; + psa_invec invec = {&driver_test_fn_id, sizeof(driver_test_fn_id)}; psa->call(handle, &invec, 1, NULL, 0); psa->close(handle); - /* The expectation is that driver partition hang and control never reaches here. */ + /* The expectation is that driver partition get panic and control never reaches here. */ return VAL_STATUS_SPM_FAILED; } diff --git a/api-tests/ff/ipc/test_i065/test_supp_i065.c b/api-tests/ff/ipc/test_i065/test_supp_i065.c index 1388840c..5c1d8e03 100644 --- a/api-tests/ff/ipc/test_i065/test_supp_i065.c +++ b/api-tests/ff/ipc/test_i065/test_supp_i065.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_eoi_with_non_intr_signal(void); diff --git a/api-tests/ff/ipc/test_i066/test_i066.c b/api-tests/ff/ipc/test_i066/test_i066.c index fb110d68..c452dc0d 100644 --- a/api-tests/ff/ipc/test_i066/test_i066.c +++ b/api-tests/ff/ipc/test_i066/test_i066.c @@ -20,7 +20,7 @@ #include "val_target.h" #else #include "val_client_defs.h" -#include "val_partition_common.h" +#include "val_service_defs.h" #endif #include "test_i066.h" @@ -33,20 +33,20 @@ client_test_t test_i066_client_tests_list[] = { int32_t client_test_psa_eoi_with_multiple_signals(security_t caller) { - psa_handle_t handle; - test_intr_fn_id_t test_intr_fn_id = TEST_PSA_EOI_WITH_MULTIPLE_SIGNALS; + psa_handle_t handle; + driver_test_fn_id_t driver_test_fn_id = TEST_PSA_EOI_WITH_MULTIPLE_SIGNALS; /* * The interrupt related test check is captured in driver_partition.c as this is the * only partition in test suite that holds the interrupt line. The interrupt test check - * is invoked by client by calling to TEST_INTR_SID RoT service of driver partition that + * is invoked by client by calling to DRIVER_TEST_SID RoT service of driver partition that * hold the test check. */ - val->print(PRINT_TEST, "[Check1] Test psa_eoi with multiple signals\n", 0); + val->print(PRINT_TEST, "[Check 1] Test psa_eoi with multiple signals\n", 0); - /* Connect to TEST_INTR_SID */ - handle = psa->connect(TEST_INTR_SID, 1); + /* Connect to DRIVER_TEST_SID */ + handle = psa->connect(DRIVER_TEST_SID, 1); if (handle < 0) { val->print(PRINT_ERROR, "\t psa_connect failed. handle=0x%x\n", handle); @@ -54,11 +54,11 @@ int32_t client_test_psa_eoi_with_multiple_signals(security_t caller) } /* Execute driver function related to TEST_PSA_EOI_WITH_MULTIPLE_SIGNALS */ - psa_invec invec = {&test_intr_fn_id, sizeof(test_intr_fn_id)}; + psa_invec invec = {&driver_test_fn_id, sizeof(driver_test_fn_id)}; psa->call(handle, &invec, 1, NULL, 0); psa->close(handle); - /* The expectation is that driver partition hang and control never reaches here. */ + /* The expectation is that driver partition get panic and control never reaches here. */ return VAL_STATUS_SPM_FAILED; } diff --git a/api-tests/ff/ipc/test_i066/test_supp_i066.c b/api-tests/ff/ipc/test_i066/test_supp_i066.c index 8748951e..9b64caa9 100644 --- a/api-tests/ff/ipc/test_i066/test_supp_i066.c +++ b/api-tests/ff/ipc/test_i066/test_supp_i066.c @@ -15,8 +15,13 @@ * limitations under the License. **/ -#include "val/common/val_client_defs.h" -#include "val/spe/val_partition_common.h" +#include "val_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; int32_t server_test_psa_eoi_with_non_intr_signal(void); diff --git a/api-tests/ff/ipc/test_i067/source.mk b/api-tests/ff/ipc/test_i067/source.mk new file mode 100644 index 00000000..a3472640 --- /dev/null +++ b/api-tests/ff/ipc/test_i067/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i067.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i067.c test_supp_i067.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = \ No newline at end of file diff --git a/api-tests/ff/ipc/test_i067/test_entry.c b/api-tests/ff/ipc/test_i067/test_entry.c new file mode 100644 index 00000000..0d350ab7 --- /dev/null +++ b/api-tests/ff/ipc/test_i067/test_entry.c @@ -0,0 +1,52 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i067.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 67) +#define TEST_DESC "Testing dynamic memory allocation\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val; +psa_api_t *psa; + +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/api-tests/ff/ipc/test_i067/test_i067.c b/api-tests/ff/ipc/test_i067/test_i067.c new file mode 100644 index 00000000..6e6f27f8 --- /dev/null +++ b/api-tests/ff/ipc/test_i067/test_i067.c @@ -0,0 +1,65 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i067.h" + +#if (SP_HEAP_MEM_SUPP == 1) +void *malloc(size_t size); +void free(void *ptr); +#endif + +client_test_t test_i067_client_tests_list[] = { + NULL, + client_test_dynamic_mem_alloc_fn, + NULL, +}; + +int32_t client_test_dynamic_mem_alloc_fn(security_t caller) +{ + /* Check heap memory support available to secure partition */ +#if (SP_HEAP_MEM_SUPP == 1) + uint8_t *buffer; + + val->print(PRINT_TEST, "[Check 1] Test dynamic memory allocation\n", 0); + + /* If heap_size field is not specified in the manifest then the SPM can assume the size is 0 */ + buffer = (uint8_t *)malloc(sizeof(uint8_t) * 8); + if (buffer != NULL) + { + val->print(PRINT_ERROR, "\tmalloc failed for unspecified heap size\n", 0); + return VAL_STATUS_SPM_FAILED; + } + +#else + + val->print(PRINT_TEST, "[Check 1] Test dynamic memory allocation\n", 0); + + /* Skip the test */ + val->print(PRINT_ERROR, "\tSkipping test as heap memory not supported\n", 0); + return RESULT_SKIP(VAL_STATUS_HEAP_NOT_AVAILABLE); + +#endif + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i067/test_i067.h b/api-tests/ff/ipc/test_i067/test_i067.h new file mode 100644 index 00000000..f84da6e4 --- /dev/null +++ b/api-tests/ff/ipc/test_i067/test_i067.h @@ -0,0 +1,37 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I067_CLIENT_TESTS_H_ +#define _TEST_I067_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i067) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i067_client_tests_list[]; + +int32_t client_test_dynamic_mem_alloc_fn(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i067/test_supp_i067.c b/api-tests/ff/ipc/test_i067/test_supp_i067.c new file mode 100644 index 00000000..b6d0779b --- /dev/null +++ b/api-tests/ff/ipc/test_i067/test_supp_i067.c @@ -0,0 +1,127 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +#define SERVER_HEAP_SIZE 0x100 /* The size is same as heap_size field in manifest */ + +#if (SP_HEAP_MEM_SUPP == 1) +void *malloc(size_t size); +void free(void *ptr); +void *realloc(void *ptr, size_t size); +#endif + +int32_t server_test_dynamic_mem_alloc_fn(void); + +server_test_t test_i067_server_tests_list[] = { + NULL, + server_test_dynamic_mem_alloc_fn, + NULL, +}; + +int32_t server_test_dynamic_mem_alloc_fn(void) +{ + /* Perform checks only if heap memory support available to secure partition */ +#if (SP_HEAP_MEM_SUPP == 1) + uint8_t *buffer, *buffer1; + uint8_t cmpbuff[SERVER_HEAP_SIZE] = {0}; + + memset((uint8_t *)cmpbuff, 0, SERVER_HEAP_SIZE); + + /* Allocate whole heap memory size */ + buffer = (uint8_t *)malloc(sizeof(uint8_t) * SERVER_HEAP_SIZE); + if (buffer == NULL) + { + val->print(PRINT_ERROR, "\tmalloc failed for full memory allocation\n", 0); + return VAL_STATUS_SPM_FAILED; + } + + /* Check for zero init by malloc() */ + if (memcmp(buffer, cmpbuff, SERVER_HEAP_SIZE)) + { + val->print(PRINT_ERROR, "\tUnequal data in compared buffers-1\n", 0); + return VAL_STATUS_SPM_FAILED; + } + + /* Check for heap memory over run */ + buffer1 = (uint8_t *)malloc(sizeof(uint8_t) * 8); + if (buffer1 != NULL) + { + val->print(PRINT_ERROR, "\tmalloc failed for over mem alloc\n", 0); + return VAL_STATUS_SPM_FAILED; + } + + /* Set buffer to non-zero value */ + memset((uint8_t *)buffer, 1, SERVER_HEAP_SIZE); + + /* Free up the memory */ + free(buffer); + + /* Check for memory scrub by free() */ + if (*buffer == 1) + { + val->print(PRINT_ERROR, "\tUnequal data in compared buffers-2\n", 0); + return VAL_STATUS_SPM_FAILED; + } + + /* Allocate 32 byte memory to test relloac */ + buffer = (uint8_t *)malloc(sizeof(uint8_t) * 32); + if (buffer == NULL) + { + val->print(PRINT_ERROR, "\tmalloc failed\n", 0); + return VAL_STATUS_SPM_FAILED; + } + + /* Set buffer to non-zero value */ + memset((uint8_t *)buffer, 1, 32); + memset((uint8_t *)cmpbuff, 1, 32); + + /* Re-allocate the buffer, Size = 64 byte */ + buffer1 = (uint8_t *)realloc(buffer, (sizeof(uint8_t) * 64)); + + /* Check older object is deallocated */ + if (memcmp(buffer, (cmpbuff + 64), 32)) + { + val->print(PRINT_ERROR, "\tUnequal data in compared buffers-3\n", 0); + return VAL_STATUS_SPM_FAILED; + } + + /* Check new object has previous data of older object */ + if (memcmp(buffer1, cmpbuff, 32)) + { + val->print(PRINT_ERROR, "\tUnequal data in compared buffers-4\n", 0); + return VAL_STATUS_SPM_FAILED; + } + + /* Check new allocated size is zero init */ + if (memcmp((buffer1 + 32), (cmpbuff + 32), 32)) + { + val->print(PRINT_ERROR, "\tUnequal data in compared buffers-5\n", 0); + return VAL_STATUS_SPM_FAILED; + } + + free(buffer1); + +#endif + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i068/source.mk b/api-tests/ff/ipc/test_i068/source.mk new file mode 100644 index 00000000..d6cb0d7b --- /dev/null +++ b/api-tests/ff/ipc/test_i068/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i068.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i068.c test_supp_i068.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = \ No newline at end of file diff --git a/api-tests/ff/ipc/test_i068/test_entry.c b/api-tests/ff/ipc/test_i068/test_entry.c new file mode 100644 index 00000000..c328f8e2 --- /dev/null +++ b/api-tests/ff/ipc/test_i068/test_entry.c @@ -0,0 +1,52 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i068.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 68) +#define TEST_DESC "Testing Instr execution from writable memory\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val; +psa_api_t *psa; + +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/api-tests/ff/ipc/test_i068/test_i068.c b/api-tests/ff/ipc/test_i068/test_i068.c new file mode 100644 index 00000000..0ab92dc0 --- /dev/null +++ b/api-tests/ff/ipc/test_i068/test_i068.c @@ -0,0 +1,94 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i068.h" + +#define NO_OF_BYTES 16 +typedef void (*fptr_t)(void); +void test_i068_dummy_func(void); +fptr_t fptr; + +char opcode[NO_OF_BYTES] = {0}; // Data memory + +client_test_t test_i068_client_tests_list[] = { + NULL, + client_test_instr_exec_from_writable_mem, + NULL, +}; + +static void copy_mem(void *addr, void *data, size_t l) +{ + int i; + char *src = (char *)data; + char *dest = (char *)addr; + + for (i = 0; i < l; i++) + { + dest[i] = src[i]; + } +} + +/* Empty function to create opcode data set */ +void test_i068_dummy_func(void) +{ +} + +int32_t client_test_instr_exec_from_writable_mem(security_t caller) +{ + val->print(PRINT_TEST, "[Check 1] Test Instr execution from writable memory\n", 0); + + /* + * Copy test_i068_dummy_func function code into data memory + * Assuming function size to be 32 bytes max + */ + copy_mem(&opcode, &test_i068_dummy_func, NO_OF_BYTES); + + /* Point function pointer to data memory */ + fptr = (fptr_t) opcode; + val->print(PRINT_DEBUG, "\t&opcode = 0x%x\n", (uint32_t) &opcode); + val->print(PRINT_DEBUG, "\tfptr = 0x%x\n",(uint32_t) fptr); + + /* 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; + } + + /* Check - Execute opcode from data memory. This should generate internal fault */ + fptr(); + + 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); + } + + return VAL_STATUS_ERROR; +} + + diff --git a/api-tests/ff/ipc/test_i068/test_i068.h b/api-tests/ff/ipc/test_i068/test_i068.h new file mode 100644 index 00000000..4426f389 --- /dev/null +++ b/api-tests/ff/ipc/test_i068/test_i068.h @@ -0,0 +1,37 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I068_CLIENT_TESTS_H_ +#define _TEST_I068_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i068) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i068_client_tests_list[]; + +int32_t client_test_instr_exec_from_writable_mem(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i068/test_supp_i068.c b/api-tests/ff/ipc/test_i068/test_supp_i068.c new file mode 100644 index 00000000..6fa87d37 --- /dev/null +++ b/api-tests/ff/ipc/test_i068/test_supp_i068.c @@ -0,0 +1,37 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_instr_exec_from_writable_mem(void); + +server_test_t test_i068_server_tests_list[] = { + NULL, + server_test_instr_exec_from_writable_mem, + NULL, +}; + +int32_t server_test_instr_exec_from_writable_mem(void) +{ + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i069/source.mk b/api-tests/ff/ipc/test_i069/source.mk new file mode 100644 index 00000000..1a745a51 --- /dev/null +++ b/api-tests/ff/ipc/test_i069/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i069.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i069.c test_supp_i069.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = \ No newline at end of file diff --git a/api-tests/ff/ipc/test_i069/test_entry.c b/api-tests/ff/ipc/test_i069/test_entry.c new file mode 100644 index 00000000..e1411a41 --- /dev/null +++ b/api-tests/ff/ipc/test_i069/test_entry.c @@ -0,0 +1,52 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i069.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 69) +#define TEST_DESC "Testing write to code space\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val; +psa_api_t *psa; + +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/api-tests/ff/ipc/test_i069/test_i069.c b/api-tests/ff/ipc/test_i069/test_i069.c new file mode 100644 index 00000000..53afce3a --- /dev/null +++ b/api-tests/ff/ipc/test_i069/test_i069.c @@ -0,0 +1,68 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i069.h" + +client_test_t test_i069_client_tests_list[] = { + NULL, + client_test_write_to_code_space, + NULL, +}; + +int32_t client_test_write_to_code_space(security_t caller) +{ + int32_t *p; + + val->print(PRINT_TEST, "[Check 1] Test write to code space\n", 0); + + /* 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; + } + + p = (int32_t *)&client_test_write_to_code_space; + + /* Check - Write to code memory. This should generate internal fault */ + *p = 0x0; + + if (*p == (int32_t)client_test_write_to_code_space) + { + /* This means, write ignored */ + return VAL_STATUS_SUCCESS; + } + val->print(PRINT_ERROR, "\tWrite to code memory check failed\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; +} + + diff --git a/api-tests/ff/ipc/test_i069/test_i069.h b/api-tests/ff/ipc/test_i069/test_i069.h new file mode 100644 index 00000000..fbb3e50d --- /dev/null +++ b/api-tests/ff/ipc/test_i069/test_i069.h @@ -0,0 +1,37 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I069_CLIENT_TESTS_H_ +#define _TEST_I069_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i069) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i069_client_tests_list[]; + +int32_t client_test_write_to_code_space(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i069/test_supp_i069.c b/api-tests/ff/ipc/test_i069/test_supp_i069.c new file mode 100644 index 00000000..29a3ca1e --- /dev/null +++ b/api-tests/ff/ipc/test_i069/test_supp_i069.c @@ -0,0 +1,37 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_write_to_code_space(void); + +server_test_t test_i069_server_tests_list[] = { + NULL, + server_test_write_to_code_space, + NULL, +}; + +int32_t server_test_write_to_code_space(void) +{ + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i070/source.mk b/api-tests/ff/ipc/test_i070/source.mk new file mode 100644 index 00000000..f399229b --- /dev/null +++ b/api-tests/ff/ipc/test_i070/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i070.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i070.c test_supp_i070.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = \ No newline at end of file diff --git a/api-tests/ff/ipc/test_i070/test_entry.c b/api-tests/ff/ipc/test_i070/test_entry.c new file mode 100644 index 00000000..7ea558cf --- /dev/null +++ b/api-tests/ff/ipc/test_i070/test_entry.c @@ -0,0 +1,52 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i070.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 70) +#define TEST_DESC "Testing write to const data\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val; +psa_api_t *psa; + +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/api-tests/ff/ipc/test_i070/test_i070.c b/api-tests/ff/ipc/test_i070/test_i070.c new file mode 100644 index 00000000..45c5dc97 --- /dev/null +++ b/api-tests/ff/ipc/test_i070/test_i070.c @@ -0,0 +1,75 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i070.h" + +client_test_t test_i070_client_tests_list[] = { + NULL, + client_test_write_to_const_data, + NULL, +}; + +int32_t client_test_write_to_const_data(security_t caller) +{ + const char *string = "This text should be in RO space"; + char *p; + + val->print(PRINT_TEST, "[Check 1] Test write to const data\n", 0); + + /* 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; + } + + p = (char *) string; + val->print(PRINT_DEBUG, "\tstring[0] = 0x%x\n", (uint32_t) string); + val->print(PRINT_DEBUG, "\tp[0] = 0x%x\n", (uint32_t) p); + + /* + * Check - Write to const data string[0]. + * This should generate internal fault or write ignored + */ + p[0] = 'a'; + + if (p[0] == 'T') + { + /* This means, write ignored */ + return VAL_STATUS_SUCCESS; + } + + val->print(PRINT_ERROR, "\tWrite to constant data check failed\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; +} + + diff --git a/api-tests/ff/ipc/test_i070/test_i070.h b/api-tests/ff/ipc/test_i070/test_i070.h new file mode 100644 index 00000000..4189b898 --- /dev/null +++ b/api-tests/ff/ipc/test_i070/test_i070.h @@ -0,0 +1,37 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I070_CLIENT_TESTS_H_ +#define _TEST_I070_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i070) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i070_client_tests_list[]; + +int32_t client_test_write_to_const_data(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i070/test_supp_i070.c b/api-tests/ff/ipc/test_i070/test_supp_i070.c new file mode 100644 index 00000000..51090fad --- /dev/null +++ b/api-tests/ff/ipc/test_i070/test_supp_i070.c @@ -0,0 +1,37 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_write_to_const_data(void); + +server_test_t test_i070_server_tests_list[] = { + NULL, + server_test_write_to_const_data, + NULL, +}; + +int32_t server_test_write_to_const_data(void) +{ + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i071/source.mk b/api-tests/ff/ipc/test_i071/source.mk new file mode 100644 index 00000000..c6eade09 --- /dev/null +++ b/api-tests/ff/ipc/test_i071/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i071.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i071.c test_supp_i071.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = diff --git a/api-tests/ff/ipc/test_i071/test_entry.c b/api-tests/ff/ipc/test_i071/test_entry.c new file mode 100644 index 00000000..26050b2a --- /dev/null +++ b/api-tests/ff/ipc/test_i071/test_entry.c @@ -0,0 +1,52 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i071.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 71) +#define TEST_DESC "Testing memory manipulation functions\n" +TEST_PUBLISH(TEST_NUM, test_entry); +val_api_t *val; +psa_api_t *psa; + +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/api-tests/ff/ipc/test_i071/test_i071.c b/api-tests/ff/ipc/test_i071/test_i071.c new file mode 100644 index 00000000..1c56a022 --- /dev/null +++ b/api-tests/ff/ipc/test_i071/test_i071.c @@ -0,0 +1,120 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i071.h" + +#define BUFF_SIZE 32 + +client_test_t test_i071_client_tests_list[] = { + NULL, + client_test_mem_manipulation_fn, + NULL, +}; + +int32_t client_test_mem_manipulation_fn(security_t caller) +{ + uint8_t buffer[BUFF_SIZE] = {0}; + uint8_t buffer1[BUFF_SIZE] = {0}; + int i; + + val->print(PRINT_TEST, "[Check 1] Test memory manipulation functions\n", 0); + + /* Test memset()- Set all buffer elements with zero */ + memset((uint8_t *)buffer, 0, BUFF_SIZE); + for (i = 0; i < BUFF_SIZE; i++) + { + if (buffer[i] != 0) + { + val->print(PRINT_ERROR, + "\tmemset() failed, found buffer with non-zero value = %x\n", buffer[i]); + return VAL_STATUS_ERROR; + } + } + + /* Test memset()- Set all buffer elements with 0x3 */ + memset((uint8_t *)buffer, 0x3, BUFF_SIZE); + for (i = 0; i < BUFF_SIZE; i++) + { + if (buffer[i] != 0x3) + { + val->print(PRINT_ERROR, + "\tmemset() failed, found buffer with wrong value = %x\n", buffer[i]); + return VAL_STATUS_ERROR; + } + } + + /* Test memcpy(), copy buffer to buffer1 */ + memcpy((uint8_t *)buffer1, (uint8_t *)buffer, BUFF_SIZE); + for (i = 0; i < BUFF_SIZE; i++) + { + if (buffer1[i] != 0x3) + { + val->print(PRINT_ERROR, + "\tmemcpy() failed, found buffer with wrong value = %x\n", buffer1[i]); + return VAL_STATUS_ERROR; + } + } + + /* Test memcmp() with equal buffer */ + if (memcmp((uint8_t *)buffer1, (uint8_t *)buffer, BUFF_SIZE)) + { + val->print(PRINT_ERROR, + "\tmemcmp() failed for two equal buffer\n", 0); + return VAL_STATUS_ERROR; + } + + buffer1[0] = 0x4; + /* Test memcmp() with unequal buffer */ + if (!memcmp((uint8_t *)buffer1, (uint8_t *)buffer, BUFF_SIZE)) + { + val->print(PRINT_ERROR, + "\tmemcmp() failed for two unequal buffer\n", 0); + return VAL_STATUS_ERROR; + } + + /* buffer[0-4] = 1 and buffer[5-31] = 2 */ + memset((uint8_t *)buffer, 1, BUFF_SIZE); + memset((uint8_t *)buffer+5, 2, BUFF_SIZE-5); + + /* Test memmove(), expected result is buffer[0-9] = 1 and buffer[10-31] = 2 */ + memmove((uint8_t *)buffer+5, (uint8_t *)buffer, BUFF_SIZE-5); + for (i = 0; i < BUFF_SIZE; i++) + { + if ((buffer[i] != 0x1) && (i < 10)) + { + val->print(PRINT_ERROR, "\tmemmove() failed-1\n", 0); + return VAL_STATUS_ERROR; + } + else if ((buffer[i] != 0x2) && (i >10)) + { + val->print(PRINT_ERROR, "\tmemmove() failed-2\n", 0); + return VAL_STATUS_ERROR; + } + } + + return VAL_STATUS_SUCCESS; +} + + diff --git a/api-tests/ff/ipc/test_i071/test_i071.h b/api-tests/ff/ipc/test_i071/test_i071.h new file mode 100644 index 00000000..bf1bacf6 --- /dev/null +++ b/api-tests/ff/ipc/test_i071/test_i071.h @@ -0,0 +1,37 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I071_CLIENT_TESTS_H_ +#define _TEST_I071_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i071) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i071_client_tests_list[]; + +int32_t client_test_mem_manipulation_fn(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i071/test_supp_i071.c b/api-tests/ff/ipc/test_i071/test_supp_i071.c new file mode 100644 index 00000000..7ce17d89 --- /dev/null +++ b/api-tests/ff/ipc/test_i071/test_supp_i071.c @@ -0,0 +1,37 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_mem_manipulation_fn(void); + +server_test_t test_i071_server_tests_list[] = { + NULL, + server_test_mem_manipulation_fn, + NULL, +}; + +int32_t server_test_mem_manipulation_fn(void) +{ + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i072/source.mk b/api-tests/ff/ipc/test_i072/source.mk new file mode 100644 index 00000000..7d3e4953 --- /dev/null +++ b/api-tests/ff/ipc/test_i072/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i072.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i072.c test_supp_i072.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = diff --git a/api-tests/ff/ipc/test_i072/test_entry.c b/api-tests/ff/ipc/test_i072/test_entry.c new file mode 100644 index 00000000..28e6ac4d --- /dev/null +++ b/api-tests/ff/ipc/test_i072/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i072.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 72) +#define TEST_DESC "Testing NSPE access to APP-RoT data\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_i072_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/api-tests/ff/ipc/test_i072/test_i072.c b/api-tests/ff/ipc/test_i072/test_i072.c new file mode 100644 index 00000000..aa586e70 --- /dev/null +++ b/api-tests/ff/ipc/test_i072/test_i072.c @@ -0,0 +1,125 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" +#endif + +#include "test_i072.h" + +client_test_t test_i072_client_tests_list[] = { + NULL, + client_test_nspe_read_app_rot_variable, + client_test_nspe_write_app_rot_variable, + NULL, +}; + +static int32_t get_secure_partition_address(addr_t *addr) +{ + psa_handle_t handle = 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; + } + + /* Get App-RoT address */ + psa_outvec outvec[1] = {{addr, sizeof(addr_t)}}; + if (psa->call(handle, NULL, 0, outvec, 1) != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tmsg request failed\n", 0); + return VAL_STATUS_CALL_FAILED; + } + + psa->close(handle); + return VAL_STATUS_SUCCESS; +} + +int32_t client_test_nspe_read_app_rot_variable(security_t caller) +{ + addr_t app_rot_addr; + uint32_t data = 0x1234; + + val->print(PRINT_TEST, "[Check 1] Test NSPE reading APP-RoT data\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&app_rot_addr))) + return VAL_STATUS_ERROR; + + /* Setting boot.state before test check */ + if (val->set_boot_flag(BOOT_EXPECTED_REENTER_TEST)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Read Application RoT global variable address. + * This should generate internal fault or ignore the read. + */ + data = *(uint32_t *)app_rot_addr; + + /* Did read ignore? */ + if (data == 0x1234) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected read to fault but it didn't\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; +} + +int32_t client_test_nspe_write_app_rot_variable(security_t caller) +{ + addr_t app_rot_addr; + uint32_t data = 0x1234; + + val->print(PRINT_TEST, "[Check 2] Test NSPE writing APP-RoT data\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&app_rot_addr))) + return VAL_STATUS_ERROR; + + /* 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); + return VAL_STATUS_ERROR; + } + + /* Write Application RoT global variable address. + * This should generate internal fault or ignore the write. + */ + *(uint32_t *)app_rot_addr = (uint32_t)data; + + /* Handshake with server to decide write status */ + if ((psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1)) > 0) + { + val->print(PRINT_ERROR, "\tExpected connection to fail but succeed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i072/test_i072.h b/api-tests/ff/ipc/test_i072/test_i072.h new file mode 100644 index 00000000..586f8e51 --- /dev/null +++ b/api-tests/ff/ipc/test_i072/test_i072.h @@ -0,0 +1,38 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I072_CLIENT_TESTS_H_ +#define _TEST_I072_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i072) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i072_client_tests_list[]; + +int32_t client_test_nspe_read_app_rot_variable(security_t); +int32_t client_test_nspe_write_app_rot_variable(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i072/test_supp_i072.c b/api-tests/ff/ipc/test_i072/test_supp_i072.c new file mode 100644 index 00000000..05cf9181 --- /dev/null +++ b/api-tests/ff/ipc/test_i072/test_supp_i072.c @@ -0,0 +1,113 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_nspe_read_app_rot_variable(void); +int32_t server_test_nspe_write_app_rot_variable(void); + +#define DATA_VALUE 0x5467 + +/* Application RoT data region */ +volatile uint32_t g_test_i072 = DATA_VALUE; + +server_test_t test_i072_server_tests_list[] = { + NULL, + server_test_nspe_read_app_rot_variable, + server_test_nspe_write_app_rot_variable, + NULL, +}; + +static int32_t send_secure_partition_address(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_ERROR_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); + return status; + } + + /* Send Application RoT data address - global variable */ + psa->write(msg.handle, 0, (void *)&g_test_i072, sizeof(g_test_i072)); + psa->reply(msg.handle, PSA_SUCCESS); + + status = val->process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val->err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + return status; + } + psa->reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_SUCCESS; +} + +int32_t server_test_nspe_read_app_rot_variable(void) +{ + return send_secure_partition_address(); +} + +int32_t server_test_nspe_write_app_rot_variable(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + status = send_secure_partition_address(); + if (VAL_ERROR(status)) + return status; + + /* Wait for write to get performed by client */ + status = val->process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val->err_check_set(TEST_CHECKPOINT_NUM(204), status)) + { + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); + return status; + } + + /* Connection request is just for handshake, reject connection anyways */ + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); + + /* Reached here means there could be write succeed or ignored */ + if (g_test_i072 == DATA_VALUE) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected write to fault but it didn't\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_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i073/source.mk b/api-tests/ff/ipc/test_i073/source.mk new file mode 100644 index 00000000..2cea6c5f --- /dev/null +++ b/api-tests/ff/ipc/test_i073/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i073.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i073.c test_supp_i073.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = diff --git a/api-tests/ff/ipc/test_i073/test_entry.c b/api-tests/ff/ipc/test_i073/test_entry.c new file mode 100644 index 00000000..74b38179 --- /dev/null +++ b/api-tests/ff/ipc/test_i073/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i073.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 73) +#define TEST_DESC "Testing NSPE access to APP-RoT stack\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_i073_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/api-tests/ff/ipc/test_i073/test_i073.c b/api-tests/ff/ipc/test_i073/test_i073.c new file mode 100644 index 00000000..9172ec48 --- /dev/null +++ b/api-tests/ff/ipc/test_i073/test_i073.c @@ -0,0 +1,125 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" +#endif + +#include "test_i073.h" + +client_test_t test_i073_client_tests_list[] = { + NULL, + client_test_nspe_read_app_rot_stack, + client_test_nspe_write_app_rot_stack, + NULL, +}; + +static int32_t get_secure_partition_address(addr_t *addr) +{ + psa_handle_t handle = 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; + } + + /* Get App-RoT address */ + psa_outvec outvec[1] = {{addr, sizeof(addr_t)}}; + if (psa->call(handle, NULL, 0, outvec, 1) != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tmsg request failed\n", 0); + return VAL_STATUS_CALL_FAILED; + } + + psa->close(handle); + return VAL_STATUS_SUCCESS; +} + +int32_t client_test_nspe_read_app_rot_stack(security_t caller) +{ + addr_t app_rot_addr; + uint32_t data = 0x1234; + + val->print(PRINT_TEST, "[Check 1] Test NSPE reading APP-RoT stack\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&app_rot_addr))) + return VAL_STATUS_ERROR; + + /* Setting boot.state before test check */ + if (val->set_boot_flag(BOOT_EXPECTED_REENTER_TEST)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Read Application RoT stack address. + * This should generate internal fault or ignore the read. + */ + data = *(uint32_t *)app_rot_addr; + + /* Did read ignore? */ + if (data == 0x1234) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected read to fault but it didn't\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; +} + +int32_t client_test_nspe_write_app_rot_stack(security_t caller) +{ + addr_t app_rot_addr; + uint32_t data = 0x1234; + + val->print(PRINT_TEST, "[Check 2] Test NSPE writing APP-RoT stack\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&app_rot_addr))) + return VAL_STATUS_ERROR; + + /* 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); + return VAL_STATUS_ERROR; + } + + /* Write Application RoT stack address. + * This should generate internal fault or ignore the write. + */ + *(uint32_t *)app_rot_addr = (uint32_t)data; + + /* Handshake with server to decide write status */ + if ((psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1)) > 0) + { + val->print(PRINT_ERROR, "\tExpected connection to fail but succeed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i073/test_i073.h b/api-tests/ff/ipc/test_i073/test_i073.h new file mode 100644 index 00000000..1badc212 --- /dev/null +++ b/api-tests/ff/ipc/test_i073/test_i073.h @@ -0,0 +1,38 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I073_CLIENT_TESTS_H_ +#define _TEST_I073_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i073) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i073_client_tests_list[]; + +int32_t client_test_nspe_read_app_rot_stack(security_t); +int32_t client_test_nspe_write_app_rot_stack(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i073/test_supp_i073.c b/api-tests/ff/ipc/test_i073/test_supp_i073.c new file mode 100644 index 00000000..114b0a2a --- /dev/null +++ b/api-tests/ff/ipc/test_i073/test_supp_i073.c @@ -0,0 +1,120 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_nspe_read_app_rot_stack(void); +int32_t server_test_nspe_write_app_rot_stack(void); + +#define DATA_VALUE 0x5467 + +server_test_t test_i073_server_tests_list[] = { + NULL, + server_test_nspe_read_app_rot_stack, + server_test_nspe_write_app_rot_stack, + NULL, +}; + +static int32_t send_secure_partition_address(addr_t *stack) +{ + 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_ERROR_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); + return status; + } + + /* Send Application RoT stack address */ + psa->write(msg.handle, 0, (void *)stack, sizeof(uint32_t)); + psa->reply(msg.handle, PSA_SUCCESS); + + status = val->process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val->err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + return status; + } + psa->reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_SUCCESS; +} + +int32_t server_test_nspe_read_app_rot_stack(void) +{ + /* Application RoT stack - local variable */ + uint32_t l_test_i073 = DATA_VALUE; + int32_t status = VAL_STATUS_SUCCESS; + + status = send_secure_partition_address(&l_test_i073); + + /* Dummy print to avoid compiler optimisation on local variable */ + val->print(PRINT_INFO, "\tData value 0x%x\n", l_test_i073); + return status; +} + +int32_t server_test_nspe_write_app_rot_stack(void) +{ + /* Application RoT stack - local variable */ + uint32_t l_test_i073 = DATA_VALUE; + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + status = send_secure_partition_address(&l_test_i073); + if (VAL_ERROR(status)) + return status; + + /* Wait for write to get performed by client */ + status = val->process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val->err_check_set(TEST_CHECKPOINT_NUM(204), status)) + { + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); + return status; + } + + /* Connection request is just for handshake, reject connection anyways */ + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); + + /* Reached here means there could be write succeed or ignored */ + if (l_test_i073 == DATA_VALUE) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected write to fault but it didn't\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_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i074/source.mk b/api-tests/ff/ipc/test_i074/source.mk new file mode 100644 index 00000000..e82e890e --- /dev/null +++ b/api-tests/ff/ipc/test_i074/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i074.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i074.c test_supp_i074.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = diff --git a/api-tests/ff/ipc/test_i074/test_entry.c b/api-tests/ff/ipc/test_i074/test_entry.c new file mode 100644 index 00000000..5468cc8b --- /dev/null +++ b/api-tests/ff/ipc/test_i074/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i074.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 74) +#define TEST_DESC "Testing NSPE access to APP-RoT heap\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_i074_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/api-tests/ff/ipc/test_i074/test_i074.c b/api-tests/ff/ipc/test_i074/test_i074.c new file mode 100644 index 00000000..8c58ca99 --- /dev/null +++ b/api-tests/ff/ipc/test_i074/test_i074.c @@ -0,0 +1,145 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" +#endif + +#include "test_i074.h" + +#define DATA_VALUE_ORG 0x11 +#define DATA_VALUE 0x12 +#define BUFFER_SIZE 0x4 + +client_test_t test_i074_client_tests_list[] = { + NULL, + client_test_nspe_read_app_rot_heap, + client_test_nspe_write_app_rot_heap, + NULL, +}; + +#if (SP_HEAP_MEM_SUPP == 1) +static int32_t get_secure_partition_address(addr_t *addr) +{ + psa_handle_t handle = 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; + } + + /* Get App-RoT address */ + psa_outvec outvec[1] = {{addr, BUFFER_SIZE}}; + if (psa->call(handle, NULL, 0, outvec, 1) != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tmsg request failed\n", 0); + return VAL_STATUS_CALL_FAILED; + } + + psa->close(handle); + return VAL_STATUS_SUCCESS; +} + +int32_t client_test_nspe_read_app_rot_heap(security_t caller) +{ + addr_t app_rot_addr; + uint8_t data = DATA_VALUE; + + val->print(PRINT_TEST, "[Check 1] Test NSPE reading APP-RoT heap\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&app_rot_addr))) + return VAL_STATUS_ERROR; + + /* Setting boot.state before test check */ + if (val->set_boot_flag(BOOT_EXPECTED_REENTER_TEST)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Read Application RoT heap address. + * This should generate internal fault or ignore the read. + */ + data = *(uint8_t *)app_rot_addr; + + /* Did read ignore? */ + if (data == DATA_VALUE) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected read to fault but it didn't\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; +} + +int32_t client_test_nspe_write_app_rot_heap(security_t caller) +{ + addr_t app_rot_addr; + uint8_t data = DATA_VALUE; + + val->print(PRINT_TEST, "[Check 2] Test NSPE writing APP-RoT heap\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&app_rot_addr))) + return VAL_STATUS_ERROR; + + /* 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); + return VAL_STATUS_ERROR; + } + + /* Write Application RoT heap address. + * This should generate internal fault or ignore the write. + */ + *(uint8_t *)app_rot_addr = (uint8_t)data; + + /* Handshake with server to decide write status */ + if ((psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1)) > 0) + { + val->print(PRINT_ERROR, "\tExpected connection to fail but succeed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + return VAL_STATUS_SUCCESS; +} +#else +int32_t client_test_nspe_read_app_rot_heap(security_t caller) +{ + val->print(PRINT_TEST, "[Check 1] Test NSPE reading APP-RoT heap\n", 0); + val->print(PRINT_ERROR, "\tSkipping test as heap memory not supported\n", 0); + return RESULT_SKIP(VAL_STATUS_HEAP_NOT_AVAILABLE); +} + +int32_t client_test_nspe_write_app_rot_heap(security_t caller) +{ + val->print(PRINT_TEST, "[Check 2] Test NSPE writing APP-RoT heap\n", 0); + val->print(PRINT_ERROR, "\tSkipping test as heap memory not supported\n", 0); + return RESULT_SKIP(VAL_STATUS_HEAP_NOT_AVAILABLE); +} +#endif diff --git a/api-tests/ff/ipc/test_i074/test_i074.h b/api-tests/ff/ipc/test_i074/test_i074.h new file mode 100644 index 00000000..f6d1d8d5 --- /dev/null +++ b/api-tests/ff/ipc/test_i074/test_i074.h @@ -0,0 +1,38 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I074_CLIENT_TESTS_H_ +#define _TEST_I074_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i074) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i074_client_tests_list[]; + +int32_t client_test_nspe_read_app_rot_heap(security_t); +int32_t client_test_nspe_write_app_rot_heap(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i074/test_supp_i074.c b/api-tests/ff/ipc/test_i074/test_supp_i074.c new file mode 100644 index 00000000..cbc790e7 --- /dev/null +++ b/api-tests/ff/ipc/test_i074/test_supp_i074.c @@ -0,0 +1,146 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_nspe_read_app_rot_heap(void); +int32_t server_test_nspe_write_app_rot_heap(void); + +#if (SP_HEAP_MEM_SUPP == 1) +void *malloc(size_t size); +void free(void *ptr); +#endif + +#define DATA_VALUE_ORG 0x11 +#define BUFFER_SIZE 0x4 + +server_test_t test_i074_server_tests_list[] = { + NULL, + server_test_nspe_read_app_rot_heap, + server_test_nspe_write_app_rot_heap, + NULL, +}; + +#if (SP_HEAP_MEM_SUPP == 1) +static int32_t send_secure_partition_address(uint8_t *heap) +{ + 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_ERROR_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); + return status; + } + + /* Send Application RoT heap address */ + psa->write(msg.handle, 0, (void *)heap, sizeof(BUFFER_SIZE)); + psa->reply(msg.handle, PSA_SUCCESS); + + status = val->process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val->err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + return status; + } + psa->reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_SUCCESS; +} + +int32_t server_test_nspe_read_app_rot_heap(void) +{ + /* Application RoT heap buffer */ + uint8_t *buffer; + int32_t status = VAL_STATUS_SUCCESS; + + buffer = (uint8_t *)malloc(sizeof(uint8_t) * BUFFER_SIZE); + memset((uint8_t *)buffer, DATA_VALUE_ORG, BUFFER_SIZE); + + status = send_secure_partition_address(buffer); + free(buffer); + + return status; +} + +int32_t server_test_nspe_write_app_rot_heap(void) +{ + /* Application RoT heap buffer */ + uint8_t *buffer; + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + buffer = (uint8_t *)malloc(sizeof(uint8_t) * BUFFER_SIZE); + memset((uint8_t *)buffer, DATA_VALUE_ORG, BUFFER_SIZE); + + status = send_secure_partition_address(buffer); + if (VAL_ERROR(status)) + return status; + + /* Wait for write to get performed by client */ + status = val->process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val->err_check_set(TEST_CHECKPOINT_NUM(204), status)) + { + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); + return status; + } + + /* Connection request is just for handshake, reject connection anyways */ + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); + + /* Reached here means there could be write succeed or ignored */ + if (buffer[0] == DATA_VALUE_ORG) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected write to fault but it didn't\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; + } + + free(buffer); + return VAL_STATUS_SUCCESS; +} +#else + +int32_t server_test_nspe_read_app_rot_heap(void) +{ + return RESULT_SKIP(VAL_STATUS_HEAP_NOT_AVAILABLE); +} + +int32_t server_test_nspe_write_app_rot_heap(void) +{ + return RESULT_SKIP(VAL_STATUS_HEAP_NOT_AVAILABLE); +} +#endif diff --git a/api-tests/ff/ipc/test_i075/source.mk b/api-tests/ff/ipc/test_i075/source.mk new file mode 100644 index 00000000..db83c871 --- /dev/null +++ b/api-tests/ff/ipc/test_i075/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i075.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i075.c test_supp_i075.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = diff --git a/api-tests/ff/ipc/test_i075/test_entry.c b/api-tests/ff/ipc/test_i075/test_entry.c new file mode 100644 index 00000000..1131a5ce --- /dev/null +++ b/api-tests/ff/ipc/test_i075/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i075.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 75) +#define TEST_DESC "Testing NSPE access to APP-RoT mmio\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_i075_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/api-tests/ff/ipc/test_i075/test_i075.c b/api-tests/ff/ipc/test_i075/test_i075.c new file mode 100644 index 00000000..62c7b6ab --- /dev/null +++ b/api-tests/ff/ipc/test_i075/test_i075.c @@ -0,0 +1,127 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" +#endif + +#include "test_i075.h" + +#define DATA_VALUE 0x1234 + +client_test_t test_i075_client_tests_list[] = { + NULL, + client_test_nspe_read_app_rot_mmio, + client_test_nspe_write_app_rot_mmio, + NULL, +}; + +static int32_t get_secure_partition_address(addr_t *addr) +{ + psa_handle_t handle = 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; + } + + /* Get App-RoT address */ + psa_outvec outvec[1] = {{addr, sizeof(addr_t)}}; + if (psa->call(handle, NULL, 0, outvec, 1) != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tmsg request failed\n", 0); + return VAL_STATUS_CALL_FAILED; + } + + psa->close(handle); + return VAL_STATUS_SUCCESS; +} + +int32_t client_test_nspe_read_app_rot_mmio(security_t caller) +{ + addr_t app_rot_addr; + uint32_t data = DATA_VALUE; + + val->print(PRINT_TEST, "[Check 1] Test NSPE reading APP-RoT mmio\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&app_rot_addr))) + return VAL_STATUS_ERROR; + + /* Setting boot.state before test check */ + if (val->set_boot_flag(BOOT_EXPECTED_REENTER_TEST)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Read Application RoT mmio address. + * This should generate internal fault or ignore the read. + */ + data = *(uint32_t *)app_rot_addr; + + /* Did read ignore? */ + if (data == DATA_VALUE) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected read to fault but it didn't\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; +} + +int32_t client_test_nspe_write_app_rot_mmio(security_t caller) +{ + addr_t app_rot_addr; + uint32_t data = DATA_VALUE; + + val->print(PRINT_TEST, "[Check 2] Test NSPE writing APP-RoT mmio\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&app_rot_addr))) + return VAL_STATUS_ERROR; + + /* 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); + return VAL_STATUS_ERROR; + } + + /* Write Application RoT mmio address. + * This should generate internal fault or ignore the write. + */ + *(uint32_t *)app_rot_addr = (uint32_t)data; + + /* Handshake with server to decide write status */ + if ((psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1)) > 0) + { + val->print(PRINT_ERROR, "\tExpected connection to fail but succeed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i075/test_i075.h b/api-tests/ff/ipc/test_i075/test_i075.h new file mode 100644 index 00000000..5cae303d --- /dev/null +++ b/api-tests/ff/ipc/test_i075/test_i075.h @@ -0,0 +1,38 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I075_CLIENT_TESTS_H_ +#define _TEST_I075_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i075) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i075_client_tests_list[]; + +int32_t client_test_nspe_read_app_rot_mmio(security_t); +int32_t client_test_nspe_write_app_rot_mmio(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i075/test_supp_i075.c b/api-tests/ff/ipc/test_i075/test_supp_i075.c new file mode 100644 index 00000000..b98fffba --- /dev/null +++ b/api-tests/ff/ipc/test_i075/test_supp_i075.c @@ -0,0 +1,143 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_nspe_read_app_rot_mmio(void); +int32_t server_test_nspe_write_app_rot_mmio(void); + +#define DATA_VALUE 0x5467 + +server_test_t test_i075_server_tests_list[] = { + NULL, + server_test_nspe_read_app_rot_mmio, + server_test_nspe_write_app_rot_mmio, + NULL, +}; + +static int32_t get_mmio_addr(addr_t *addr) +{ + memory_desc_t *memory_desc; + int32_t status = VAL_STATUS_SUCCESS; + + /* Get APP-ROT MMIO address */ + status = val->target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_MEMORY, + MEMORY_SERVER_PARTITION_MMIO, 0), + (uint8_t **)&memory_desc, + (uint32_t *)sizeof(memory_desc_t)); + if (val->err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + return status; + } + + *addr = memory_desc->start; + return VAL_STATUS_SUCCESS; +} + +static int32_t send_secure_partition_address(addr_t *stack) +{ + 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(202), status)) + { + psa->reply(msg.handle, PSA_ERROR_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(203), status)) + { + psa->reply(msg.handle, -2); + return status; + } + + /* Send Application RoT stack address */ + psa->write(msg.handle, 0, (void *)stack, sizeof(uint32_t)); + psa->reply(msg.handle, PSA_SUCCESS); + + status = val->process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val->err_check_set(TEST_CHECKPOINT_NUM(204), status)) + { + return status; + } + psa->reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_SUCCESS; +} + +int32_t server_test_nspe_read_app_rot_mmio(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + addr_t app_rot_addr; + + status = get_mmio_addr(&app_rot_addr); + if (VAL_ERROR(status)) + return status; + + return send_secure_partition_address(&app_rot_addr); +} + +int32_t server_test_nspe_write_app_rot_mmio(void) +{ + addr_t app_rot_addr; + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + status = get_mmio_addr(&app_rot_addr); + if (VAL_ERROR(status)) + return status; + + /* Initialise mmio address */ + *(uint32_t *)app_rot_addr = (uint32_t)DATA_VALUE; + status = send_secure_partition_address(&app_rot_addr); + if (VAL_ERROR(status)) + return status; + + /* Wait for write to get performed by client */ + status = val->process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val->err_check_set(TEST_CHECKPOINT_NUM(204), status)) + { + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); + return status; + } + + /* Connection request is just for handshake, reject connection anyways */ + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); + + /* Reached here means there could be write succeed or ignored */ + if (*(uint32_t *)app_rot_addr == (uint32_t)DATA_VALUE) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected write to fault but it didn't\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_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i076/source.mk b/api-tests/ff/ipc/test_i076/source.mk new file mode 100644 index 00000000..e98fc9db --- /dev/null +++ b/api-tests/ff/ipc/test_i076/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i076.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i076.c test_supp_i076.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = diff --git a/api-tests/ff/ipc/test_i076/test_entry.c b/api-tests/ff/ipc/test_i076/test_entry.c new file mode 100644 index 00000000..f26e4f87 --- /dev/null +++ b/api-tests/ff/ipc/test_i076/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i076.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 76) +#define TEST_DESC "Testing NSPE access to PSA-RoT data\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_i076_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/api-tests/ff/ipc/test_i076/test_i076.c b/api-tests/ff/ipc/test_i076/test_i076.c new file mode 100644 index 00000000..3fc077ba --- /dev/null +++ b/api-tests/ff/ipc/test_i076/test_i076.c @@ -0,0 +1,144 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" +#endif + +#include "test_i076.h" + +#define DATA_VALUE1 0x1234 + +client_test_t test_i076_client_tests_list[] = { + NULL, + client_test_nspe_read_psa_rot_variable, + client_test_nspe_write_psa_rot_variable, + NULL, +}; + +static int32_t get_secure_partition_address(psa_handle_t *handle, + addr_t *addr, + driver_test_fn_id_t test_fn_id) +{ + *handle = psa->connect(DRIVER_TEST_SID, 1); + if (*handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + /* Execute driver function related to TEST_ISOLATION_PSA_ROT_* */ + psa_invec invec[1] = {{&test_fn_id, sizeof(test_fn_id)}}; + psa_outvec outvec[1] = {{addr, sizeof(addr_t)}}; + if (psa->call(*handle, invec, 1, outvec, 1) != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tmsg request failed\n", 0); + return VAL_STATUS_CALL_FAILED; + } + + return VAL_STATUS_SUCCESS; +} + +static int32_t get_driver_status(psa_handle_t *handle) +{ + if (psa->call(*handle, NULL, 0, NULL, 0) != PSA_SUCCESS) + { + return VAL_STATUS_CALL_FAILED; + } + return VAL_STATUS_SUCCESS; +} + +static void close_driver_fn(psa_handle_t *handle) +{ + psa->close(*handle); +} + +int32_t client_test_nspe_read_psa_rot_variable(security_t caller) +{ + addr_t psa_rot_addr; + uint32_t data = DATA_VALUE1; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check 1] Test NSPE reading PSA-RoT data\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&handle, + &psa_rot_addr, + TEST_ISOLATION_PSA_ROT_DATA_RD))) + return VAL_STATUS_ERROR; + + close_driver_fn(&handle); + + /* Setting boot.state before test check */ + if (val->set_boot_flag(BOOT_EXPECTED_REENTER_TEST)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Read PSA RoT global variable address. + * This should generate internal fault or ignore the read. + */ + data = *(uint32_t *)psa_rot_addr; + + /* Did read ignore? */ + if (data == DATA_VALUE1) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected read to fault but it didn't\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; +} + +int32_t client_test_nspe_write_psa_rot_variable(security_t caller) +{ + addr_t psa_rot_addr; + uint32_t data = DATA_VALUE1; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check 2] Test NSPE writing PSA-RoT data\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&handle, + &psa_rot_addr, + TEST_ISOLATION_PSA_ROT_DATA_WR))) + return VAL_STATUS_ERROR; + + /* Write PSA RoT global variable address. + * This should generate internal fault or ignore the write. + */ + *(uint32_t *)psa_rot_addr = (uint32_t)data; + + /* Handshake with driver to decide write status */ + if (VAL_ERROR(get_driver_status(&handle))) + { + close_driver_fn(&handle); + return VAL_STATUS_DRIVER_FN_FAILED; + } + + close_driver_fn(&handle); + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i076/test_i076.h b/api-tests/ff/ipc/test_i076/test_i076.h new file mode 100644 index 00000000..1e5e11f5 --- /dev/null +++ b/api-tests/ff/ipc/test_i076/test_i076.h @@ -0,0 +1,38 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I076_CLIENT_TESTS_H_ +#define _TEST_I076_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i076) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i076_client_tests_list[]; + +int32_t client_test_nspe_read_psa_rot_variable(security_t); +int32_t client_test_nspe_write_psa_rot_variable(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i076/test_supp_i076.c b/api-tests/ff/ipc/test_i076/test_supp_i076.c new file mode 100644 index 00000000..c32ec2fb --- /dev/null +++ b/api-tests/ff/ipc/test_i076/test_supp_i076.c @@ -0,0 +1,44 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_nspe_read_psa_rot_variable(void); +int32_t server_test_nspe_write_psa_rot_variable(void); + +server_test_t test_i076_server_tests_list[] = { + NULL, + server_test_nspe_read_psa_rot_variable, + server_test_nspe_write_psa_rot_variable, + NULL, +}; + +int32_t server_test_nspe_read_psa_rot_variable(void) +{ + return VAL_STATUS_SUCCESS; +} + +int32_t server_test_nspe_write_psa_rot_variable(void) +{ + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i077/source.mk b/api-tests/ff/ipc/test_i077/source.mk new file mode 100644 index 00000000..e1abcb0e --- /dev/null +++ b/api-tests/ff/ipc/test_i077/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i077.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i077.c test_supp_i077.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = diff --git a/api-tests/ff/ipc/test_i077/test_entry.c b/api-tests/ff/ipc/test_i077/test_entry.c new file mode 100644 index 00000000..08fa84f7 --- /dev/null +++ b/api-tests/ff/ipc/test_i077/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i077.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 77) +#define TEST_DESC "Testing NSPE access to PSA-RoT stack\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_i077_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/api-tests/ff/ipc/test_i077/test_i077.c b/api-tests/ff/ipc/test_i077/test_i077.c new file mode 100644 index 00000000..5f4e4d74 --- /dev/null +++ b/api-tests/ff/ipc/test_i077/test_i077.c @@ -0,0 +1,145 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" +#endif + +#include "test_i077.h" + +#define DATA_VALUE1 0x1234 + +client_test_t test_i077_client_tests_list[] = { + NULL, + client_test_nspe_read_psa_rot_stack, + client_test_nspe_write_psa_rot_stack, + NULL, +}; + +static int32_t get_secure_partition_address(psa_handle_t *handle, + addr_t *addr, + driver_test_fn_id_t test_fn_id) +{ + *handle = psa->connect(DRIVER_TEST_SID, 1); + if (*handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + /* Execute driver function related to TEST_ISOLATION_PSA_ROT_DATA_RD */ + psa_invec invec[1] = {{&test_fn_id, sizeof(test_fn_id)}}; + psa_outvec outvec[1] = {{addr, sizeof(addr_t)}}; + if (psa->call(*handle, invec, 1, outvec, 1) != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tmsg request failed\n", 0); + return VAL_STATUS_CALL_FAILED; + } + + return VAL_STATUS_SUCCESS; +} + +static int32_t get_driver_status(psa_handle_t *handle) +{ + if (psa->call(*handle, NULL, 0, NULL, 0) != PSA_SUCCESS) + { + return VAL_STATUS_CALL_FAILED; + } + return VAL_STATUS_SUCCESS; +} + +static void close_driver_fn(psa_handle_t *handle) +{ + psa->close(*handle); +} + + +int32_t client_test_nspe_read_psa_rot_stack(security_t caller) +{ + addr_t psa_rot_addr; + uint32_t data = DATA_VALUE1; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check 1] Test NSPE reading PSA-RoT stack\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&handle, + &psa_rot_addr, + TEST_ISOLATION_PSA_ROT_STACK_RD))) + return VAL_STATUS_ERROR; + + close_driver_fn(&handle); + + /* Setting boot.state before test check */ + if (val->set_boot_flag(BOOT_EXPECTED_REENTER_TEST)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Read PSA RoT stack address. + * This should generate internal fault or ignore the read. + */ + data = *(uint32_t *)psa_rot_addr; + + /* Did read ignore? */ + if (data == DATA_VALUE1) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected read to fault but it didn't\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; +} + +int32_t client_test_nspe_write_psa_rot_stack(security_t caller) +{ + addr_t psa_rot_addr; + uint32_t data = DATA_VALUE1; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check 2] Test NSPE writing PSA-RoT stack\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&handle, + &psa_rot_addr, + TEST_ISOLATION_PSA_ROT_STACK_WR))) + return VAL_STATUS_ERROR; + + /* Write PSA RoT stack address. + * This should generate internal fault or ignore the write. + */ + *(uint32_t *)psa_rot_addr = (uint32_t)data; + + /* Handshake with driver to decide write status */ + if (VAL_ERROR(get_driver_status(&handle))) + { + close_driver_fn(&handle); + return VAL_STATUS_DRIVER_FN_FAILED; + } + + close_driver_fn(&handle); + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i077/test_i077.h b/api-tests/ff/ipc/test_i077/test_i077.h new file mode 100644 index 00000000..57a36a86 --- /dev/null +++ b/api-tests/ff/ipc/test_i077/test_i077.h @@ -0,0 +1,38 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I077_CLIENT_TESTS_H_ +#define _TEST_I077_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i077) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i077_client_tests_list[]; + +int32_t client_test_nspe_read_psa_rot_stack(security_t); +int32_t client_test_nspe_write_psa_rot_stack(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i077/test_supp_i077.c b/api-tests/ff/ipc/test_i077/test_supp_i077.c new file mode 100644 index 00000000..03a42175 --- /dev/null +++ b/api-tests/ff/ipc/test_i077/test_supp_i077.c @@ -0,0 +1,44 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_nspe_read_psa_rot_stack(void); +int32_t server_test_nspe_write_psa_rot_stack(void); + +server_test_t test_i077_server_tests_list[] = { + NULL, + server_test_nspe_read_psa_rot_stack, + server_test_nspe_write_psa_rot_stack, + NULL, +}; + +int32_t server_test_nspe_read_psa_rot_stack(void) +{ + return VAL_STATUS_SUCCESS; +} + +int32_t server_test_nspe_write_psa_rot_stack(void) +{ + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i078/source.mk b/api-tests/ff/ipc/test_i078/source.mk new file mode 100644 index 00000000..628a2821 --- /dev/null +++ b/api-tests/ff/ipc/test_i078/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i078.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i078.c test_supp_i078.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = diff --git a/api-tests/ff/ipc/test_i078/test_entry.c b/api-tests/ff/ipc/test_i078/test_entry.c new file mode 100644 index 00000000..d0ae79a1 --- /dev/null +++ b/api-tests/ff/ipc/test_i078/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i078.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 78) +#define TEST_DESC "Testing NSPE access to PSA-RoT heap\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_i078_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/api-tests/ff/ipc/test_i078/test_i078.c b/api-tests/ff/ipc/test_i078/test_i078.c new file mode 100644 index 00000000..338b781a --- /dev/null +++ b/api-tests/ff/ipc/test_i078/test_i078.c @@ -0,0 +1,162 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" +#endif + +#include "test_i078.h" + +#define DATA_VALUE_ORG 0x11 +#define DATA_VALUE 0x12 +#define BUFFER_SIZE 0x4 + +client_test_t test_i078_client_tests_list[] = { + NULL, + client_test_nspe_read_psa_rot_heap, + client_test_nspe_write_psa_rot_heap, + NULL, +}; + +#if (SP_HEAP_MEM_SUPP == 1) +static int32_t get_secure_partition_address(psa_handle_t *handle, + addr_t *addr, + driver_test_fn_id_t test_fn_id) +{ + *handle = psa->connect(DRIVER_TEST_SID, 1); + if (*handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + /* Execute driver function related to TEST_ISOLATION_PSA_ROT_DATA_RD */ + psa_invec invec[1] = {{&test_fn_id, sizeof(test_fn_id)}}; + psa_outvec outvec[1] = {{addr, sizeof(addr_t)}}; + if (psa->call(*handle, invec, 1, outvec, 1) != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tmsg request failed\n", 0); + return VAL_STATUS_CALL_FAILED; + } + + return VAL_STATUS_SUCCESS; +} + +static int32_t get_driver_status(psa_handle_t *handle) +{ + if (psa->call(*handle, NULL, 0, NULL, 0) != PSA_SUCCESS) + { + return VAL_STATUS_CALL_FAILED; + } + return VAL_STATUS_SUCCESS; +} + +static void close_driver_fn(psa_handle_t *handle) +{ + psa->close(*handle); +} + +int32_t client_test_nspe_read_psa_rot_heap(security_t caller) +{ + addr_t psa_rot_addr; + uint8_t data = DATA_VALUE; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check 1] Test NSPE reading PSA-RoT heap\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&handle, + &psa_rot_addr, + TEST_ISOLATION_PSA_ROT_HEAP_RD))) + return VAL_STATUS_ERROR; + + close_driver_fn(&handle); + + /* Setting boot.state before test check */ + if (val->set_boot_flag(BOOT_EXPECTED_REENTER_TEST)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Read PSA RoT heap address. + * This should generate internal fault or ignore the read. + */ + data = *(uint8_t *)psa_rot_addr; + + /* Did read ignore? */ + if (data == DATA_VALUE) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected read to fault but it didn't\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; +} + +int32_t client_test_nspe_write_psa_rot_heap(security_t caller) +{ + addr_t psa_rot_addr; + uint8_t data = DATA_VALUE; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check 2] Test NSPE writing PSA-RoT heap\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&handle, + &psa_rot_addr, + TEST_ISOLATION_PSA_ROT_HEAP_WR))) + return VAL_STATUS_ERROR; + + /* Write PSA RoT heap address. + * This should generate internal fault or ignore the write. + */ + *(uint8_t *)psa_rot_addr = (uint8_t)data; + + /* Handshake with driver to decide write status */ + if (VAL_ERROR(get_driver_status(&handle))) + { + close_driver_fn(&handle); + return VAL_STATUS_DRIVER_FN_FAILED; + } + + close_driver_fn(&handle); + return VAL_STATUS_SUCCESS; +} +#else +int32_t client_test_nspe_read_psa_rot_heap(security_t caller) +{ + val->print(PRINT_TEST, "[Check 1] Test NSPE reading PSA-RoT heap\n", 0); + val->print(PRINT_ERROR, "\tSkipping test as heap memory not supported\n", 0); + return RESULT_SKIP(VAL_STATUS_HEAP_NOT_AVAILABLE); +} + +int32_t client_test_nspe_write_psa_rot_heap(security_t caller) +{ + val->print(PRINT_TEST, "[Check 2] Test NSPE writing PSA-RoT heap\n", 0); + val->print(PRINT_ERROR, "\tSkipping test as heap memory not supported\n", 0); + return RESULT_SKIP(VAL_STATUS_HEAP_NOT_AVAILABLE); +} +#endif diff --git a/api-tests/ff/ipc/test_i078/test_i078.h b/api-tests/ff/ipc/test_i078/test_i078.h new file mode 100644 index 00000000..207f8d35 --- /dev/null +++ b/api-tests/ff/ipc/test_i078/test_i078.h @@ -0,0 +1,38 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I078_CLIENT_TESTS_H_ +#define _TEST_I078_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i078) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i078_client_tests_list[]; + +int32_t client_test_nspe_read_psa_rot_heap(security_t); +int32_t client_test_nspe_write_psa_rot_heap(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i078/test_supp_i078.c b/api-tests/ff/ipc/test_i078/test_supp_i078.c new file mode 100644 index 00000000..0b5f67f2 --- /dev/null +++ b/api-tests/ff/ipc/test_i078/test_supp_i078.c @@ -0,0 +1,44 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_nspe_read_psa_rot_heap(void); +int32_t server_test_nspe_write_psa_rot_heap(void); + +server_test_t test_i078_server_tests_list[] = { + NULL, + server_test_nspe_read_psa_rot_heap, + server_test_nspe_write_psa_rot_heap, + NULL, +}; + +int32_t server_test_nspe_read_psa_rot_heap(void) +{ + return VAL_STATUS_SUCCESS; +} + +int32_t server_test_nspe_write_psa_rot_heap(void) +{ + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i079/source.mk b/api-tests/ff/ipc/test_i079/source.mk new file mode 100644 index 00000000..09026a8a --- /dev/null +++ b/api-tests/ff/ipc/test_i079/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i079.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i079.c test_supp_i079.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = diff --git a/api-tests/ff/ipc/test_i079/test_entry.c b/api-tests/ff/ipc/test_i079/test_entry.c new file mode 100644 index 00000000..131a15b6 --- /dev/null +++ b/api-tests/ff/ipc/test_i079/test_entry.c @@ -0,0 +1,51 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i079.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 79) +#define TEST_DESC "Testing NSPE access to PSA-RoT mmio\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_i079_client_tests_list, TRUE); + if (VAL_ERROR(status)) + { + goto test_exit; + } + +test_exit: + val->test_exit(); +} diff --git a/api-tests/ff/ipc/test_i079/test_i079.c b/api-tests/ff/ipc/test_i079/test_i079.c new file mode 100644 index 00000000..11c66f8f --- /dev/null +++ b/api-tests/ff/ipc/test_i079/test_i079.c @@ -0,0 +1,144 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" +#endif + +#include "test_i079.h" + +#define DATA_VALUE 0x1234 + +client_test_t test_i079_client_tests_list[] = { + NULL, + client_test_nspe_read_psa_rot_mmio, + client_test_nspe_write_psa_rot_mmio, + NULL, +}; + +static int32_t get_secure_partition_address(psa_handle_t *handle, + addr_t *addr, + driver_test_fn_id_t test_fn_id) +{ + *handle = psa->connect(DRIVER_TEST_SID, 1); + if (*handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + /* Execute driver function related to TEST_ISOLATION_PSA_ROT_DATA_RD */ + psa_invec invec[1] = {{&test_fn_id, sizeof(test_fn_id)}}; + psa_outvec outvec[1] = {{addr, sizeof(addr_t)}}; + if (psa->call(*handle, invec, 1, outvec, 1) != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tmsg request failed\n", 0); + return VAL_STATUS_CALL_FAILED; + } + + return VAL_STATUS_SUCCESS; +} + +static int32_t get_driver_status(psa_handle_t *handle) +{ + if (psa->call(*handle, NULL, 0, NULL, 0) != PSA_SUCCESS) + { + return VAL_STATUS_CALL_FAILED; + } + return VAL_STATUS_SUCCESS; +} + +static void close_driver_fn(psa_handle_t *handle) +{ + psa->close(*handle); +} + +int32_t client_test_nspe_read_psa_rot_mmio(security_t caller) +{ + addr_t psa_rot_addr; + uint32_t data = DATA_VALUE; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check 1] Test NSPE reading PSA-RoT mmio\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&handle, + &psa_rot_addr, + TEST_ISOLATION_PSA_ROT_MMIO_RD))) + return VAL_STATUS_ERROR; + + close_driver_fn(&handle); + + /* Setting boot.state before test check */ + if (val->set_boot_flag(BOOT_EXPECTED_REENTER_TEST)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Read PSA RoT mmio address. + * This should generate internal fault or ignore the read. + */ + data = *(uint32_t *)psa_rot_addr; + + /* Did read ignore? */ + if (data == DATA_VALUE) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected read to fault but it didn't\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; +} + +int32_t client_test_nspe_write_psa_rot_mmio(security_t caller) +{ + addr_t psa_rot_addr; + uint32_t data = DATA_VALUE; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check 2] Test NSPE writing PSA-RoT mmio\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&handle, + &psa_rot_addr, + TEST_ISOLATION_PSA_ROT_MMIO_WR))) + return VAL_STATUS_ERROR; + + /* Write PSA RoT mmio address. + * This should generate internal fault or ignore the write. + */ + *(uint32_t *)psa_rot_addr = (uint32_t)data; + + /* Handshake with driver to decide write status */ + if (VAL_ERROR(get_driver_status(&handle))) + { + close_driver_fn(&handle); + return VAL_STATUS_DRIVER_FN_FAILED; + } + + close_driver_fn(&handle); + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i079/test_i079.h b/api-tests/ff/ipc/test_i079/test_i079.h new file mode 100644 index 00000000..c5ab49cf --- /dev/null +++ b/api-tests/ff/ipc/test_i079/test_i079.h @@ -0,0 +1,38 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I079_CLIENT_TESTS_H_ +#define _TEST_I079_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i079) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i079_client_tests_list[]; + +int32_t client_test_nspe_read_psa_rot_mmio(security_t); +int32_t client_test_nspe_write_psa_rot_mmio(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i079/test_supp_i079.c b/api-tests/ff/ipc/test_i079/test_supp_i079.c new file mode 100644 index 00000000..24a0f89f --- /dev/null +++ b/api-tests/ff/ipc/test_i079/test_supp_i079.c @@ -0,0 +1,46 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_nspe_read_psa_rot_mmio(void); +int32_t server_test_nspe_write_psa_rot_mmio(void); + +#define DATA_VALUE 0x5467 + +server_test_t test_i079_server_tests_list[] = { + NULL, + server_test_nspe_read_psa_rot_mmio, + server_test_nspe_write_psa_rot_mmio, + NULL, +}; + +int32_t server_test_nspe_read_psa_rot_mmio(void) +{ + return VAL_STATUS_SUCCESS; +} + +int32_t server_test_nspe_write_psa_rot_mmio(void) +{ + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i080/source.mk b/api-tests/ff/ipc/test_i080/source.mk new file mode 100644 index 00000000..0b19b917 --- /dev/null +++ b/api-tests/ff/ipc/test_i080/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i080.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i080.c test_supp_i080.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = diff --git a/api-tests/ff/ipc/test_i080/test_entry.c b/api-tests/ff/ipc/test_i080/test_entry.c new file mode 100644 index 00000000..ee50dd68 --- /dev/null +++ b/api-tests/ff/ipc/test_i080/test_entry.c @@ -0,0 +1,52 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i080.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 80) +#define TEST_DESC "Testing APP-RoT access to PSA-RoT data\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_L2, 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/api-tests/ff/ipc/test_i080/test_i080.c b/api-tests/ff/ipc/test_i080/test_i080.c new file mode 100644 index 00000000..bd1bd368 --- /dev/null +++ b/api-tests/ff/ipc/test_i080/test_i080.c @@ -0,0 +1,144 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" +#endif + +#include "test_i080.h" + +#define DATA_VALUE1 0x1234 + +client_test_t test_i080_client_tests_list[] = { + NULL, + client_test_app_rot_read_psa_rot_variable, + client_test_app_rot_write_psa_rot_variable, + NULL, +}; + +static int32_t get_secure_partition_address(psa_handle_t *handle, + addr_t *addr, + driver_test_fn_id_t test_fn_id) +{ + *handle = psa->connect(DRIVER_TEST_SID, 1); + if (*handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + /* Execute driver function related to TEST_ISOLATION_PSA_ROT_* */ + psa_invec invec[1] = {{&test_fn_id, sizeof(test_fn_id)}}; + psa_outvec outvec[1] = {{addr, sizeof(addr_t)}}; + if (psa->call(*handle, invec, 1, outvec, 1) != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tmsg request failed\n", 0); + return VAL_STATUS_CALL_FAILED; + } + + return VAL_STATUS_SUCCESS; +} + +static int32_t get_driver_status(psa_handle_t *handle) +{ + if (psa->call(*handle, NULL, 0, NULL, 0) != PSA_SUCCESS) + { + return VAL_STATUS_CALL_FAILED; + } + return VAL_STATUS_SUCCESS; +} + +static void close_driver_fn(psa_handle_t *handle) +{ + psa->close(*handle); +} + +int32_t client_test_app_rot_read_psa_rot_variable(security_t caller) +{ + addr_t psa_rot_addr; + uint32_t data = DATA_VALUE1; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check 1] Test APP-RoT reading PSA-RoT data\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&handle, + &psa_rot_addr, + TEST_ISOLATION_PSA_ROT_DATA_RD))) + return VAL_STATUS_ERROR; + + close_driver_fn(&handle); + + /* Setting boot.state before test check */ + if (val->set_boot_flag(BOOT_EXPECTED_REENTER_TEST)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Read PSA RoT global variable address. + * This should generate internal fault or ignore the read. + */ + data = *(uint32_t *)psa_rot_addr; + + /* Did read ignore? */ + if (data == DATA_VALUE1) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected read to fault but it didn't\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; +} + +int32_t client_test_app_rot_write_psa_rot_variable(security_t caller) +{ + addr_t psa_rot_addr; + uint32_t data = DATA_VALUE1; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check 2] Test APP-RoT writing PSA-RoT data\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&handle, + &psa_rot_addr, + TEST_ISOLATION_PSA_ROT_DATA_WR))) + return VAL_STATUS_ERROR; + + /* Write PSA RoT global variable address. + * This should generate internal fault or ignore the write. + */ + *(uint32_t *)psa_rot_addr = (uint32_t)data; + + /* Handshake with driver to decide write status */ + if (VAL_ERROR(get_driver_status(&handle))) + { + close_driver_fn(&handle); + return VAL_STATUS_DRIVER_FN_FAILED; + } + + close_driver_fn(&handle); + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i080/test_i080.h b/api-tests/ff/ipc/test_i080/test_i080.h new file mode 100644 index 00000000..fdbe7e91 --- /dev/null +++ b/api-tests/ff/ipc/test_i080/test_i080.h @@ -0,0 +1,38 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I080_CLIENT_TESTS_H_ +#define _TEST_I080_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i080) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i080_client_tests_list[]; + +int32_t client_test_app_rot_read_psa_rot_variable(security_t); +int32_t client_test_app_rot_write_psa_rot_variable(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i080/test_supp_i080.c b/api-tests/ff/ipc/test_i080/test_supp_i080.c new file mode 100644 index 00000000..cd022639 --- /dev/null +++ b/api-tests/ff/ipc/test_i080/test_supp_i080.c @@ -0,0 +1,44 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_app_rot_read_psa_rot_variable(void); +int32_t server_test_app_rot_write_psa_rot_variable(void); + +server_test_t test_i080_server_tests_list[] = { + NULL, + server_test_app_rot_read_psa_rot_variable, + server_test_app_rot_write_psa_rot_variable, + NULL, +}; + +int32_t server_test_app_rot_read_psa_rot_variable(void) +{ + return VAL_STATUS_SUCCESS; +} + +int32_t server_test_app_rot_write_psa_rot_variable(void) +{ + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i081/source.mk b/api-tests/ff/ipc/test_i081/source.mk new file mode 100644 index 00000000..f84c2cb6 --- /dev/null +++ b/api-tests/ff/ipc/test_i081/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i081.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i081.c test_supp_i081.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = diff --git a/api-tests/ff/ipc/test_i081/test_entry.c b/api-tests/ff/ipc/test_i081/test_entry.c new file mode 100644 index 00000000..2a69daf2 --- /dev/null +++ b/api-tests/ff/ipc/test_i081/test_entry.c @@ -0,0 +1,52 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i081.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 81) +#define TEST_DESC "Testing APP-RoT access to PSA-RoT stack\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_L2, 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/api-tests/ff/ipc/test_i081/test_i081.c b/api-tests/ff/ipc/test_i081/test_i081.c new file mode 100644 index 00000000..42c07658 --- /dev/null +++ b/api-tests/ff/ipc/test_i081/test_i081.c @@ -0,0 +1,145 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" +#endif + +#include "test_i081.h" + +#define DATA_VALUE1 0x1234 + +client_test_t test_i081_client_tests_list[] = { + NULL, + client_test_app_rot_read_psa_rot_stack, + client_test_app_rot_write_psa_rot_stack, + NULL, +}; + +static int32_t get_secure_partition_address(psa_handle_t *handle, + addr_t *addr, + driver_test_fn_id_t test_fn_id) +{ + *handle = psa->connect(DRIVER_TEST_SID, 1); + if (*handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + /* Execute driver function related to TEST_ISOLATION_PSA_ROT_DATA_RD */ + psa_invec invec[1] = {{&test_fn_id, sizeof(test_fn_id)}}; + psa_outvec outvec[1] = {{addr, sizeof(addr_t)}}; + if (psa->call(*handle, invec, 1, outvec, 1) != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tmsg request failed\n", 0); + return VAL_STATUS_CALL_FAILED; + } + + return VAL_STATUS_SUCCESS; +} + +static int32_t get_driver_status(psa_handle_t *handle) +{ + if (psa->call(*handle, NULL, 0, NULL, 0) != PSA_SUCCESS) + { + return VAL_STATUS_CALL_FAILED; + } + return VAL_STATUS_SUCCESS; +} + +static void close_driver_fn(psa_handle_t *handle) +{ + psa->close(*handle); +} + + +int32_t client_test_app_rot_read_psa_rot_stack(security_t caller) +{ + addr_t psa_rot_addr; + uint32_t data = DATA_VALUE1; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check 1] Test APP-RoT reading PSA-RoT stack\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&handle, + &psa_rot_addr, + TEST_ISOLATION_PSA_ROT_STACK_RD))) + return VAL_STATUS_ERROR; + + close_driver_fn(&handle); + + /* Setting boot.state before test check */ + if (val->set_boot_flag(BOOT_EXPECTED_REENTER_TEST)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Read PSA RoT stack address. + * This should generate internal fault or ignore the read. + */ + data = *(uint32_t *)psa_rot_addr; + + /* Did read ignore? */ + if (data == DATA_VALUE1) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected read to fault but it didn't\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; +} + +int32_t client_test_app_rot_write_psa_rot_stack(security_t caller) +{ + addr_t psa_rot_addr; + uint32_t data = DATA_VALUE1; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check 2] Test APP-RoT writing PSA-RoT stack\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&handle, + &psa_rot_addr, + TEST_ISOLATION_PSA_ROT_STACK_WR))) + return VAL_STATUS_ERROR; + + /* Write PSA RoT stack address. + * This should generate internal fault or ignore the write. + */ + *(uint32_t *)psa_rot_addr = (uint32_t)data; + + /* Handshake with driver to decide write status */ + if (VAL_ERROR(get_driver_status(&handle))) + { + close_driver_fn(&handle); + return VAL_STATUS_DRIVER_FN_FAILED; + } + + close_driver_fn(&handle); + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i081/test_i081.h b/api-tests/ff/ipc/test_i081/test_i081.h new file mode 100644 index 00000000..c62a89bb --- /dev/null +++ b/api-tests/ff/ipc/test_i081/test_i081.h @@ -0,0 +1,38 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I081_CLIENT_TESTS_H_ +#define _TEST_I081_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i081) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i081_client_tests_list[]; + +int32_t client_test_app_rot_read_psa_rot_stack(security_t); +int32_t client_test_app_rot_write_psa_rot_stack(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i081/test_supp_i081.c b/api-tests/ff/ipc/test_i081/test_supp_i081.c new file mode 100644 index 00000000..0f844163 --- /dev/null +++ b/api-tests/ff/ipc/test_i081/test_supp_i081.c @@ -0,0 +1,44 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_app_rot_read_app_rot_stack(void); +int32_t server_test_app_rot_write_app_rot_stack(void); + +server_test_t test_i081_server_tests_list[] = { + NULL, + server_test_app_rot_read_app_rot_stack, + server_test_app_rot_write_app_rot_stack, + NULL, +}; + +int32_t server_test_app_rot_read_app_rot_stack(void) +{ + return VAL_STATUS_SUCCESS; +} + +int32_t server_test_app_rot_write_app_rot_stack(void) +{ + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i082/source.mk b/api-tests/ff/ipc/test_i082/source.mk new file mode 100644 index 00000000..af85416d --- /dev/null +++ b/api-tests/ff/ipc/test_i082/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i082.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i082.c test_supp_i082.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = diff --git a/api-tests/ff/ipc/test_i082/test_entry.c b/api-tests/ff/ipc/test_i082/test_entry.c new file mode 100644 index 00000000..8a38ca72 --- /dev/null +++ b/api-tests/ff/ipc/test_i082/test_entry.c @@ -0,0 +1,52 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i082.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 82) +#define TEST_DESC "Testing APP-RoT access to PSA-RoT heap\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_L2, 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/api-tests/ff/ipc/test_i082/test_i082.c b/api-tests/ff/ipc/test_i082/test_i082.c new file mode 100644 index 00000000..6ddd6a72 --- /dev/null +++ b/api-tests/ff/ipc/test_i082/test_i082.c @@ -0,0 +1,162 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" +#endif + +#include "test_i082.h" + +#define DATA_VALUE_ORG 0x11 +#define DATA_VALUE 0x12 +#define BUFFER_SIZE 0x4 + +client_test_t test_i082_client_tests_list[] = { + NULL, + client_test_app_rot_read_psa_rot_heap, + client_test_app_rot_write_psa_rot_heap, + NULL, +}; + +#if (SP_HEAP_MEM_SUPP == 1) +static int32_t get_secure_partition_address(psa_handle_t *handle, + addr_t *addr, + driver_test_fn_id_t test_fn_id) +{ + *handle = psa->connect(DRIVER_TEST_SID, 1); + if (*handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + /* Execute driver function related to TEST_ISOLATION_PSA_ROT_DATA_RD */ + psa_invec invec[1] = {{&test_fn_id, sizeof(test_fn_id)}}; + psa_outvec outvec[1] = {{addr, sizeof(addr_t)}}; + if (psa->call(*handle, invec, 1, outvec, 1) != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tmsg request failed\n", 0); + return VAL_STATUS_CALL_FAILED; + } + + return VAL_STATUS_SUCCESS; +} + +static int32_t get_driver_status(psa_handle_t *handle) +{ + if (psa->call(*handle, NULL, 0, NULL, 0) != PSA_SUCCESS) + { + return VAL_STATUS_CALL_FAILED; + } + return VAL_STATUS_SUCCESS; +} + +static void close_driver_fn(psa_handle_t *handle) +{ + psa->close(*handle); +} + +int32_t client_test_app_rot_read_psa_rot_heap(security_t caller) +{ + addr_t psa_rot_addr; + uint8_t data = DATA_VALUE; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check 1] Test APP-RoT reading PSA-RoT heap\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&handle, + &psa_rot_addr, + TEST_ISOLATION_PSA_ROT_HEAP_RD))) + return VAL_STATUS_ERROR; + + close_driver_fn(&handle); + + /* Setting boot.state before test check */ + if (val->set_boot_flag(BOOT_EXPECTED_REENTER_TEST)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Read PSA RoT heap address. + * This should generate internal fault or ignore the read. + */ + data = *(uint8_t *)psa_rot_addr; + + /* Did read ignore? */ + if (data == DATA_VALUE) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected read to fault but it didn't\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; +} + +int32_t client_test_app_rot_write_psa_rot_heap(security_t caller) +{ + addr_t psa_rot_addr; + uint8_t data = DATA_VALUE; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check 2] Test APP-RoT writing PSA-RoT heap\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&handle, + &psa_rot_addr, + TEST_ISOLATION_PSA_ROT_HEAP_WR))) + return VAL_STATUS_ERROR; + + /* Write PSA RoT heap address. + * This should generate internal fault or ignore the write. + */ + *(uint8_t *)psa_rot_addr = (uint8_t)data; + + /* Handshake with driver to decide write status */ + if (VAL_ERROR(get_driver_status(&handle))) + { + close_driver_fn(&handle); + return VAL_STATUS_DRIVER_FN_FAILED; + } + + close_driver_fn(&handle); + return VAL_STATUS_SUCCESS; +} +#else +int32_t client_test_app_rot_read_psa_rot_heap(security_t caller) +{ + val->print(PRINT_TEST, "[Check 1] Test APP-RoT reading PSA-RoT heap\n", 0); + val->print(PRINT_ERROR, "\tSkipping test as heap memory not supported\n", 0); + return RESULT_SKIP(VAL_STATUS_HEAP_NOT_AVAILABLE); +} + +int32_t client_test_app_rot_write_psa_rot_heap(security_t caller) +{ + val->print(PRINT_TEST, "[Check 2] Test APP-RoT writing PSA-RoT heap\n", 0); + val->print(PRINT_ERROR, "\tSkipping test as heap memory not supported\n", 0); + return RESULT_SKIP(VAL_STATUS_HEAP_NOT_AVAILABLE); +} +#endif diff --git a/api-tests/ff/ipc/test_i082/test_i082.h b/api-tests/ff/ipc/test_i082/test_i082.h new file mode 100644 index 00000000..accc73f5 --- /dev/null +++ b/api-tests/ff/ipc/test_i082/test_i082.h @@ -0,0 +1,38 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I082_CLIENT_TESTS_H_ +#define _TEST_I082_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i082) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i082_client_tests_list[]; + +int32_t client_test_app_rot_read_psa_rot_heap(security_t); +int32_t client_test_app_rot_write_psa_rot_heap(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i082/test_supp_i082.c b/api-tests/ff/ipc/test_i082/test_supp_i082.c new file mode 100644 index 00000000..20a5b7a3 --- /dev/null +++ b/api-tests/ff/ipc/test_i082/test_supp_i082.c @@ -0,0 +1,44 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_app_rot_read_psa_rot_heap(void); +int32_t server_test_app_rot_write_psa_rot_heap(void); + +server_test_t test_i082_server_tests_list[] = { + NULL, + server_test_app_rot_read_psa_rot_heap, + server_test_app_rot_write_psa_rot_heap, + NULL, +}; + +int32_t server_test_app_rot_read_psa_rot_heap(void) +{ + return VAL_STATUS_SUCCESS; +} + +int32_t server_test_app_rot_write_psa_rot_heap(void) +{ + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i083/source.mk b/api-tests/ff/ipc/test_i083/source.mk new file mode 100644 index 00000000..a0354eb0 --- /dev/null +++ b/api-tests/ff/ipc/test_i083/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i083.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i083.c test_supp_i083.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = diff --git a/api-tests/ff/ipc/test_i083/test_entry.c b/api-tests/ff/ipc/test_i083/test_entry.c new file mode 100644 index 00000000..403452bc --- /dev/null +++ b/api-tests/ff/ipc/test_i083/test_entry.c @@ -0,0 +1,52 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i083.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 83) +#define TEST_DESC "Testing APP-RoT access to PSA-RoT mmio\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_L2, 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/api-tests/ff/ipc/test_i083/test_i083.c b/api-tests/ff/ipc/test_i083/test_i083.c new file mode 100644 index 00000000..9899e3ee --- /dev/null +++ b/api-tests/ff/ipc/test_i083/test_i083.c @@ -0,0 +1,144 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" +#endif + +#include "test_i083.h" + +#define DATA_VALUE 0x1234 + +client_test_t test_i083_client_tests_list[] = { + NULL, + client_test_app_rot_read_psa_rot_mmio, + client_test_app_rot_write_psa_rot_mmio, + NULL, +}; + +static int32_t get_secure_partition_address(psa_handle_t *handle, + addr_t *addr, + driver_test_fn_id_t test_fn_id) +{ + *handle = psa->connect(DRIVER_TEST_SID, 1); + if (*handle < 0) + { + val->print(PRINT_ERROR, "\tConnection failed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + + /* Execute driver function related to TEST_ISOLATION_PSA_ROT_DATA_RD */ + psa_invec invec[1] = {{&test_fn_id, sizeof(test_fn_id)}}; + psa_outvec outvec[1] = {{addr, sizeof(addr_t)}}; + if (psa->call(*handle, invec, 1, outvec, 1) != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tmsg request failed\n", 0); + return VAL_STATUS_CALL_FAILED; + } + + return VAL_STATUS_SUCCESS; +} + +static int32_t get_driver_status(psa_handle_t *handle) +{ + if (psa->call(*handle, NULL, 0, NULL, 0) != PSA_SUCCESS) + { + return VAL_STATUS_CALL_FAILED; + } + return VAL_STATUS_SUCCESS; +} + +static void close_driver_fn(psa_handle_t *handle) +{ + psa->close(*handle); +} + +int32_t client_test_app_rot_read_psa_rot_mmio(security_t caller) +{ + addr_t psa_rot_addr; + uint32_t data = DATA_VALUE; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check 1] Test APP-RoT reading PSA-RoT mmio\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&handle, + &psa_rot_addr, + TEST_ISOLATION_PSA_ROT_MMIO_RD))) + return VAL_STATUS_ERROR; + + close_driver_fn(&handle); + + /* Setting boot.state before test check */ + if (val->set_boot_flag(BOOT_EXPECTED_REENTER_TEST)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Read PSA RoT mmio address. + * This should generate internal fault or ignore the read. + */ + data = *(uint32_t *)psa_rot_addr; + + /* Did read ignore? */ + if (data == DATA_VALUE) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected read to fault but it didn't\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; +} + +int32_t client_test_app_rot_write_psa_rot_mmio(security_t caller) +{ + addr_t psa_rot_addr; + uint32_t data = DATA_VALUE; + psa_handle_t handle = 0; + + val->print(PRINT_TEST, "[Check 2] Test APP-RoT writing PSA-RoT mmio\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&handle, + &psa_rot_addr, + TEST_ISOLATION_PSA_ROT_MMIO_WR))) + return VAL_STATUS_ERROR; + + /* Write PSA RoT mmio address. + * This should generate internal fault or ignore the write. + */ + *(uint32_t *)psa_rot_addr = (uint32_t)data; + + /* Handshake with driver to decide write status */ + if (VAL_ERROR(get_driver_status(&handle))) + { + close_driver_fn(&handle); + return VAL_STATUS_DRIVER_FN_FAILED; + } + + close_driver_fn(&handle); + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i083/test_i083.h b/api-tests/ff/ipc/test_i083/test_i083.h new file mode 100644 index 00000000..99496a87 --- /dev/null +++ b/api-tests/ff/ipc/test_i083/test_i083.h @@ -0,0 +1,38 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I083_CLIENT_TESTS_H_ +#define _TEST_I083_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i083) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i083_client_tests_list[]; + +int32_t client_test_app_rot_read_psa_rot_mmio(security_t); +int32_t client_test_app_rot_write_psa_rot_mmio(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i083/test_supp_i083.c b/api-tests/ff/ipc/test_i083/test_supp_i083.c new file mode 100644 index 00000000..e7bf8f51 --- /dev/null +++ b/api-tests/ff/ipc/test_i083/test_supp_i083.c @@ -0,0 +1,46 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_app_rot_read_psa_rot_mmio(void); +int32_t server_test_app_rot_write_psa_rot_mmio(void); + +#define DATA_VALUE 0x5467 + +server_test_t test_i083_server_tests_list[] = { + NULL, + server_test_app_rot_read_psa_rot_mmio, + server_test_app_rot_write_psa_rot_mmio, + NULL, +}; + +int32_t server_test_app_rot_read_psa_rot_mmio(void) +{ + return VAL_STATUS_SUCCESS; +} + +int32_t server_test_app_rot_write_psa_rot_mmio(void) +{ + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i084/source.mk b/api-tests/ff/ipc/test_i084/source.mk new file mode 100644 index 00000000..5d162ed9 --- /dev/null +++ b/api-tests/ff/ipc/test_i084/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i084.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i084.c test_supp_i084.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = diff --git a/api-tests/ff/ipc/test_i084/test_entry.c b/api-tests/ff/ipc/test_i084/test_entry.c new file mode 100644 index 00000000..e12e09a5 --- /dev/null +++ b/api-tests/ff/ipc/test_i084/test_entry.c @@ -0,0 +1,52 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i084.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 84) +#define TEST_DESC "Testing SP access to other SP data\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_L3, 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/api-tests/ff/ipc/test_i084/test_i084.c b/api-tests/ff/ipc/test_i084/test_i084.c new file mode 100644 index 00000000..933312aa --- /dev/null +++ b/api-tests/ff/ipc/test_i084/test_i084.c @@ -0,0 +1,125 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" +#endif + +#include "test_i084.h" + +client_test_t test_i084_client_tests_list[] = { + NULL, + client_test_sp_read_other_sp_variable, + client_test_sp_write_other_sp_variable, + NULL, +}; + +static int32_t get_secure_partition_address(addr_t *addr) +{ + psa_handle_t handle = 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; + } + + /* Get App-RoT address */ + psa_outvec outvec[1] = {{addr, sizeof(addr_t)}}; + if (psa->call(handle, NULL, 0, outvec, 1) != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tmsg request failed\n", 0); + return VAL_STATUS_CALL_FAILED; + } + + psa->close(handle); + return VAL_STATUS_SUCCESS; +} + +int32_t client_test_sp_read_other_sp_variable(security_t caller) +{ + addr_t app_rot_addr; + uint32_t data = 0x1234; + + val->print(PRINT_TEST, "[Check 1] Test SP reading other SP data\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&app_rot_addr))) + return VAL_STATUS_ERROR; + + /* Setting boot.state before test check */ + if (val->set_boot_flag(BOOT_EXPECTED_REENTER_TEST)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Read Application RoT global variable address. + * This should generate internal fault or ignore the read. + */ + data = *(uint32_t *)app_rot_addr; + + /* Did read ignore? */ + if (data == 0x1234) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected read to fault but it didn't\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; +} + +int32_t client_test_sp_write_other_sp_variable(security_t caller) +{ + addr_t app_rot_addr; + uint32_t data = 0x1234; + + val->print(PRINT_TEST, "[Check 2] Test SP writing other SP data\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&app_rot_addr))) + return VAL_STATUS_ERROR; + + /* 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); + return VAL_STATUS_ERROR; + } + + /* Write Application RoT global variable address. + * This should generate internal fault or ignore the write. + */ + *(uint32_t *)app_rot_addr = (uint32_t)data; + + /* Handshake with server to decide write status */ + if ((psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1)) > 0) + { + val->print(PRINT_ERROR, "\tExpected connection to fail but succeed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i084/test_i084.h b/api-tests/ff/ipc/test_i084/test_i084.h new file mode 100644 index 00000000..0fa86afc --- /dev/null +++ b/api-tests/ff/ipc/test_i084/test_i084.h @@ -0,0 +1,38 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I084_CLIENT_TESTS_H_ +#define _TEST_I084_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i084) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i084_client_tests_list[]; + +int32_t client_test_sp_read_other_sp_variable(security_t); +int32_t client_test_sp_write_other_sp_variable(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i084/test_supp_i084.c b/api-tests/ff/ipc/test_i084/test_supp_i084.c new file mode 100644 index 00000000..3c663d34 --- /dev/null +++ b/api-tests/ff/ipc/test_i084/test_supp_i084.c @@ -0,0 +1,113 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_sp_read_other_sp_variable(void); +int32_t server_test_sp_write_other_sp_variable(void); + +#define DATA_VALUE 0x5467 + +/* Application RoT data region */ +volatile uint32_t g_test_i084 = DATA_VALUE; + +server_test_t test_i084_server_tests_list[] = { + NULL, + server_test_sp_read_other_sp_variable, + server_test_sp_write_other_sp_variable, + NULL, +}; + +static int32_t send_secure_partition_address(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_ERROR_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); + return status; + } + + /* Send Application RoT data address - global variable */ + psa->write(msg.handle, 0, (void *)&g_test_i084, sizeof(g_test_i084)); + psa->reply(msg.handle, PSA_SUCCESS); + + status = val->process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val->err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + return status; + } + psa->reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_SUCCESS; +} + +int32_t server_test_sp_read_other_sp_variable(void) +{ + return send_secure_partition_address(); +} + +int32_t server_test_sp_write_other_sp_variable(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + status = send_secure_partition_address(); + if (VAL_ERROR(status)) + return status; + + /* Wait for write to get performed by client */ + status = val->process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val->err_check_set(TEST_CHECKPOINT_NUM(204), status)) + { + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); + return status; + } + + /* Connection request is just for handshake, reject connection anyways */ + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); + + /* Reached here means there could be write succeed or ignored */ + if (g_test_i084 == DATA_VALUE) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected write to fault but it didn't\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_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i085/source.mk b/api-tests/ff/ipc/test_i085/source.mk new file mode 100644 index 00000000..2456cf49 --- /dev/null +++ b/api-tests/ff/ipc/test_i085/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i085.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i085.c test_supp_i085.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = diff --git a/api-tests/ff/ipc/test_i085/test_entry.c b/api-tests/ff/ipc/test_i085/test_entry.c new file mode 100644 index 00000000..fadc37f4 --- /dev/null +++ b/api-tests/ff/ipc/test_i085/test_entry.c @@ -0,0 +1,52 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i085.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 85) +#define TEST_DESC "Testing SP access other SP stack\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_L3, 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/api-tests/ff/ipc/test_i085/test_i085.c b/api-tests/ff/ipc/test_i085/test_i085.c new file mode 100644 index 00000000..da19a245 --- /dev/null +++ b/api-tests/ff/ipc/test_i085/test_i085.c @@ -0,0 +1,125 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" +#endif + +#include "test_i085.h" + +client_test_t test_i085_client_tests_list[] = { + NULL, + client_test_sp_read_other_sp_stack, + client_test_sp_write_other_sp_stack, + NULL, +}; + +static int32_t get_secure_partition_address(addr_t *addr) +{ + psa_handle_t handle = 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; + } + + /* Get App-RoT address */ + psa_outvec outvec[1] = {{addr, sizeof(addr_t)}}; + if (psa->call(handle, NULL, 0, outvec, 1) != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tmsg request failed\n", 0); + return VAL_STATUS_CALL_FAILED; + } + + psa->close(handle); + return VAL_STATUS_SUCCESS; +} + +int32_t client_test_sp_read_other_sp_stack(security_t caller) +{ + addr_t app_rot_addr; + uint32_t data = 0x1234; + + val->print(PRINT_TEST, "[Check 1] Test SP reading other SP stack\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&app_rot_addr))) + return VAL_STATUS_ERROR; + + /* Setting boot.state before test check */ + if (val->set_boot_flag(BOOT_EXPECTED_REENTER_TEST)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Read Application RoT stack address. + * This should generate internal fault or ignore the read. + */ + data = *(uint32_t *)app_rot_addr; + + /* Did read ignore? */ + if (data == 0x1234) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected read to fault but it didn't\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; +} + +int32_t client_test_sp_write_other_sp_stack(security_t caller) +{ + addr_t app_rot_addr; + uint32_t data = 0x1234; + + val->print(PRINT_TEST, "[Check 2] Test SP writing other SP stack\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&app_rot_addr))) + return VAL_STATUS_ERROR; + + /* 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); + return VAL_STATUS_ERROR; + } + + /* Write Application RoT stack address. + * This should generate internal fault or ignore the write. + */ + *(uint32_t *)app_rot_addr = (uint32_t)data; + + /* Handshake with server to decide write status */ + if ((psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1)) > 0) + { + val->print(PRINT_ERROR, "\tExpected connection to fail but succeed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i085/test_i085.h b/api-tests/ff/ipc/test_i085/test_i085.h new file mode 100644 index 00000000..80d5bd6d --- /dev/null +++ b/api-tests/ff/ipc/test_i085/test_i085.h @@ -0,0 +1,38 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I085_CLIENT_TESTS_H_ +#define _TEST_I085_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i085) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i085_client_tests_list[]; + +int32_t client_test_sp_read_other_sp_stack(security_t); +int32_t client_test_sp_write_other_sp_stack(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i085/test_supp_i085.c b/api-tests/ff/ipc/test_i085/test_supp_i085.c new file mode 100644 index 00000000..9922716c --- /dev/null +++ b/api-tests/ff/ipc/test_i085/test_supp_i085.c @@ -0,0 +1,120 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_sp_read_other_sp_stack(void); +int32_t server_test_sp_write_other_sp_stack(void); + +#define DATA_VALUE 0x5467 + +server_test_t test_i085_server_tests_list[] = { + NULL, + server_test_sp_read_other_sp_stack, + server_test_sp_write_other_sp_stack, + NULL, +}; + +static int32_t send_secure_partition_address(addr_t *stack) +{ + 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_ERROR_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); + return status; + } + + /* Send Application RoT stack address */ + psa->write(msg.handle, 0, (void *)stack, sizeof(uint32_t)); + psa->reply(msg.handle, PSA_SUCCESS); + + status = val->process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val->err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + return status; + } + psa->reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_SUCCESS; +} + +int32_t server_test_sp_read_other_sp_stack(void) +{ + /* Application RoT stack - local variable */ + uint32_t l_test_i085 = DATA_VALUE; + int32_t status = VAL_STATUS_SUCCESS; + + status = send_secure_partition_address(&l_test_i085); + + /* Dummy print to avoid compiler optimisation on local variable */ + val->print(PRINT_INFO, "\tData value 0x%x\n", l_test_i085); + return status; +} + +int32_t server_test_sp_write_other_sp_stack(void) +{ + /* Application RoT stack - local variable */ + uint32_t l_test_i085 = DATA_VALUE; + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + status = send_secure_partition_address(&l_test_i085); + if (VAL_ERROR(status)) + return status; + + /* Wait for write to get performed by client */ + status = val->process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val->err_check_set(TEST_CHECKPOINT_NUM(204), status)) + { + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); + return status; + } + + /* Connection request is just for handshake, reject connection anyways */ + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); + + /* Reached here means there could be write succeed or ignored */ + if (l_test_i085 == DATA_VALUE) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected write to fault but it didn't\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_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i086/source.mk b/api-tests/ff/ipc/test_i086/source.mk new file mode 100644 index 00000000..c4127af3 --- /dev/null +++ b/api-tests/ff/ipc/test_i086/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i086.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i086.c test_supp_i086.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = diff --git a/api-tests/ff/ipc/test_i086/test_entry.c b/api-tests/ff/ipc/test_i086/test_entry.c new file mode 100644 index 00000000..e2c31eee --- /dev/null +++ b/api-tests/ff/ipc/test_i086/test_entry.c @@ -0,0 +1,52 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i086.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 86) +#define TEST_DESC "Testing SP access other SP heap\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_L3, 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/api-tests/ff/ipc/test_i086/test_i086.c b/api-tests/ff/ipc/test_i086/test_i086.c new file mode 100644 index 00000000..7f56cbe8 --- /dev/null +++ b/api-tests/ff/ipc/test_i086/test_i086.c @@ -0,0 +1,145 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" +#endif + +#include "test_i086.h" + +#define DATA_VALUE_ORG 0x11 +#define DATA_VALUE 0x12 +#define BUFFER_SIZE 0x4 + +client_test_t test_i086_client_tests_list[] = { + NULL, + client_test_sp_read_other_sp_heap, + client_test_sp_write_other_sp_heap, + NULL, +}; + +#if (SP_HEAP_MEM_SUPP == 1) +static int32_t get_secure_partition_address(addr_t *addr) +{ + psa_handle_t handle = 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; + } + + /* Get App-RoT address */ + psa_outvec outvec[1] = {{addr, BUFFER_SIZE}}; + if (psa->call(handle, NULL, 0, outvec, 1) != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tmsg request failed\n", 0); + return VAL_STATUS_CALL_FAILED; + } + + psa->close(handle); + return VAL_STATUS_SUCCESS; +} + +int32_t client_test_sp_read_other_sp_heap(security_t caller) +{ + addr_t app_rot_addr; + uint8_t data = DATA_VALUE; + + val->print(PRINT_TEST, "[Check 1] Test SP reading other SP heap\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&app_rot_addr))) + return VAL_STATUS_ERROR; + + /* Setting boot.state before test check */ + if (val->set_boot_flag(BOOT_EXPECTED_REENTER_TEST)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Read Application RoT heap address. + * This should generate internal fault or ignore the read. + */ + data = *(uint8_t *)app_rot_addr; + + /* Did read ignore? */ + if (data == DATA_VALUE) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected read to fault but it didn't\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; +} + +int32_t client_test_sp_write_other_sp_heap(security_t caller) +{ + addr_t app_rot_addr; + uint8_t data = DATA_VALUE; + + val->print(PRINT_TEST, "[Check 2] Test SP writing other SP heap\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&app_rot_addr))) + return VAL_STATUS_ERROR; + + /* 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); + return VAL_STATUS_ERROR; + } + + /* Write Application RoT heap address. + * This should generate internal fault or ignore the write. + */ + *(uint8_t *)app_rot_addr = (uint8_t)data; + + /* Handshake with server to decide write status */ + if ((psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1)) > 0) + { + val->print(PRINT_ERROR, "\tExpected connection to fail but succeed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + return VAL_STATUS_SUCCESS; +} +#else +int32_t client_test_sp_read_other_sp_heap(security_t caller) +{ + val->print(PRINT_TEST, "[Check 1] Test SP reading other SP heap\n", 0); + val->print(PRINT_ERROR, "\tSkipping test as heap memory not supported\n", 0); + return RESULT_SKIP(VAL_STATUS_HEAP_NOT_AVAILABLE); +} + +int32_t client_test_sp_write_other_sp_heap(security_t caller) +{ + val->print(PRINT_TEST, "[Check 2] Test SP writing other SP heap\n", 0); + val->print(PRINT_ERROR, "\tSkipping test as heap memory not supported\n", 0); + return RESULT_SKIP(VAL_STATUS_HEAP_NOT_AVAILABLE); +} +#endif diff --git a/api-tests/ff/ipc/test_i086/test_i086.h b/api-tests/ff/ipc/test_i086/test_i086.h new file mode 100644 index 00000000..eb64546d --- /dev/null +++ b/api-tests/ff/ipc/test_i086/test_i086.h @@ -0,0 +1,38 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I086_CLIENT_TESTS_H_ +#define _TEST_I086_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i086) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i086_client_tests_list[]; + +int32_t client_test_sp_read_other_sp_heap(security_t); +int32_t client_test_sp_write_other_sp_heap(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i086/test_supp_i086.c b/api-tests/ff/ipc/test_i086/test_supp_i086.c new file mode 100644 index 00000000..52fef112 --- /dev/null +++ b/api-tests/ff/ipc/test_i086/test_supp_i086.c @@ -0,0 +1,146 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_sp_read_other_sp_heap(void); +int32_t server_test_sp_write_other_sp_heap(void); + +#if (SP_HEAP_MEM_SUPP == 1) +void *malloc(size_t size); +void free(void *ptr); +#endif + +#define DATA_VALUE_ORG 0x11 +#define BUFFER_SIZE 0x4 + +server_test_t test_i086_server_tests_list[] = { + NULL, + server_test_sp_read_other_sp_heap, + server_test_sp_write_other_sp_heap, + NULL, +}; + +#if (SP_HEAP_MEM_SUPP == 1) +static int32_t send_secure_partition_address(uint8_t *heap) +{ + 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_ERROR_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); + return status; + } + + /* Send Application RoT heap address */ + psa->write(msg.handle, 0, (void *)heap, sizeof(BUFFER_SIZE)); + psa->reply(msg.handle, PSA_SUCCESS); + + status = val->process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val->err_check_set(TEST_CHECKPOINT_NUM(203), status)) + { + return status; + } + psa->reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_SUCCESS; +} + +int32_t server_test_sp_read_other_sp_heap(void) +{ + /* Application RoT heap buffer */ + uint8_t *buffer; + int32_t status = VAL_STATUS_SUCCESS; + + buffer = (uint8_t *)malloc(sizeof(uint8_t) * BUFFER_SIZE); + memset((uint8_t *)buffer, DATA_VALUE_ORG, BUFFER_SIZE); + + status = send_secure_partition_address(buffer); + free(buffer); + + return status; +} + +int32_t server_test_sp_write_other_sp_heap(void) +{ + /* Application RoT heap buffer */ + uint8_t *buffer; + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + buffer = (uint8_t *)malloc(sizeof(uint8_t) * BUFFER_SIZE); + memset((uint8_t *)buffer, DATA_VALUE_ORG, BUFFER_SIZE); + + status = send_secure_partition_address(buffer); + if (VAL_ERROR(status)) + return status; + + /* Wait for write to get performed by client */ + status = val->process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val->err_check_set(TEST_CHECKPOINT_NUM(204), status)) + { + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); + return status; + } + + /* Connection request is just for handshake, reject connection anyways */ + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); + + /* Reached here means there could be write succeed or ignored */ + if (buffer[0] == DATA_VALUE_ORG) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected write to fault but it didn't\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; + } + + free(buffer); + return VAL_STATUS_SUCCESS; +} +#else + +int32_t server_test_sp_read_other_sp_heap(void) +{ + return RESULT_SKIP(VAL_STATUS_HEAP_NOT_AVAILABLE); +} + +int32_t server_test_sp_write_other_sp_heap(void) +{ + return RESULT_SKIP(VAL_STATUS_HEAP_NOT_AVAILABLE); +} +#endif diff --git a/api-tests/ff/ipc/test_i087/source.mk b/api-tests/ff/ipc/test_i087/source.mk new file mode 100644 index 00000000..492f7953 --- /dev/null +++ b/api-tests/ff/ipc/test_i087/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_i087.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_i087.c test_supp_i087.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = diff --git a/api-tests/ff/ipc/test_i087/test_entry.c b/api-tests/ff/ipc/test_i087/test_entry.c new file mode 100644 index 00000000..a5de90ad --- /dev/null +++ b/api-tests/ff/ipc/test_i087/test_entry.c @@ -0,0 +1,52 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_i087.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 87) +#define TEST_DESC "Testing SP access other SP mmio\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_L3, 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/api-tests/ff/ipc/test_i087/test_i087.c b/api-tests/ff/ipc/test_i087/test_i087.c new file mode 100644 index 00000000..eb6cbf22 --- /dev/null +++ b/api-tests/ff/ipc/test_i087/test_i087.c @@ -0,0 +1,127 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" +#endif + +#include "test_i087.h" + +#define DATA_VALUE 0x1234 + +client_test_t test_i087_client_tests_list[] = { + NULL, + client_test_sp_read_other_sp_mmio, + client_test_sp_write_other_sp_mmio, + NULL, +}; + +static int32_t get_secure_partition_address(addr_t *addr) +{ + psa_handle_t handle = 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; + } + + /* Get App-RoT address */ + psa_outvec outvec[1] = {{addr, sizeof(addr_t)}}; + if (psa->call(handle, NULL, 0, outvec, 1) != PSA_SUCCESS) + { + val->print(PRINT_ERROR, "\tmsg request failed\n", 0); + return VAL_STATUS_CALL_FAILED; + } + + psa->close(handle); + return VAL_STATUS_SUCCESS; +} + +int32_t client_test_sp_read_other_sp_mmio(security_t caller) +{ + addr_t app_rot_addr; + uint32_t data = DATA_VALUE; + + val->print(PRINT_TEST, "[Check 1] Test SP reading other SP mmio\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&app_rot_addr))) + return VAL_STATUS_ERROR; + + /* Setting boot.state before test check */ + if (val->set_boot_flag(BOOT_EXPECTED_REENTER_TEST)) + { + val->print(PRINT_ERROR, "\tFailed to set boot flag before check\n", 0); + return VAL_STATUS_ERROR; + } + + /* Read Application RoT mmio address. + * This should generate internal fault or ignore the read. + */ + data = *(uint32_t *)app_rot_addr; + + /* Did read ignore? */ + if (data == DATA_VALUE) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected read to fault but it didn't\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; +} + +int32_t client_test_sp_write_other_sp_mmio(security_t caller) +{ + addr_t app_rot_addr; + uint32_t data = DATA_VALUE; + + val->print(PRINT_TEST, "[Check 2] Test SP writing other SP mmio\n", 0); + + if (VAL_ERROR(get_secure_partition_address(&app_rot_addr))) + return VAL_STATUS_ERROR; + + /* 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); + return VAL_STATUS_ERROR; + } + + /* Write Application RoT mmio address. + * This should generate internal fault or ignore the write. + */ + *(uint32_t *)app_rot_addr = (uint32_t)data; + + /* Handshake with server to decide write status */ + if ((psa->connect(SERVER_UNSPECIFED_MINOR_V_SID, 1)) > 0) + { + val->print(PRINT_ERROR, "\tExpected connection to fail but succeed\n", 0); + return VAL_STATUS_INVALID_HANDLE; + } + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_i087/test_i087.h b/api-tests/ff/ipc/test_i087/test_i087.h new file mode 100644 index 00000000..450427d2 --- /dev/null +++ b/api-tests/ff/ipc/test_i087/test_i087.h @@ -0,0 +1,38 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_I087_CLIENT_TESTS_H_ +#define _TEST_I087_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,i087) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i087_client_tests_list[]; + +int32_t client_test_sp_read_other_sp_mmio(security_t); +int32_t client_test_sp_write_other_sp_mmio(security_t); +#endif diff --git a/api-tests/ff/ipc/test_i087/test_supp_i087.c b/api-tests/ff/ipc/test_i087/test_supp_i087.c new file mode 100644 index 00000000..b59fb978 --- /dev/null +++ b/api-tests/ff/ipc/test_i087/test_supp_i087.c @@ -0,0 +1,143 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_sp_read_other_sp_mmio(void); +int32_t server_test_sp_write_other_sp_mmio(void); + +#define DATA_VALUE 0x5467 + +server_test_t test_i087_server_tests_list[] = { + NULL, + server_test_sp_read_other_sp_mmio, + server_test_sp_write_other_sp_mmio, + NULL, +}; + +static int32_t get_mmio_addr(addr_t *addr) +{ + memory_desc_t *memory_desc; + int32_t status = VAL_STATUS_SUCCESS; + + /* Get APP-ROT MMIO address */ + status = val->target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_MEMORY, + MEMORY_SERVER_PARTITION_MMIO, 0), + (uint8_t **)&memory_desc, + (uint32_t *)sizeof(memory_desc_t)); + if (val->err_check_set(TEST_CHECKPOINT_NUM(201), status)) + { + return status; + } + + *addr = memory_desc->start; + return VAL_STATUS_SUCCESS; +} + +static int32_t send_secure_partition_address(addr_t *stack) +{ + 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(202), status)) + { + psa->reply(msg.handle, PSA_ERROR_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(203), status)) + { + psa->reply(msg.handle, -2); + return status; + } + + /* Send Application RoT stack address */ + psa->write(msg.handle, 0, (void *)stack, sizeof(uint32_t)); + psa->reply(msg.handle, PSA_SUCCESS); + + status = val->process_disconnect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val->err_check_set(TEST_CHECKPOINT_NUM(204), status)) + { + return status; + } + psa->reply(msg.handle, PSA_SUCCESS); + return VAL_STATUS_SUCCESS; +} + +int32_t server_test_sp_read_other_sp_mmio(void) +{ + int32_t status = VAL_STATUS_SUCCESS; + addr_t app_rot_addr; + + status = get_mmio_addr(&app_rot_addr); + if (VAL_ERROR(status)) + return status; + + return send_secure_partition_address(&app_rot_addr); +} + +int32_t server_test_sp_write_other_sp_mmio(void) +{ + addr_t app_rot_addr; + int32_t status = VAL_STATUS_SUCCESS; + psa_msg_t msg = {0}; + + status = get_mmio_addr(&app_rot_addr); + if (VAL_ERROR(status)) + return status; + + /* Initialise mmio address */ + *(uint32_t *)app_rot_addr = (uint32_t)DATA_VALUE; + status = send_secure_partition_address(&app_rot_addr); + if (VAL_ERROR(status)) + return status; + + /* Wait for write to get performed by client */ + status = val->process_connect_request(SERVER_UNSPECIFED_MINOR_V_SIG, &msg); + if (val->err_check_set(TEST_CHECKPOINT_NUM(204), status)) + { + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); + return status; + } + + /* Connection request is just for handshake, reject connection anyways */ + psa->reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); + + /* Reached here means there could be write succeed or ignored */ + if (*(uint32_t *)app_rot_addr == (uint32_t)DATA_VALUE) + return VAL_STATUS_SUCCESS; + + val->print(PRINT_ERROR, "\tExpected write to fault but it didn't\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_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_l088/source.mk b/api-tests/ff/ipc/test_l088/source.mk new file mode 100644 index 00000000..c2004628 --- /dev/null +++ b/api-tests/ff/ipc/test_l088/source.mk @@ -0,0 +1,25 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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_l088.c +CC_OPTIONS = +AS_SOURCE = +AS_OPTIONS = + +CC_SOURCE_SPE = test_l088.c test_supp_l088.c +CC_OPTIONS_SPE = +AS_SOURCE_SPE = +AS_OPTIONS_SPE = diff --git a/api-tests/ff/ipc/test_l088/test_entry.c b/api-tests/ff/ipc/test_l088/test_entry.c new file mode 100644 index 00000000..4732122f --- /dev/null +++ b/api-tests/ff/ipc/test_l088/test_entry.c @@ -0,0 +1,52 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_l088.h" + +#define TEST_NUM VAL_CREATE_TEST_ID(VAL_FF_BASE, 88) +#define TEST_DESC "Testing psa_rot_lifecycle_state API\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/api-tests/ff/ipc/test_l088/test_l088.c b/api-tests/ff/ipc/test_l088/test_l088.c new file mode 100644 index 00000000..612c9278 --- /dev/null +++ b/api-tests/ff/ipc/test_l088/test_l088.c @@ -0,0 +1,37 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" +#endif + +#include "test_l088.h" + +client_test_t test_l088_client_tests_list[] = { + NULL, + client_test_psa_rot_lifecycle_state, + NULL, +}; + +int32_t client_test_psa_rot_lifecycle_state(security_t caller) +{ + return VAL_STATUS_SUCCESS; +} diff --git a/api-tests/ff/ipc/test_l088/test_l088.h b/api-tests/ff/ipc/test_l088/test_l088.h new file mode 100644 index 00000000..e27f8acf --- /dev/null +++ b/api-tests/ff/ipc/test_l088/test_l088.h @@ -0,0 +1,37 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_L088_CLIENT_TESTS_H_ +#define _TEST_L088_CLIENT_TESTS_H_ + +#include "val_client_defs.h" + +#ifdef NONSECURE_TEST_BUILD +#define test_entry CONCAT(test_entry_,l088) +#define val CONCAT(val,test_entry) +#define psa CONCAT(psa,test_entry) +#else +#define val CONCAT(val,_client_sp) +#define psa CONCAT(psa,_client_sp) +#endif + +extern val_api_t *val; +extern psa_api_t *psa; + +extern client_test_t test_i088_client_tests_list[]; + +int32_t client_test_psa_rot_lifecycle_state(security_t); +#endif diff --git a/api-tests/ff/ipc/test_l088/test_supp_l088.c b/api-tests/ff/ipc/test_l088/test_supp_l088.c new file mode 100644 index 00000000..1ca9fe6d --- /dev/null +++ b/api-tests/ff/ipc/test_l088/test_supp_l088.c @@ -0,0 +1,59 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_defs.h" +#include "val_service_defs.h" + +#define val CONCAT(val,_server_sp) +#define psa CONCAT(psa,_server_sp) +extern val_api_t *val; +extern psa_api_t *psa; + +int32_t server_test_psa_rot_lifecycle_state(void); + +server_test_t test_l088_server_tests_list[] = { + NULL, + server_test_psa_rot_lifecycle_state, + NULL, +}; + +int32_t server_test_psa_rot_lifecycle_state(void) +{ + uint32_t state; + int32_t status = VAL_STATUS_UNSUPPORTED; + + val->print(PRINT_TEST, "[Check 1] Test calling psa_rot_lifecycle_state API\n", 0); + + state = psa->rot_lifecycle_state(); + switch (state & PSA_LIFECYCLE_PSA_STATE_MASK) + { + case PSA_LIFECYCLE_UNKNOWN: + case PSA_LIFECYCLE_ASSEMBLY_AND_TEST: + case PSA_LIFECYCLE_PSA_ROT_PROVISIONING: + case PSA_LIFECYCLE_SECURED: + case PSA_LIFECYCLE_NON_PSA_ROT_DEBUG: + case PSA_LIFECYCLE_RECOVERABLE_PSA_ROT_DEBUG: + case PSA_LIFECYCLE_DECOMMISSIONED: + status = VAL_STATUS_SUCCESS; + break; + default: + val->print(PRINT_ERROR, "\tpsa_rot_lifecycle_state API returned" + "unsupported value\n", 0); + } + + return status; +} diff --git a/api-tests/ff/ipc/testsuite.db b/api-tests/ff/ipc/testsuite.db index 4bff10ed..f8a30133 100644 --- a/api-tests/ff/ipc/testsuite.db +++ b/api-tests/ff/ipc/testsuite.db @@ -23,68 +23,90 @@ 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_i004, panic_test +test_i005, panic_test +test_i006, panic_test +test_i007, panic_test +test_i008, panic_test +test_i009, panic_test +test_i010, panic_test +test_i011, panic_test +test_i012, panic_test +test_i013, panic_test +test_i014, panic_test +test_i015, panic_test +test_i016, panic_test +test_i017, panic_test +test_i018, panic_test +test_i019, panic_test +test_i020, panic_test 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 -test_i047 -test_i048 -test_i049 -test_i050 -test_i051 -test_i052 -test_i053 -test_i054 -test_i055 -test_i056 -test_i057 +test_i022, panic_test +test_i023, panic_test +test_i024, panic_test +test_i025, panic_test +test_i026, panic_test +test_i027, panic_test +test_i028, panic_test +test_i029, panic_test +test_i030, panic_test +test_i031, panic_test +test_i032, panic_test +test_i033, panic_test +test_i034, panic_test +test_i035, panic_test +test_i036, panic_test +test_i037, panic_test +test_i038, panic_test +test_i039, panic_test +test_i040, panic_test +test_i041, panic_test +test_i042, panic_test +test_i043, panic_test +test_i044, panic_test +test_i045, panic_test +test_i046, panic_test +test_i047, panic_test +test_i048, panic_test +test_i049, panic_test +test_i050, panic_test +test_i051, panic_test +test_i052, panic_test +test_i053, panic_test +test_i054, panic_test +test_i055, panic_test +test_i056, panic_test +test_i057, panic_test test_i058 -test_i059 -test_i060 -test_i061 -test_i062 +test_i059, panic_test +test_i060, panic_test +test_i061, panic_test +test_i062, panic_test test_i063 -test_i064 -test_i065 -test_i066 +test_i064, panic_test +test_i065, panic_test +test_i066, panic_test +test_i067 +test_i068, panic_test +test_i069, panic_test +test_i070, panic_test +test_i071 +test_i072, panic_test +test_i073, panic_test +test_i074, panic_test +test_i075, panic_test +test_i076, panic_test +test_i077, panic_test +test_i078, panic_test +test_i079, panic_test +test_i080, panic_test +test_i081, panic_test +test_i082, panic_test +test_i083, panic_test +test_i084, panic_test +test_i085, panic_test +test_i086, panic_test +test_i087, panic_test +test_l088 (END) diff --git a/api-tests/ff/partition/common/driver_partition.c b/api-tests/ff/partition/common/driver_partition.c index 6523fd58..53a6004b 100644 --- a/api-tests/ff/partition/common/driver_partition.c +++ b/api-tests/ff/partition/common/driver_partition.c @@ -17,22 +17,36 @@ #include "val/spe/val_driver_service_apis.h" +#define DATA_VALUE 0x1111 +#define BUFFER_SIZE 4 + +uint32_t g_psa_rot_data = DATA_VALUE; + int32_t driver_test_psa_eoi_with_non_intr_signal(void); int32_t driver_test_psa_eoi_with_unasserted_signal(void); int32_t driver_test_psa_eoi_with_multiple_signals(void); +int32_t driver_test_irq_routing(void); +void driver_test_isolation_psa_rot_data_rd(psa_msg_t *msg); +void driver_test_isolation_psa_rot_data_wr(psa_msg_t *msg); +void driver_test_isolation_psa_rot_stack_rd(psa_msg_t *msg); +void driver_test_isolation_psa_rot_stack_wr(psa_msg_t *msg); +void driver_test_isolation_psa_rot_heap_rd(psa_msg_t *msg); +void driver_test_isolation_psa_rot_heap_wr(psa_msg_t *msg); +void driver_test_isolation_psa_rot_mmio_rd(psa_msg_t *msg); +void driver_test_isolation_psa_rot_mmio_wr(psa_msg_t *msg); void driver_main(void) { - psa_signal_t signals = 0; - psa_msg_t msg = {0}; - uint32_t data = 0; - uart_fn_type_t uart_fn; - nvmem_param_t nvmem_param; - test_intr_fn_id_t test_intr_fn_id; - char string[256] = {0}; - uint8_t buffer[256] = {0}; - wd_param_t wd_param; - addr_t uart_base; + psa_signal_t signals = 0; + psa_msg_t msg = {0}; + int32_t data = 0; + uart_fn_type_t uart_fn; + nvmem_param_t nvmem_param; + driver_test_fn_id_t driver_test_fn_id; + char string[256] = {0}; + uint8_t buffer[256] = {0}; + wd_param_t wd_param; + addr_t uart_base; /* Initialised driver mmio space */ if (val_init_driver_memory()) @@ -206,14 +220,14 @@ void driver_main(void) break; } } - /* SID reserved for interrupt testing */ - else if (signals & TEST_INTR_SIG) + /* SID reserved for Driver test functions */ + else if (signals & DRIVER_TEST_SIG) { - psa_get(TEST_INTR_SIG, &msg); + psa_get(DRIVER_TEST_SIG, &msg); switch (msg.type) { case PSA_IPC_CALL: - if (msg.in_size[0] > sizeof(test_intr_fn_id_t)) + if (msg.in_size[0] > sizeof(driver_test_fn_id_t)) { /* buffer overflow */ psa_reply(msg.handle, VAL_STATUS_ERROR); @@ -222,10 +236,10 @@ void driver_main(void) } else { - psa_read(msg.handle, 0, &test_intr_fn_id, msg.in_size[0]); + psa_read(msg.handle, 0, &driver_test_fn_id, msg.in_size[0]); } - switch (test_intr_fn_id) + switch (driver_test_fn_id) { case TEST_PSA_EOI_WITH_NON_INTR_SIGNAL: driver_test_psa_eoi_with_non_intr_signal(); @@ -240,6 +254,34 @@ void driver_main(void) psa_reply(msg.handle, VAL_STATUS_ERROR); break; case TEST_INTR_SERVICE: + if (driver_test_irq_routing() != VAL_STATUS_SUCCESS) + psa_reply(msg.handle, VAL_STATUS_ERROR); + else + psa_reply(msg.handle, PSA_SUCCESS); + break; + case TEST_ISOLATION_PSA_ROT_DATA_RD: + driver_test_isolation_psa_rot_data_rd(&msg); + break; + case TEST_ISOLATION_PSA_ROT_DATA_WR: + driver_test_isolation_psa_rot_data_wr(&msg); + break; + case TEST_ISOLATION_PSA_ROT_STACK_RD: + driver_test_isolation_psa_rot_stack_rd(&msg); + break; + case TEST_ISOLATION_PSA_ROT_STACK_WR: + driver_test_isolation_psa_rot_stack_wr(&msg); + break; + case TEST_ISOLATION_PSA_ROT_HEAP_RD: + driver_test_isolation_psa_rot_heap_rd(&msg); + break; + case TEST_ISOLATION_PSA_ROT_HEAP_WR: + driver_test_isolation_psa_rot_heap_wr(&msg); + break; + case TEST_ISOLATION_PSA_ROT_MMIO_RD: + driver_test_isolation_psa_rot_mmio_rd(&msg); + break; + case TEST_ISOLATION_PSA_ROT_MMIO_WR: + driver_test_isolation_psa_rot_mmio_wr(&msg); break; } break; @@ -267,7 +309,7 @@ int32_t driver_test_psa_eoi_with_non_intr_signal(void) return VAL_STATUS_ERROR; } - /* psa_eoi should not return as signal is non-interrupt signal*/ + /* psa_eoi should panic as signal is non-interrupt signal*/ psa_eoi(PSA_DOORBELL); /* Control shouldn't have reached here */ @@ -292,7 +334,7 @@ int32_t driver_test_psa_eoi_with_unasserted_signal(void) return VAL_STATUS_ERROR; } - /* psa_eoi should not return as DRIVER_UART_INTR_SIG is unasserted */ + /* psa_eoi should panic as DRIVER_UART_INTR_SIG is unasserted */ psa_eoi(DRIVER_UART_INTR_SIG); /* Control shouldn't have reached here */ @@ -310,6 +352,27 @@ int32_t driver_test_psa_eoi_with_unasserted_signal(void) int32_t driver_test_psa_eoi_with_multiple_signals(void) { + /* + * To test psa_eoi for multiple signals, one of signal should asserted first. + * Otherwise, check can false pass with psa_eoi_with_unasserted_signal. + * Assert interrupt signal assigned to driver partition + */ + val_generate_interrupt(); + + /* Wait for DRIVER_UART_INTR_SIG signal */ + if (psa_wait(DRIVER_UART_INTR_SIG, PSA_BLOCK) & DRIVER_UART_INTR_SIG) + { + /* Received DRIVER_UART_INTR_SIG signal, now process it */ + val_disable_interrupt(); + } + else + { + /* didn't receive DRIVER_UART_INTR_SIG signal, however process it */ + val_disable_interrupt(); + val_print_sf("\tFailed to receive irq signal\n", 0); + return VAL_STATUS_SPM_FAILED; + } + /* Setting boot.state before test check */ if (val_driver_private_set_boot_flag_fn(BOOT_EXPECTED_NS)) { @@ -317,8 +380,8 @@ int32_t driver_test_psa_eoi_with_multiple_signals(void) return VAL_STATUS_ERROR; } - /* psa_eoi should not return as irq_signal is provided with multiple signals */ - psa_eoi(DRIVER_UART_INTR_SIG|TEST_INTR_SIG); + /* psa_eoi should panic as irq_signal is provided with multiple signals */ + psa_eoi(DRIVER_UART_INTR_SIG|DRIVER_TEST_SIG); /* Control shouldn't have reached here */ val_print_sf("\tCheck for psa_eoi(multiple_signals) failed\n", 0); @@ -332,3 +395,307 @@ int32_t driver_test_psa_eoi_with_multiple_signals(void) return VAL_STATUS_SPM_FAILED; } + +int32_t driver_test_irq_routing(void) +{ + psa_signal_t signals = 0; + + /* Assert interrupt signal assigned to driver partition */ + val_generate_interrupt(); + + /* Wait for DRIVER_UART_INTR_SIG signal */ + signals = psa_wait(PSA_WAIT_ANY, PSA_BLOCK); + + if (signals & DRIVER_UART_INTR_SIG) + { + /* Received DRIVER_UART_INTR_SIG signal, now process it */ + val_disable_interrupt(); + + /* A signal remains active until it is processed by psa_eoi */ + if ((psa_wait(DRIVER_UART_INTR_SIG, PSA_BLOCK) & DRIVER_UART_INTR_SIG) == 0) + { + val_print_sf("\tIrq signal got de-activate before psa_eoi()\n", 0); + return VAL_STATUS_SPM_FAILED; + } + + /* Perform end of interrupt */ + psa_eoi(DRIVER_UART_INTR_SIG); + + /* Irq signal should be de-activated now */ + if (psa_wait(DRIVER_UART_INTR_SIG, PSA_POLL) & DRIVER_UART_INTR_SIG) + { + val_print_sf("\tIrq signal didn't get de-activate after psa_eoi()\n", 0); + return VAL_STATUS_SPM_FAILED; + } + + return VAL_STATUS_SUCCESS; + } + else + { + /* didn't receive DRIVER_UART_INTR_SIG signal, however process it */ + val_disable_interrupt(); + val_print_sf("\tFailed to receive irq signal, signals=0x%x\n", signals); + return VAL_STATUS_SPM_FAILED; + } +} + +static int32_t process_call_request(psa_signal_t sig, psa_msg_t *msg) +{ + val_status_t res = VAL_STATUS_ERROR; + psa_signal_t signals; + +wait: + signals = psa_wait(PSA_WAIT_ANY, PSA_BLOCK); + if (signals & sig) + { + if (psa_get(sig, msg) != PSA_SUCCESS) + { + goto wait; + } + + if ((msg->type != PSA_IPC_CALL) || (msg->handle <= 0)) + { + val_print_sf("\tpsa_get failed for PSA_IPC_CALL\n", 0); + res = VAL_STATUS_ERROR; + } + else + { + res = VAL_STATUS_SUCCESS; + } + } + else + { + val_print_sf("\tpsa_wait returned with invalid signal value = 0x%x\n", signals); + res = VAL_STATUS_ERROR; + } + return res; +} + +void driver_test_isolation_psa_rot_data_rd(psa_msg_t *msg) +{ + /* Send PSA RoT data address - global variable */ + psa_write(msg->handle, 0, (void *) &g_psa_rot_data, sizeof(addr_t)); + psa_reply(msg->handle, PSA_SUCCESS); +} + +void driver_test_isolation_psa_rot_data_wr(psa_msg_t *msg) +{ + /* Send PSA RoT data address - global variable */ + psa_write(msg->handle, 0, (void *) &g_psa_rot_data, sizeof(addr_t)); + + /* Setting boot.state before test check */ + if (val_driver_private_set_boot_flag_fn(BOOT_EXPECTED_S)) + { + val_print_sf("\tFailed to set boot flag before check\n", 0); + psa_reply(msg->handle, -2); + } + psa_reply(msg->handle, PSA_SUCCESS); + + /* Process second call request */ + if (VAL_ERROR(process_call_request(DRIVER_TEST_SIG, msg))) + { + psa_reply(msg->handle, -2); + return; + } + + /* Resetting boot.state to catch unwanted reboot */ + if (val_driver_private_set_boot_flag_fn(BOOT_EXPECTED_BUT_FAILED)) + { + val_print_sf("\tFailed to set boot flag after check\n", 0); + psa_reply(msg->handle, -2); + return; + } + + /* Reached here means there could be write succeed or ignored */ + if (g_psa_rot_data == DATA_VALUE) + { + psa_reply(msg->handle, PSA_SUCCESS); + } + else + { + val_print_sf("\tExpected write to fault but it didn't\n", 0); + psa_reply(msg->handle, -2); + } +} + +void driver_test_isolation_psa_rot_stack_rd(psa_msg_t *msg) +{ + uint32_t l_psa_rot_data = DATA_VALUE; + + /* Send PSA RoT stack address - local variable */ + psa_write(msg->handle, 0, (void *) &l_psa_rot_data, sizeof(addr_t)); + psa_reply(msg->handle, PSA_SUCCESS); + + /* Dummy print to avoid compiler optimisation on local variable */ + val_print_sf("\tl_psa_rot_data value 0x%x\n", l_psa_rot_data); +} + +void driver_test_isolation_psa_rot_stack_wr(psa_msg_t *msg) +{ + uint32_t l_psa_rot_data = DATA_VALUE; + + /* Send PSA RoT stack address - local variable */ + psa_write(msg->handle, 0, (void *) &l_psa_rot_data, sizeof(addr_t)); + + /* Setting boot.state before test check */ + if (val_driver_private_set_boot_flag_fn(BOOT_EXPECTED_S)) + { + val_print_sf("\tFailed to set boot flag before check\n", 0); + psa_reply(msg->handle, -2); + } + psa_reply(msg->handle, PSA_SUCCESS); + + /* Process second call request */ + if (VAL_ERROR(process_call_request(DRIVER_TEST_SIG, msg))) + { + psa_reply(msg->handle, -2); + return; + } + + /* Resetting boot.state to catch unwanted reboot */ + if (val_driver_private_set_boot_flag_fn(BOOT_EXPECTED_BUT_FAILED)) + { + val_print_sf("\tFailed to set boot flag after check\n", 0); + psa_reply(msg->handle, -2); + return; + } + + /* Reached here means there could be write succeed or ignored */ + if (l_psa_rot_data == DATA_VALUE) + { + psa_reply(msg->handle, PSA_SUCCESS); + } + else + { + val_print_sf("\tExpected write to fault but it didn't\n", 0); + psa_reply(msg->handle, -2); + } +} + +void driver_test_isolation_psa_rot_heap_rd(psa_msg_t *msg) +{ +#if (SP_HEAP_MEM_SUPP == 1) + uint8_t *buffer; + + buffer = (uint8_t *)malloc(sizeof(uint8_t) * BUFFER_SIZE); + memset((uint8_t *)buffer, (uint8_t)DATA_VALUE, BUFFER_SIZE); + + /* Send PSA RoT heap address */ + psa_write(msg->handle, 0, (void *) buffer, BUFFER_SIZE); + psa_reply(msg->handle, PSA_SUCCESS); + free(buffer); +#endif +} + +void driver_test_isolation_psa_rot_heap_wr(psa_msg_t *msg) +{ +#if (SP_HEAP_MEM_SUPP == 1) + uint8_t *buffer; + + buffer = (uint8_t *)malloc(sizeof(uint8_t) * BUFFER_SIZE); + memset((uint8_t *)buffer, (uint8_t)DATA_VALUE, BUFFER_SIZE); + + /* Send PSA RoT heap address */ + psa_write(msg->handle, 0, (void *) buffer, BUFFER_SIZE); + psa_reply(msg->handle, PSA_SUCCESS); + + /* Setting boot.state before test check */ + if (val_driver_private_set_boot_flag_fn(BOOT_EXPECTED_S)) + { + val_print_sf("\tFailed to set boot flag before check\n", 0); + psa_reply(msg->handle, -2); + } + + /* Process second call request */ + if (VAL_ERROR(process_call_request(DRIVER_TEST_SIG, msg))) + { + psa_reply(msg->handle, -2); + return; + } + + /* Resetting boot.state to catch unwanted reboot */ + if (val_driver_private_set_boot_flag_fn(BOOT_EXPECTED_BUT_FAILED)) + { + val_print_sf("\tFailed to set boot flag after check\n", 0); + psa_reply(msg->handle, -2); + return; + } + + /* Reached here means there could be write succeed or ignored */ + if (buffer[0] == (uint8_t)DATA_VALUE) + { + psa_reply(msg->handle, PSA_SUCCESS); + } + else + { + val_print_sf("\tExpected write to fault but it didn't\n", 0); + psa_reply(msg->handle, -2); + } + free(buffer); +#endif +} + +void driver_test_isolation_psa_rot_mmio_rd(psa_msg_t *msg) +{ + addr_t psa_rot_mmio_addr; + + if (VAL_ERROR(val_get_driver_mmio_addr(&psa_rot_mmio_addr))) + { + psa_reply(msg->handle, -2); + return; + } + + /* Send PSA RoT mmio address */ + memset((uint8_t *)&psa_rot_mmio_addr, (uint8_t)DATA_VALUE, sizeof(addr_t)); + psa_write(msg->handle, 0, (void *) &psa_rot_mmio_addr, sizeof(addr_t)); + psa_reply(msg->handle, PSA_SUCCESS); +} + +void driver_test_isolation_psa_rot_mmio_wr(psa_msg_t *msg) +{ + addr_t psa_rot_mmio_addr; + + if (VAL_ERROR(val_get_driver_mmio_addr(&psa_rot_mmio_addr))) + { + psa_reply(msg->handle, -2); + return; + } + + /* Send PSA RoT mmio address */ + memset((uint8_t *)&psa_rot_mmio_addr, (uint8_t)DATA_VALUE, sizeof(uint32_t)); + psa_write(msg->handle, 0, (void *) &psa_rot_mmio_addr, sizeof(uint32_t)); + psa_reply(msg->handle, PSA_SUCCESS); + + /* Setting boot.state before test check */ + if (val_driver_private_set_boot_flag_fn(BOOT_EXPECTED_S)) + { + val_print_sf("\tFailed to set boot flag before check\n", 0); + psa_reply(msg->handle, -2); + } + + /* Process second call request */ + if (VAL_ERROR(process_call_request(DRIVER_TEST_SIG, msg))) + { + psa_reply(msg->handle, -2); + return; + } + + /* Resetting boot.state to catch unwanted reboot */ + if (val_driver_private_set_boot_flag_fn(BOOT_EXPECTED_BUT_FAILED)) + { + val_print_sf("\tFailed to set boot flag after check\n", 0); + psa_reply(msg->handle, -2); + return; + } + + /* Reached here means there could be write succeed or ignored */ + if (*(uint32_t *)psa_rot_mmio_addr == (uint32_t)DATA_VALUE) + { + psa_reply(msg->handle, PSA_SUCCESS); + } + else + { + val_print_sf("\tExpected write to fault but it didn't\n", 0); + psa_reply(msg->handle, -2); + } +} diff --git a/api-tests/ff/partition/ipc/client_partition.c b/api-tests/ff/partition/ipc/client_partition.c index 794f6d24..7be5a6cb 100644 --- a/api-tests/ff/partition/ipc/client_partition.c +++ b/api-tests/ff/partition/ipc/client_partition.c @@ -25,6 +25,7 @@ void client_main(void) psa_signal_t signals = 0; val_status_t status, test_status; psa_msg_t msg = {0}; + test_info_t test_info; while (1) { @@ -40,7 +41,7 @@ void client_main(void) if (test_data != 0) { val_print(PRINT_ERROR, "must clear previous dispatcher connection\n", 0); - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa_reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); } else { @@ -56,7 +57,7 @@ void client_main(void) } if (VAL_ERROR(status)) { - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa_reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); break; } @@ -66,8 +67,10 @@ void client_main(void) val_print(PRINT_INFO, "In client part, test-id %d\n", GET_TEST_NUM(test_data)); + test_info.test_num = GET_TEST_NUM(test_data); + test_info.block_num = GET_BLOCK_NUM(test_data); /* Execute client test func from secure*/ - test_status = val_execute_secure_tests(GET_TEST_NUM(test_data), + test_status = val_execute_secure_tests(test_info, client_ipc_test_list[GET_TEST_NUM(test_data)]); } else if (GET_ACTION_NUM(test_data) == TEST_RETURN_RESULT) @@ -77,7 +80,7 @@ void client_main(void) } else { - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa_reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); } break; case PSA_IPC_DISCONNECT: @@ -94,7 +97,7 @@ void client_main(void) /* Server_partition requests client to connect to SERVER_SECURE_CONNECT_ONLY_SID */ else if (signals & PSA_DOORBELL) { - if (psa_connect(SERVER_SECURE_CONNECT_ONLY_SID, 2) != PSA_CONNECTION_REFUSED) + if (psa_connect(SERVER_SECURE_CONNECT_ONLY_SID, 2) != PSA_ERROR_CONNECTION_REFUSED) { val_print(PRINT_ERROR, "psa_connect failed \n", 0); } diff --git a/api-tests/ff/partition/ipc/server_partition.c b/api-tests/ff/partition/ipc/server_partition.c index 35c06418..de0e7a42 100644 --- a/api-tests/ff/partition/ipc/server_partition.c +++ b/api-tests/ff/partition/ipc/server_partition.c @@ -17,6 +17,9 @@ #include "server_partition.h" +val_api_t *val_server_sp = &val_api; +psa_api_t *psa_server_sp = &psa_api; + void server_main(void) { uint32_t test_data = 0; @@ -40,7 +43,7 @@ void server_main(void) if (test_data != 0) { val_print(PRINT_ERROR, "must clear previous dispatcher connection\n", 0); - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa_reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); } else { @@ -56,7 +59,7 @@ void server_main(void) } if (VAL_ERROR(status)) { - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa_reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); break; } @@ -80,7 +83,7 @@ void server_main(void) } else { - psa_reply(msg.handle, PSA_CONNECTION_REFUSED); + psa_reply(msg.handle, PSA_ERROR_CONNECTION_REFUSED); } break; case PSA_IPC_DISCONNECT: diff --git a/api-tests/platform/drivers/nvmem/pal_nvmem.c b/api-tests/platform/drivers/nvmem/pal_nvmem.c index 65a9442b..502d7010 100644 --- a/api-tests/platform/drivers/nvmem/pal_nvmem.c +++ b/api-tests/platform/drivers/nvmem/pal_nvmem.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/api-tests/platform/drivers/nvmem/pal_nvmem.h b/api-tests/platform/drivers/nvmem/pal_nvmem.h index 5a6f7be4..11443045 100644 --- a/api-tests/platform/drivers/nvmem/pal_nvmem.h +++ b/api-tests/platform/drivers/nvmem/pal_nvmem.h @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/api-tests/platform/drivers/uart/cmsdk/pal_uart.c b/api-tests/platform/drivers/uart/cmsdk/pal_uart.c index 5ccc5fcc..e55a4115 100644 --- a/api-tests/platform/drivers/uart/cmsdk/pal_uart.c +++ b/api-tests/platform/drivers/uart/cmsdk/pal_uart.c @@ -65,10 +65,10 @@ static void pal_uart_cmsdk_putc(uint8_t c) @param - str : Input String - data : Value for format specifier **/ -void pal_cmsdk_print(char *str, uint32_t data) +void pal_cmsdk_print(char *str, int32_t data) { - uint8_t j, buffer[16]; - int8_t i = 0; + int8_t j, buffer[16]; + int8_t i = 0, is_neg = 0; for (; *str != '\0'; ++str) { @@ -77,6 +77,12 @@ void pal_cmsdk_print(char *str, uint32_t data) ++str; if (*str == 'd') { + if (data < 0) + { + data = -(data); + is_neg = 1; + } + while (data != 0) { j = data % 10; @@ -84,6 +90,9 @@ void pal_cmsdk_print(char *str, uint32_t data) buffer[i] = j + 48; i += 1; } + + if (is_neg) + buffer[i++] = '-'; } else if (*str == 'x' || *str == 'X') { @@ -112,3 +121,39 @@ void pal_cmsdk_print(char *str, uint32_t data) } } } + +/** + @brief - This function checks for TX interrupt triggered or not +**/ +static int pal_uart_cmsdk_is_tx_irq_triggerd(void) +{ + return (((uart_t *) g_uart)->INTSTATUS & CMSDK_UART_INTSTATUS_TXIRQ_Msk); +} + +/** + @brief - This function triggers the UART TX interrupt +**/ +void pal_uart_cmsdk_generate_irq(void) +{ + /* Enable TX interrupt */ + ((uart_t *) g_uart)->CTRL |= CMSDK_UART_CTRL_TXIRQEN_Msk; + + /* Fill the TX buffer to generate TX IRQ */ + pal_uart_cmsdk_putc(' '); + pal_uart_cmsdk_putc(' '); + + /* Loop until TX interrupt trigger */ + while (!pal_uart_cmsdk_is_tx_irq_triggerd()); +} + +/** + @brief - This function disable the UART TX interrupt +**/ +void pal_uart_cmsdk_disable_irq(void) +{ + /* Clear TX interrupt */ + ((uart_t *) g_uart)->INTCLEAR |= CMSDK_UART_INTCLEAR_TXIRQ_Msk; + + /* Disable TX interrupt */ + ((uart_t *) g_uart)->CTRL &= ~CMSDK_UART_CTRL_TXIRQEN_Msk; +} diff --git a/api-tests/platform/drivers/uart/cmsdk/pal_uart.h b/api-tests/platform/drivers/uart/cmsdk/pal_uart.h index 7e82d417..442536f9 100644 --- a/api-tests/platform/drivers/uart/cmsdk/pal_uart.h +++ b/api-tests/platform/drivers/uart/cmsdk/pal_uart.h @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,6 +27,15 @@ /* 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 */ +#define CMSDK_UART_CTRL_TXIRQEN_Pos 2 /* CMSDK_UART CTRL: TXIRQEN Position */ +#define CMSDK_UART_CTRL_TXIRQEN_Msk (0x01ul << CMSDK_UART_CTRL_TXIRQEN_Pos) + /* CMSDK_UART CTRL: TXIRQEN Mask */ +#define CMSDK_UART_INTCLEAR_TXIRQ_Pos 0 /* CMSDK_UART CLEAR: TXIRQ Position */ +#define CMSDK_UART_INTCLEAR_TXIRQ_Msk (0x01ul << CMSDK_UART_INTCLEAR_TXIRQ_Pos) + /* CMSDK_UART CLEAR: TXIRQ Mask */ +#define CMSDK_UART_INTSTATUS_TXIRQ_Pos 0 /* CMSDK_UART STATUS: TXIRQ Position */ +#define CMSDK_UART_INTSTATUS_TXIRQ_Msk (0x01ul << CMSDK_UART_INTSTATUS_TXIRQ_Pos) + /* CMSDK_UART STATUS: TXIRQ Mask */ /* typedef's */ typedef struct { @@ -43,6 +52,8 @@ typedef struct { /* function prototypes */ void pal_uart_cmsdk_init(uint32_t uart_base_addr); -void pal_cmsdk_print(char *str, uint32_t data); +void pal_cmsdk_print(char *str, int32_t data); +void pal_uart_cmsdk_generate_irq(void); +void pal_uart_cmsdk_disable_irq(void); #endif /* _PAL_UART_CMSDK_H_ */ diff --git a/api-tests/platform/drivers/uart/pl011/pal_uart.c b/api-tests/platform/drivers/uart/pl011/pal_uart.c index b6ba1bd0..9a1095b5 100644 --- a/api-tests/platform/drivers/uart/pl011/pal_uart.c +++ b/api-tests/platform/drivers/uart/pl011/pal_uart.c @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,7 +17,7 @@ #include "pal_uart.h" -volatile uint32_t uart; +volatile uint32_t g_uart; /** @brief - This function initializes the UART @@ -25,7 +25,7 @@ volatile uint32_t uart; **/ void pal_uart_pl011_init(uint32_t uart_base_addr) { - uart = uart_base_addr; + g_uart = uart_base_addr; } /** @@ -33,11 +33,11 @@ void pal_uart_pl011_init(uint32_t uart_base_addr) **/ static int pal_uart_is_tx_empty(void) { - if ((((uart_gt *)uart)->uartcr & UART_PL011_UATRCR_EN_MASK) && + if ((((uart_t *)g_uart)->uartcr & UART_PL011_UATRCR_EN_MASK) && /* UART is enabled */ - (((uart_gt *)uart)->uartcr & UART_PL011_UARTCR_TX_EN_MASK) && + (((uart_t *)g_uart)->uartcr & UART_PL011_UARTCR_TX_EN_MASK) && /* Transmit is enabled */ - ((((uart_gt *)uart)->uartfr & UART_PL011_UARTFR_TX_FIFO_FULL) == 0)) + ((((uart_t *)g_uart)->uartfr & UART_PL011_UARTFR_TX_FIFO_FULL) == 0)) { return 1; } @@ -50,7 +50,7 @@ static int pal_uart_is_tx_empty(void) /** @brief - This function checks for empty TX FIFO and writes to FIFO register **/ -static void pal_uart_putc(uint8_t c) +void pal_uart_putc(uint8_t c) { const uint8_t pdata = (uint8_t)c; @@ -58,7 +58,7 @@ static void pal_uart_putc(uint8_t c) while(!pal_uart_is_tx_empty()); /* write the data (upper 24 bits are reserved) */ - ((uart_gt *)uart)->uartdr = pdata; + ((uart_t *)g_uart)->uartdr = pdata; } /** @@ -66,10 +66,10 @@ static void pal_uart_putc(uint8_t c) @param - str : Input String - data : Value for format specifier **/ -void pal_uart_pl011_print(char *str, uint32_t data) +void pal_uart_pl011_print(char *str, int32_t data) { uint8_t j, buffer[16]; - int8_t i = 0; + int8_t i = 0, is_neg = 0; for (; *str != '\0'; ++str) { @@ -78,6 +78,12 @@ void pal_uart_pl011_print(char *str, uint32_t data) ++str; if (*str == 'd') { + if (data < 0) + { + data = -(data); + is_neg = 1; + } + while (data != 0) { j = data % 10; @@ -85,6 +91,9 @@ void pal_uart_pl011_print(char *str, uint32_t data) buffer[i] = j + 48; i += 1; } + + if (is_neg) + buffer[i++] = '-'; } else if (*str == 'x' || *str == 'X') { @@ -119,3 +128,43 @@ void pal_uart_pl011_print(char *str, uint32_t data) } } } + +/** + @brief - This function checks for TX interrupt triggered or not +**/ +static int pal_uart_pl011_is_tx_irq_triggerd(void) +{ + return (((uart_t *) g_uart)->uartris & UART_PL011_TX_INTR_MASK); +} + +/** + @brief - This function triggers the UART TX interrupt +**/ +void pal_uart_pl011_generate_irq(void) +{ + volatile int bytecount = 32; + + /* Enable TX interrupt */ + ((uart_t *) g_uart)->uartimsc |= (uint32_t)(UART_PL011_TX_INTR_MASK); + + /* Fill the TX buffer to generate TX IRQ. TX buffer is 16x8 bit wide */ + do + { + pal_uart_putc(' '); + } while (--bytecount); + + /* Loop until TX interrupt trigger */ + while (!pal_uart_pl011_is_tx_irq_triggerd()); +} + +/** + @brief - This function disable the UART TX interrupt +**/ +void pal_uart_pl011_disable_irq(void) +{ + /* Clear TX interrupt */ + ((uart_t *) g_uart)->uarticr = (uint32_t)(UART_PL011_TX_INTR_MASK); + + /* Disable TX interrupt */ + ((uart_t *) g_uart)->uartimsc &= (uint32_t)(~UART_PL011_TX_INTR_MASK); +} diff --git a/api-tests/platform/drivers/uart/pl011/pal_uart.h b/api-tests/platform/drivers/uart/pl011/pal_uart.h index 0553013a..3b8ea8aa 100644 --- a/api-tests/platform/drivers/uart/pl011/pal_uart.h +++ b/api-tests/platform/drivers/uart/pl011/pal_uart.h @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -36,7 +36,13 @@ typedef struct volatile uint32_t uartfbrd; /* Offset: 0x028 (R/W) Fractional baud rate register */ volatile uint32_t uartlcr_h; /* Offset: 0x02C (R/W) Line control register */ volatile uint32_t uartcr; /* Offset: 0x030 (R/W) Control register */ -} uart_gt; + volatile uint32_t uartifls; /* Offset: 0x034 (R/W) Interrupt FIFO level select reg */ + volatile uint32_t uartimsc; /* Offset: 0x038 (R/W) Interrupt mask set/clear register */ + volatile uint32_t uartris; /* Offset: 0x03C (R/ ) Raw interrupt status register */ + volatile uint32_t uartmis; /* Offset: 0x040 (R/ ) Masked interrupt status register */ + volatile uint32_t uarticr; /* Offset: 0x044 ( /W) Interrupt clear register */ + volatile uint32_t uartdmacr; /* Offset: 0x048 (R/W) DMA control register */ +} uart_t; #define UART_PL011_UARTCR_UARTEN_OFF 0x0u #define UART_PL011_UARTCR_TXE_OFF 0x8u @@ -46,8 +52,15 @@ typedef struct #define UART_PL011_UARTCR_TX_EN_MASK (0x1u << UART_PL011_UARTCR_TXE_OFF) #define UART_PL011_UARTFR_TX_FIFO_FULL (0x1u << UART_PL011_UARTFR_TX_FIFO_FULL_OFF) +#define UART_PL011_INTR_TX_OFF 0x5u +#define UART_PL011_TX_INTR_MASK (0x1u << UART_PL011_INTR_TX_OFF) +#define UART_PL011_UARTLCR_H_FEN_OFF 0x4u +#define UART_PL011_UARTLCR_H_FEN_MASK (0x1u << UART_PL011_UARTLCR_H_FEN_OFF) + /* function prototypes */ void pal_uart_pl011_init(uint32_t uart_base_addr); -void pal_uart_pl011_print(char *str, uint32_t data); +void pal_uart_pl011_print(char *str, int32_t data); +void pal_uart_pl011_generate_irq(void); +void pal_uart_pl011_disable_irq(void); #endif /* _PAL_UART_CMSDK_H_ */ diff --git a/api-tests/platform/drivers/watchdog/cmsdk/pal_wd_cmsdk.h b/api-tests/platform/drivers/watchdog/cmsdk/pal_wd_cmsdk.h index 9bf33c66..930e1a1c 100644 --- a/api-tests/platform/drivers/watchdog/cmsdk/pal_wd_cmsdk.h +++ b/api-tests/platform/drivers/watchdog/cmsdk/pal_wd_cmsdk.h @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/Makefile b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/Makefile index a0230da2..f757e4b9 100644 --- a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/Makefile +++ b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/Makefile @@ -83,6 +83,7 @@ endif ifeq (${PSA_INITIAL_ATTESTATION_IMPLEMENTED},1) SRC_C_NSPE += pal_attestation_intf.c SRC_C_NSPE += pal_attestation_eat.c +SRC_C_NSPE += pal_attestation_crypto.c else SRC_C_NSPE += pal_attestation_empty_intf.c endif @@ -98,7 +99,7 @@ INCLUDE= -I$(SOURCE)/platform/targets/$(TARGET)/nspe \ -I$(SOURCE)/platform/targets/$(TARGET)/nspe/initial_attestation \ -I$(SOURCE)/platform/targets/$(TARGET)/nspe/initial_attestation/ext/inc \ -I$(SOURCE)/platform/targets/$(TARGET)/nspe/internal_trusted_storage \ - -I$(SOURCE)/platform/targets/$(TARGET)/nspe/protected_storage + -I$(SOURCE)/platform/targets/$(TARGET)/nspe/protected_storage \ VPATH=$(SOURCE)/platform/targets/$(TARGET)/: \ $(SOURCE)/platform/targets/$(TARGET)/spe: \ diff --git a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/manifests/common/driver_partition_psa.json b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/manifests/common/driver_partition_psa.json index 5d57571c..3e2ec674 100644 --- a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/manifests/common/driver_partition_psa.json +++ b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/manifests/common/driver_partition_psa.json @@ -3,11 +3,9 @@ "name": "DRIVER_PARTITION", "type": "PSA-ROT", "priority": "NORMAL", - "id": "0x00000003", "description": "Implements device services such print, flash read/write,. etc.", "entry_point": "driver_main", "stack_size": "0x400", - "heap_size": "0x100", "services": [{ "name": "DRIVER_UART_SID", "sid": "0x0000FC01", @@ -33,9 +31,9 @@ "minor_policy": "RELAXED" }, { - "name": "TEST_INTR_SID", + "name": "DRIVER_TEST_SID", "sid": "0x0000FC04", - "signal": "TEST_INTR_SIG", + "signal": "DRIVER_TEST_SIG", "non_secure_clients": true, "minor_version": 1, "minor_policy": "RELAXED" @@ -69,13 +67,9 @@ ], "irqs": [ { + "description": "Using UART TX interrupt to test psa_wait and psa_eoi for irq_signal", "signal": "DRIVER_UART_INTR_SIG", "line_num": 17 } - ], - "linker_pattern": { - "object_list": [ - "driver_partition.a" - ] - } + ] } diff --git a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/manifests/ipc/client_partition_psa.json b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/manifests/ipc/client_partition_psa.json index 081dc95d..b93377bd 100644 --- a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/manifests/ipc/client_partition_psa.json +++ b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/manifests/ipc/client_partition_psa.json @@ -3,11 +3,9 @@ "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": "0x100", "services": [{ "name": "CLIENT_TEST_DISPATCHER_SID", "sid": "0x0000FA01", @@ -20,7 +18,7 @@ "dependencies": [ "DRIVER_UART_SID", "DRIVER_NVMEM_SID", - "TEST_INTR_SID", + "DRIVER_TEST_SID", "SERVER_TEST_DISPATCHER_SID", "SERVER_UNSPECIFED_MINOR_V_SID", "SERVER_STRICT_MINOR_VERSION_SID", @@ -35,10 +33,5 @@ "size": "0x20", "permission": "READ-WRITE" } - ], - "linker_pattern": { - "object_list": [ - "client_partition.a" - ] - } + ] } diff --git a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/manifests/ipc/server_partition_psa.json b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/manifests/ipc/server_partition_psa.json index 3541387c..146b8fbc 100644 --- a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/manifests/ipc/server_partition_psa.json +++ b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/manifests/ipc/server_partition_psa.json @@ -3,7 +3,6 @@ "name": "SERVER_PARTITION", "type": "APPLICATION-ROT", "priority": "NORMAL", - "id": "0x00000002", "description": "Server partition executing server test func", "entry_point": "server_main", "stack_size": "0x400", @@ -66,10 +65,5 @@ "dependencies": [ "DRIVER_UART_SID", "DRIVER_NVMEM_SID" - ], - "linker_pattern": { - "object_list": [ - "server_partition.a" - ] - } + ] } diff --git a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/common/pal_config.h b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/common/pal_config.h index daa0ec5f..e3f70ad7 100644 --- a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/common/pal_config.h +++ b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/common/pal_config.h @@ -50,7 +50,7 @@ #endif #if !defined(VERBOSE) -#define VERBOSE 4 /* Print verbosity = ERROR */ +#define VERBOSE 3 /* Print verbosity = TEST */ #endif #if (!defined(VAL_NSPE_BUILD) && !defined(SPE_BUILD)) @@ -62,7 +62,15 @@ #endif #if !defined(TEST_COMBINE_ARCHIVE) -//#define TEST_COMBINE_ARCHIVE /* Test dispatcher code selection */ +#define TEST_COMBINE_ARCHIVE 0 /* Combine test archive or binary? */ +#endif + +#if !defined(WATCHDOG_AVAILABLE) +#define WATCHDOG_AVAILABLE 0 /* If zero, skip watchdog programming */ +#endif + +#if !defined(SP_HEAP_MEM_SUPP) +#define SP_HEAP_MEM_SUPP 0 /* Are Dynamic funcs available to secure partition? */ #endif /* @@ -79,6 +87,13 @@ * of this file. */ #include "psa_manifest/sid.h" + +/* + * psa_manifest/pid.h: Secure Partition IDs + * Macro definitions that map from Secure Partition names to Secure Partition IDs. + * Partition manifest parse build tool must provide the implementation of this file. +*/ +#include "psa_manifest/pid.h" #endif #if PSA_CRYPTO_IMPLEMENTED diff --git a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/common/pal_driver_ipc_intf.c b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/common/pal_driver_ipc_intf.c index 32aef9c5..f8f773fb 100644 --- a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/common/pal_driver_ipc_intf.c +++ b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/common/pal_driver_ipc_intf.c @@ -56,7 +56,7 @@ int pal_uart_init_ns(uint32_t uart_base_addr) @return - SUCCESS/FAILURE **/ -int pal_print_ns(char *str, uint32_t data) +int pal_print_ns(char *str, int32_t data) { int string_len = 0; char *p = str; diff --git a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/common/pal_driver_ns_intf.c b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/common/pal_driver_ns_intf.c index dd6b14c7..2af6fcc7 100644 --- a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/common/pal_driver_ns_intf.c +++ b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/common/pal_driver_ns_intf.c @@ -38,7 +38,7 @@ int pal_uart_init_ns(uint32_t uart_base_addr) @return - SUCCESS/FAILURE **/ -int pal_print_ns(char *str, uint32_t data) +int pal_print_ns(char *str, int32_t data) { pal_cmsdk_print(str, data); return PAL_STATUS_SUCCESS; diff --git a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/crypto/pal_crypto_intf.c b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/crypto/pal_crypto_intf.c index ad838f91..3df6aa8d 100644 --- a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/crypto/pal_crypto_intf.c +++ b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/crypto/pal_crypto_intf.c @@ -36,7 +36,7 @@ int32_t pal_crypto_function(int type, va_list valist) uint32_t status; const void *extra; size_t extra_size, capacity, *gen_cap, nonce_length, additional_data_length; - psa_key_handle_t handle, *key_handle; + psa_key_handle_t handle, *key_handle, target_handle; psa_key_type_t key_type, *key_type_out; psa_key_policy_t *policy; psa_key_usage_t usage, *usage_out; @@ -325,6 +325,11 @@ int32_t pal_crypto_function(int type, va_list valist) case PAL_CRYPTO_ALLOCATE_KEY: key_handle = (psa_key_handle_t *)va_arg(valist, int*); return psa_allocate_key(key_handle); + case PAL_CRYPTO_COPY_KEY: + handle = (psa_key_handle_t)va_arg(valist, int); + target_handle = (psa_key_handle_t)va_arg(valist, int); + policy = va_arg(valist, psa_key_policy_t*); + return psa_copy_key(handle, target_handle, policy); case PAL_CRYPTO_FREE: for (i = 0; i < PAL_KEY_SLOT_COUNT; i++) psa_destroy_key(i); diff --git a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/crypto/pal_crypto_intf.h b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/crypto/pal_crypto_intf.h index dfabee18..d1dabfa4 100644 --- a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/crypto/pal_crypto_intf.h +++ b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/crypto/pal_crypto_intf.h @@ -68,6 +68,7 @@ enum crypto_function_code { PAL_CRYPTO_ASYMMTERIC_VERIFY = 0x31, PAL_CRYPTO_KEY_AGREEMENT = 0x32, PAL_CRYPTO_ALLOCATE_KEY = 0x33, + PAL_CRYPTO_COPY_KEY = 0x34, PAL_CRYPTO_FREE = 0xFE, }; diff --git a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_crypto.c b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_crypto.c new file mode 100644 index 00000000..ae2bdba4 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_crypto.c @@ -0,0 +1,346 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_attestation_crypto.h" + +static uint32_t public_key_registered = 0; + +static inline struct q_useful_buf_c useful_buf_head(struct q_useful_buf_c buf, + size_t amount) +{ + return UsefulBuf_Head(buf, amount); +} + +static uint32_t check_hash_sizes(void) +{ + if (T_COSE_CRYPTO_SHA256_SIZE != PSA_HASH_SIZE(PSA_ALG_SHA_256)) + { + return PAL_ATTEST_HASH_FAIL; + } + + return PAL_ATTEST_SUCCESS; +} + +static psa_ecc_curve_t attest_map_elliptic_curve_type(int32_t cose_curve) +{ + psa_ecc_curve_t psa_curve; + + /*FixMe: Mapping is not complete, missing ones: P384, P521, ED25519, ED448 */ + switch (cose_curve) + { + case P_256: + psa_curve = PSA_ECC_CURVE_SECP256R1; + break; + default: + psa_curve = USHRT_MAX; + } + + return psa_curve; +} + +static psa_algorithm_t cose_hash_alg_id_to_psa(int32_t cose_hash_alg_id) +{ + psa_algorithm_t status; + + switch (cose_hash_alg_id) + { + case COSE_ALG_SHA256_PROPRIETARY: + status = PSA_ALG_SHA_256; + break; + default: + status = PSA_ALG_MD4; + break; + } + + return status; +} + +static int32_t hash_alg_id_from_sig_alg_id(int32_t cose_sig_alg_id) +{ + switch (cose_sig_alg_id) + { + case COSE_ALGORITHM_ES256: + return COSE_ALG_SHA256_PROPRIETARY; + default: + return INT32_MAX; + } +} + +int32_t pal_cose_crypto_hash_start(struct pal_cose_crypto_hash *hash_ctx, int32_t cose_hash_alg_id) +{ + int32_t cose_ret = PAL_ATTEST_SUCCESS; + psa_status_t psa_ret; + struct pal_cose_psa_crypto_hash *psa_hash_ctx; + + cose_ret = check_hash_sizes(); + if (cose_ret) + { + goto error; + } + + if (sizeof(struct pal_cose_crypto_hash) < sizeof(struct pal_cose_psa_crypto_hash)) + { + cose_ret = PAL_ATTEST_HASH_FAIL; + goto error; + } + + psa_hash_ctx = (struct pal_cose_psa_crypto_hash *)hash_ctx; + psa_ret = psa_hash_setup(&psa_hash_ctx->operation, cose_hash_alg_id_to_psa(cose_hash_alg_id)); + + if (psa_ret == PAL_ATTEST_SUCCESS) + { + psa_hash_ctx->status = PAL_ATTEST_SUCCESS; + cose_ret = PAL_ATTEST_SUCCESS; + } + else if (psa_ret == PSA_ERROR_NOT_SUPPORTED) + { + cose_ret = PAL_ATTEST_HASH_UNSUPPORTED; + } + else + { + cose_ret = PAL_ATTEST_HASH_FAIL; + } + +error: + return cose_ret; +} + +void pal_cose_crypto_hash_update(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf_c data_to_hash) +{ + struct pal_cose_psa_crypto_hash *psa_hash_ctx; + + if (sizeof(struct pal_cose_crypto_hash) < sizeof(struct pal_cose_psa_crypto_hash)) + { + return; + } + + psa_hash_ctx = (struct pal_cose_psa_crypto_hash *)hash_ctx; + + if (psa_hash_ctx->status == PAL_ATTEST_SUCCESS) + { + if (data_to_hash.ptr != NULL) + { + psa_hash_ctx->status = psa_hash_update(&psa_hash_ctx->operation, + data_to_hash.ptr, + data_to_hash.len); + } + else + { + /* Intentionally do nothing, just computing the size of the token */ + } + } +} + +int32_t pal_cose_crypto_hash_finish(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf buffer_to_hold_result, + struct q_useful_buf_c *hash_result) +{ + uint32_t cose_ret = PAL_ATTEST_SUCCESS; + psa_status_t psa_ret; + struct pal_cose_psa_crypto_hash *psa_hash_ctx; + + if (sizeof(struct pal_cose_crypto_hash) < sizeof(struct pal_cose_psa_crypto_hash)) + { + cose_ret = PAL_ATTEST_HASH_FAIL; + goto error; + } + + psa_hash_ctx = (struct pal_cose_psa_crypto_hash *)hash_ctx; + + if (psa_hash_ctx->status == PAL_ATTEST_SUCCESS) + { + psa_ret = psa_hash_finish(&psa_hash_ctx->operation, + buffer_to_hold_result.ptr, + buffer_to_hold_result.len, + &(hash_result->len)); + + if (psa_ret == PAL_ATTEST_SUCCESS) + { + hash_result->ptr = buffer_to_hold_result.ptr; + cose_ret = 0; + } + else if (psa_ret == PSA_ERROR_BUFFER_TOO_SMALL) + { + cose_ret = PAL_ATTEST_HASH_BUFFER_SIZE; + } + else + { + cose_ret = PAL_ATTEST_HASH_FAIL; + } + } + else + { + cose_ret = PAL_ATTEST_HASH_FAIL; + } + +error: + return cose_ret; +} + +int pal_create_sha256(struct q_useful_buf_c bytes_to_hash, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash) +{ + uint32_t status = PAL_ATTEST_SUCCESS; + struct pal_cose_crypto_hash hash_ctx; + + status = pal_cose_crypto_hash_start(&hash_ctx, COSE_ALG_SHA256_PROPRIETARY); + if (status) + return status; + + pal_cose_crypto_hash_update(&hash_ctx, bytes_to_hash); + status = pal_cose_crypto_hash_finish(&hash_ctx, buffer_for_hash, hash); + + return status; +} + +uint32_t pal_compute_hash(int32_t cose_alg_id, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash, struct q_useful_buf_c protected_headers, + struct q_useful_buf_c payload) +{ + uint32_t status; + QCBOREncodeContext cbor_encode_ctx; + struct q_useful_buf_c tbs_first_part; + QCBORError qcbor_result; + struct pal_cose_crypto_hash hash_ctx = {{0}}; + int32_t hash_alg_id; + UsefulBuf_MAKE_STACK_UB (buffer_for_TBS_first_part, T_COSE_SIZE_OF_TBS); + + /* This builds the CBOR-format to-be-signed bytes */ + QCBOREncode_Init(&cbor_encode_ctx, buffer_for_TBS_first_part); + QCBOREncode_OpenArray(&cbor_encode_ctx); + /* context */ + QCBOREncode_AddSZString(&cbor_encode_ctx, + COSE_SIG_CONTEXT_STRING_SIGNATURE1); + /* body_protected */ + QCBOREncode_AddBytes(&cbor_encode_ctx, + protected_headers); + /* sign_protected */ + QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_USEFUL_BUF_C); + /* external_aad */ + QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_USEFUL_BUF_C); + /* fake payload */ + QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_USEFUL_BUF_C); + QCBOREncode_CloseArray(&cbor_encode_ctx); + + /* Get the result and convert it to struct q_useful_buf_c representation */ + qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &tbs_first_part); + if (qcbor_result) + { + /* Mainly means that the protected_headers were too big + (which should never happen) */ + status = PAL_ATTEST_ERR_SIGN_STRUCT; + goto Done; + } + + /* Start the hashing */ + hash_alg_id = hash_alg_id_from_sig_alg_id(cose_alg_id); + + /* Don't check hash_alg_id for failure. pal_cose_crypto_hash_start() + * will handle it properly + */ + status = pal_cose_crypto_hash_start(&hash_ctx, hash_alg_id); + if (status) + goto Done; + + /* Hash the first part of the TBS. Take all but the last two + * bytes. The last two bytes are the fake payload from above. It + * is replaced by the real payload which is hashed next. The fake + * payload is needed so the array count is right. This is one of + * the main things that make it possible to implement with one + * buffer for the whole cose sign1. + */ + pal_cose_crypto_hash_update(&hash_ctx, useful_buf_head(tbs_first_part, + tbs_first_part.len - 2)); + + /* Hash the payload */ + pal_cose_crypto_hash_update(&hash_ctx, payload); + + /* Finish the hash and set up to return it */ + status = pal_cose_crypto_hash_finish(&hash_ctx, + buffer_for_hash, + hash); + +Done: + return status; +} + +uint32_t pal_import_attest_key(int32_t alg) +{ + psa_key_type_t attest_key_type; + size_t public_key_size; + psa_status_t status = PSA_SUCCESS; + psa_key_policy_t policy; + psa_ecc_curve_t psa_curve; + psa_key_handle_t public_key_handle; + + /* Mapping of COSE curve type to PSA curve types */ + psa_curve = attest_map_elliptic_curve_type(P_256); + if (psa_curve == USHRT_MAX) + return PAL_ATTEST_ERROR; + + /* Setup the key policy for public key */ + policy = psa_key_policy_init(); + psa_key_policy_set_usage(&policy, PSA_KEY_USAGE_VERIFY, alg); + + status = psa_allocate_key(&public_key_handle); + if (status != PSA_SUCCESS) + return status; + + status = psa_set_key_policy(public_key_handle, &policy); + if (status != PSA_SUCCESS) + return status; + + attest_key_type = PSA_KEY_TYPE_ECC_PUBLIC_KEY(psa_curve); + + /* Register public key to crypto service */ + public_key_size = attest_key.pubx_key_size + attest_key.puby_key_size; + + status = psa_import_key(public_key_handle, + attest_key_type, + (const uint8_t *)&attest_public_key, + public_key_size + 1); + + return status; +} + + +uint32_t pal_crypto_pub_key_verify(int32_t cose_algorithm_id, + struct q_useful_buf_c token_hash, + struct q_useful_buf_c signature) +{ + uint32_t status = PAL_ATTEST_SUCCESS; + + if (!public_key_registered) + { + status = pal_import_attest_key(cose_algorithm_id); + if (status != PAL_ATTEST_SUCCESS) + return status; + + public_key_registered = 1; + } + +/* + * Enable the verify function when Trusted Firmare - M Supports + + * Verify the signature a hash or short message using a public key. + status = psa_asymmetric_verify(public_key_handle, + cose_algorithm_id, token_hash.ptr, token_hash.len, + signature.ptr, signature.len); +*/ + return status; +} diff --git a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_crypto.h b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_crypto.h new file mode 100644 index 00000000..2d63ad13 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_crypto.h @@ -0,0 +1,102 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_attestation_eat.h" + +#define ATTEST_PUBLIC_KEY_SLOT 4 + +typedef struct{ + uint8_t *pubx_key; + uint32_t pubx_key_size; + uint8_t *puby_key; + uint32_t puby_key_size; +} ecc_key_t; + +struct ecc_public_key_t { + const uint8_t a; + uint8_t public_key[]; /* X-coordinate || Y-coordinate */ +}; + +static const struct ecc_public_key_t attest_public_key = { + /* Constant byte */ + 0x04, + /* X-coordinate */ + {0x79, 0xEB, 0xA9, 0x0E, 0x8B, 0xF4, 0x50, 0xA6, + 0x75, 0x15, 0x76, 0xAD, 0x45, 0x99, 0xB0, 0x7A, + 0xDF, 0x93, 0x8D, 0xA3, 0xBB, 0x0B, 0xD1, 0x7D, + 0x00, 0x36, 0xED, 0x49, 0xA2, 0xD0, 0xFC, 0x3F, + /* Y-coordinate */ + 0xBF, 0xCD, 0xFA, 0x89, 0x56, 0xB5, 0x68, 0xBF, + 0xDB, 0x86, 0x73, 0xE6, 0x48, 0xD8, 0xB5, 0x8D, + 0x92, 0x99, 0x55, 0xB1, 0x4A, 0x26, 0xC3, 0x08, + 0x0F, 0x34, 0x11, 0x7D, 0x97, 0x1D, 0x68, 0x64}, +}; + +struct pal_cose_crypto_hash { + /* Can't put the actual size here without creating dependecy on + * actual hash implementation, so this is a fairly large and + * accommodating size. + */ + uint8_t bytes[128]; +}; + +struct pal_cose_psa_crypto_hash { + psa_status_t status; + psa_hash_operation_t operation; +}; + +static const uint8_t initial_attestation_public_x_key[] = +{ + 0x79, 0xEB, 0xA9, 0x0E, 0x8B, 0xF4, 0x50, 0xA6, + 0x75, 0x15, 0x76, 0xAD, 0x45, 0x99, 0xB0, 0x7A, + 0xDF, 0x93, 0x8D, 0xA3, 0xBB, 0x0B, 0xD1, 0x7D, + 0x00, 0x36, 0xED, 0x49, 0xA2, 0xD0, 0xFC, 0x3F +}; + +static const uint8_t initial_attestation_public_y_key[] = +{ + 0xBF, 0xCD, 0xFA, 0x89, 0x56, 0xB5, 0x68, 0xBF, + 0xDB, 0x86, 0x73, 0xE6, 0x48, 0xD8, 0xB5, 0x8D, + 0x92, 0x99, 0x55, 0xB1, 0x4A, 0x26, 0xC3, 0x08, + 0x0F, 0x34, 0x11, 0x7D, 0x97, 0x1D, 0x68, 0x64 +}; + +/* Initialize the structure with given public key */ +static const ecc_key_t attest_key = { + (uint8_t *)initial_attestation_public_x_key, + sizeof(initial_attestation_public_x_key), + (uint8_t *)initial_attestation_public_y_key, + sizeof(initial_attestation_public_y_key) +}; + +int32_t pal_cose_crypto_hash_start(struct pal_cose_crypto_hash *hash_ctx, int32_t cose_hash_alg_id); +void pal_cose_crypto_hash_update(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf_c data_to_hash); +int32_t pal_cose_crypto_hash_finish(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf buffer_to_hold_result, + struct q_useful_buf_c *hash_result); +int pal_create_sha256(struct q_useful_buf_c bytes_to_hash, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash); +uint32_t pal_compute_hash(int32_t cose_alg_id, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash, struct q_useful_buf_c protected_headers, + struct q_useful_buf_c payload); +uint32_t pal_import_attest_key(int32_t alg); +uint32_t pal_crypto_pub_key_verify(int32_t cose_algorithm_id, struct q_useful_buf_c token_hash, + struct q_useful_buf_c signature); + + diff --git a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_eat.c b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_eat.c index 262dc5dd..178fdc9c 100644 --- a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_eat.c +++ b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_eat.c @@ -15,10 +15,56 @@ * limitations under the License. **/ -#include "pal_attestation_eat.h" +#include "pal_attestation_crypto.h" + +uint32_t mandatory_claims = 0; +uint32_t mandaroty_sw_components = 0; +bool_t sw_component_present = 0; + +static int pal_encode_cose_key(struct q_useful_buf_c *cose_key, + struct q_useful_buf buffer_for_cose_key, + struct q_useful_buf_c x_cord, struct q_useful_buf_c y_cord) +{ + uint32_t return_value; + QCBORError qcbor_result; + QCBOREncodeContext cbor_encode_ctx; + int32_t cose_curve_id = P_256; + struct q_useful_buf_c encoded_key_id; + + /* Get the public key x and y */ + /* Encode it into a COSE_Key structure */ + QCBOREncode_Init(&cbor_encode_ctx, buffer_for_cose_key); + QCBOREncode_OpenMap(&cbor_encode_ctx); + QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx, + COSE_KEY_COMMON_KTY, + COSE_KEY_TYPE_EC2); + QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx, + COSE_KEY_PARAM_CRV, + cose_curve_id); + QCBOREncode_AddBytesToMapN(&cbor_encode_ctx, + COSE_KEY_PARAM_X_COORDINATE, + x_cord); + QCBOREncode_AddBytesToMapN(&cbor_encode_ctx, + COSE_KEY_PARAM_Y_COORDINATE, + y_cord); + QCBOREncode_CloseMap(&cbor_encode_ctx); + + qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &encoded_key_id); + if (qcbor_result != QCBOR_SUCCESS) + { + /* Mainly means that the COSE_Key was too big for buffer_for_cose_key */ + return_value = PAL_ATTEST_ERR_PROTECTED_HEADERS; + goto Done; + } + + /* Finish up and return */ + *cose_key = encoded_key_id; + return_value = PAL_ATTEST_SUCCESS; + +Done: + return return_value; +} -uint32_t mandatory_claims = 0, mandaroty_sw_components = 0; -bool_t sw_component_present = 0; static int get_items_in_map(QCBORDecodeContext *decode_context, struct items_to_get_t *item_list) @@ -90,7 +136,7 @@ static int get_item_in_map(QCBORDecodeContext *decode_context, } static int parse_unprotected_headers(QCBORDecodeContext *decode_context, - struct useful_buf_c *child, + struct q_useful_buf_c *child, bool *loop_back) { struct items_to_get_t item_list[3]; @@ -120,7 +166,7 @@ static int parse_unprotected_headers(QCBORDecodeContext *decode_context, return PAL_ATTEST_SUCCESS; } -static int parse_protected_headers(struct useful_buf_c protected_headers, +static int parse_protected_headers(struct q_useful_buf_c protected_headers, int32_t *alg_id) { QCBORDecodeContext decode_context; @@ -156,7 +202,7 @@ static int parse_protected_headers(struct useful_buf_c protected_headers, @return - error status **/ static int parse_claims(QCBORDecodeContext *decode_context, QCBORItem item, - struct useful_buf_c completed_challenge) + struct q_useful_buf_c completed_challenge) { int i, count = 0; int status = PAL_ATTEST_SUCCESS; @@ -281,16 +327,42 @@ static int parse_claims(QCBORDecodeContext *decode_context, QCBORItem item, int32_t pal_initial_attest_verify_token(uint8_t *challenge, uint32_t challenge_size, uint8_t *token, uint32_t token_size) { - int status = PAL_ATTEST_SUCCESS; + int32_t status = PAL_ATTEST_SUCCESS; bool short_circuit; int32_t cose_algorithm_id; QCBORItem item; QCBORDecodeContext decode_context; - struct useful_buf_c completed_challenge; - struct useful_buf_c completed_token; - struct useful_buf_c payload; - struct useful_buf_c protected_headers; - struct useful_buf_c kid; + struct q_useful_buf_c completed_challenge; + struct q_useful_buf_c completed_token; + struct q_useful_buf_c payload; + struct q_useful_buf_c signature; + struct q_useful_buf_c protected_headers; + struct q_useful_buf_c kid; + struct q_useful_buf_c x_cord; + struct q_useful_buf_c y_cord; + struct q_useful_buf_c cose_key_to_hash; + struct q_useful_buf_c key_hash; + struct q_useful_buf_c token_hash; + USEFUL_BUF_MAKE_STACK_UB(buf_to_hold_x_coord, T_COSE_CRYPTO_EC_P256_COORD_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buf_to_hold_y_coord, T_COSE_CRYPTO_EC_P256_COORD_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_kid, T_COSE_CRYPTO_SHA256_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_cose_key, MAX_ENCODED_COSE_KEY_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_encoded_key, MAX_ENCODED_COSE_KEY_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_token_hash, T_COSE_CRYPTO_SHA256_SIZE); + + kid.ptr = buffer_for_encoded_key.ptr; + + memcpy(buf_to_hold_x_coord.ptr, (const void *)attest_key.pubx_key, attest_key.pubx_key_size); + memcpy(buf_to_hold_y_coord.ptr, (const void *)attest_key.puby_key, attest_key.puby_key_size); + + /* Update size */ + buf_to_hold_x_coord.len = attest_key.pubx_key_size; + buf_to_hold_y_coord.len = attest_key.puby_key_size; + + x_cord.ptr = buf_to_hold_x_coord.ptr; + x_cord.len = buf_to_hold_x_coord.len; + y_cord.ptr = buf_to_hold_y_coord.ptr; + y_cord.len = buf_to_hold_y_coord.len; /* Construct the token buffer for validation */ completed_token.ptr = token; @@ -345,6 +417,27 @@ int32_t pal_initial_attest_verify_token(uint8_t *challenge, uint32_t challenge_s if (status != PAL_ATTEST_SUCCESS) return status; + /* Encode the given public key */ + status = pal_encode_cose_key(&cose_key_to_hash, buffer_for_cose_key, x_cord, y_cord); + if (status != PAL_ATTEST_SUCCESS) + return status; + + /* Create hash of the given public key */ + status = pal_create_sha256(cose_key_to_hash, buffer_for_kid, &key_hash); + if (status != PSA_SUCCESS) + return status; + + /* Compare the hash of the public key in token and hash of the given public key */ + if (kid.len != key_hash.len) + { + return PAL_ATTEST_HASH_LENGTH_MISMATCH; + } + + if (memcmp(kid.ptr, key_hash.ptr, kid.len) != 0) + { + return PAL_ATTEST_HASH_MISMATCH; + } + /* Get the payload */ QCBORDecode_GetNext(&decode_context, &item); if (item.uDataType != QCBOR_TYPE_BYTE_STRING) @@ -357,6 +450,19 @@ int32_t pal_initial_attest_verify_token(uint8_t *challenge, uint32_t challenge_s if (item.uDataType != QCBOR_TYPE_BYTE_STRING) return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + signature = item.val.string; + + /* Compute the hash from the token */ + status = pal_compute_hash(cose_algorithm_id, buffer_for_token_hash, &token_hash, + protected_headers, payload); + if (status != PAL_ATTEST_SUCCESS) + return status; + + /* Verify the signature */ + status = pal_crypto_pub_key_verify(cose_algorithm_id, token_hash, signature); + if (status != PAL_ATTEST_SUCCESS) + return status; + /* Initialize the Decoder and validate the payload format */ QCBORDecode_Init(&decode_context, payload, QCBOR_DECODE_MODE_NORMAL); status = QCBORDecode_GetNext(&decode_context, &item); diff --git a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_eat.h b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_eat.h index 9f435fb3..8a0c5455 100644 --- a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_eat.h +++ b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_eat.h @@ -17,16 +17,70 @@ #include "qcbor.h" #include "pal_common.h" +#include "psa/crypto.h" #define PAL_ATTEST_MIN_ERROR 30 +/* NIST P-256 also known as secp256r1 */ +#define P_256 1 + #define COSE_HEADER_PARAM_ALG 1 #define COSE_HEADER_PARAM_KID 4 -#define MANDATORY_CLAIM_WITH_SW_COMP 862 -#define MANDATORY_CLAIM_NO_SW_COMP 926 -#define MANDATORY_SW_COMP 36 -#define CBOR_ARM_TOTAL_CLAIM_INSTANCE 10 +#define COSE_KEY_COMMON_KTY 1 +#define COSE_KEY_TYPE_EC2 2 +#define COSE_KEY_PARAM_CRV -1 +#define COSE_KEY_PARAM_X_COORDINATE -2 +#define COSE_KEY_PARAM_Y_COORDINATE -3 +#define COSE_ALGORITHM_ES256 -7 +#define COSE_ALG_SHA256_PROPRIETARY -72000 + +/** + * The size of X and Y coordinate in 2 parameter style EC public + * key. Format is as defined in [COSE (RFC 8152)] + * (https://tools.ietf.org/html/rfc8152) and [SEC 1: Elliptic Curve + * Cryptography](http://www.secg.org/sec1-v2.pdf). + * + * This size is well-known and documented in public standards. + */ +#define T_COSE_CRYPTO_EC_P256_COORD_SIZE 32 +#define T_COSE_CRYPTO_SHA256_SIZE 32 + +#define MAX_ENCODED_COSE_KEY_SIZE \ + 1 + /* 1 byte to encode map */ \ + 2 + /* 2 bytes to encode key type */ \ + 2 + /* 2 bytes to encode curve */ \ + 2 * /* the X and Y coordinates at 32 bytes each */ \ + (T_COSE_CRYPTO_EC_P256_COORD_SIZE + 1 + 2) +#define USEFUL_BUF_MAKE_STACK_UB UsefulBuf_MAKE_STACK_UB + +#define COSE_SIG_CONTEXT_STRING_SIGNATURE1 "Signature1" + +/* Private value. Intentionally not documented for Doxygen. + * This is the size allocated for the encoded protected headers. It + * needs to be big enough for make_protected_header() to succeed. It + * currently sized for one header with an algorithm ID up to 32 bits + * long -- one byte for the wrapping map, one byte for the label, 5 + * bytes for the ID. If this is made accidentially too small, QCBOR will + * only return an error, and not overrun any buffers. + * + * 9 extra bytes are added, rounding it up to 16 total, in case some + * other protected header is to be added. + */ +#define T_COSE_SIGN1_MAX_PROT_HEADER (1+1+5+9) + +/** + * This is the size of the first part of the CBOR encoded TBS + * bytes. It is around 20 bytes. See create_tbs_hash(). + */ +#define T_COSE_SIZE_OF_TBS \ + 1 + /* For opening the array */ \ + sizeof(COSE_SIG_CONTEXT_STRING_SIGNATURE1) + /* "Signature1" */ \ + 2 + /* Overhead for encoding string */ \ + T_COSE_SIGN1_MAX_PROT_HEADER + /* entire protected headers */ \ + 3 * ( /* 3 NULL bstrs for fields not used */ \ + 1 /* size of a NULL bstr */ \ + ) /* CBOR Label for proprietary header indicating short-circuit @@ -47,6 +101,8 @@ #define EAT_CBOR_ARM_LABEL_UEID (EAT_CBOR_ARM_RANGE_BASE - 9) #define EAT_CBOR_ARM_LABEL_ORIGINATION (EAT_CBOR_ARM_RANGE_BASE - 10) +#define CBOR_ARM_TOTAL_CLAIM_INSTANCE 10 + #define EAT_CBOR_SW_COMPONENT_TYPE (1u) #define EAT_CBOR_SW_COMPONENT_MEASUREMENT (2u) #define EAT_CBOR_SW_COMPONENT_EPOCH (3u) @@ -54,6 +110,40 @@ #define EAT_CBOR_SW_COMPONENT_SIGNER_ID (5u) #define EAT_CBOR_SW_COMPONENT_MEASUREMENT_DESC (6u) +#define MANDATORY_CLAIM_WITH_SW_COMP (1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_NONCE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_UEID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_CLIENT_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_BOOT_SEED) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_SW_COMPONENTS)) + +#define MANDATORY_CLAIM_NO_SW_COMP (1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_NONCE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_UEID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_CLIENT_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_BOOT_SEED) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_NO_SW_COMPONENTS)) + +#define MANDATORY_SW_COMP (1 << EAT_CBOR_SW_COMPONENT_MEASUREMENT | \ + 1 << EAT_CBOR_SW_COMPONENT_SIGNER_ID) + +#define NULL_USEFUL_BUF_C NULLUsefulBufC enum attestation_error_code { PAL_ATTEST_SUCCESS = 0, @@ -61,6 +151,13 @@ enum attestation_error_code { PAL_ATTEST_TOKEN_CHALLENGE_MISMATCH, PAL_ATTEST_TOKEN_NOT_SUPPORTED, PAL_ATTEST_TOKEN_NOT_ALL_MANDATORY_CLAIMS, + PAL_ATTEST_HASH_LENGTH_MISMATCH, + PAL_ATTEST_HASH_MISMATCH, + PAL_ATTEST_HASH_FAIL, + PAL_ATTEST_HASH_UNSUPPORTED, + PAL_ATTEST_HASH_BUFFER_SIZE, + PAL_ATTEST_ERR_PROTECTED_HEADERS, + PAL_ATTEST_ERR_SIGN_STRUCT, PAL_ATTEST_ERROR, }; diff --git a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_intf.h b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_intf.h index ef132b5b..12f6ee94 100644 --- a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_intf.h +++ b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_intf.h @@ -18,7 +18,7 @@ #ifndef _PAL_INITIAL_ATTESTATION_H_ #define _PAL_INITIAL_ATTESTATION_H_ -#include "pal_attestation_eat.h" +#include "pal_attestation_crypto.h" enum attestation_function_code { PAL_INITIAL_ATTEST_GET_TOKEN = 0x1, diff --git a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/spe/pal_driver_intf.c b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/spe/pal_driver_intf.c index cc8b5373..fd307839 100644 --- a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/spe/pal_driver_intf.c +++ b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/spe/pal_driver_intf.c @@ -33,7 +33,7 @@ void pal_uart_init(uint32_t uart_base_addr) - data : Value for format specifier **/ -void pal_print(char *str, uint32_t data) +void pal_print(char *str, int32_t data) { pal_cmsdk_print(str,data); @@ -110,3 +110,23 @@ int pal_wd_timer_is_enabled(addr_t base_addr) return (pal_wd_cmsdk_is_enabled(base_addr)); } +/** + @brief - Trigger interrupt for irq signal assigned to driver partition + before return to caller. + @param - void + @return - void +**/ +void pal_generate_interrupt(void) +{ + pal_uart_cmsdk_generate_irq(); +} + +/** + @brief - Disable interrupt that was generated using pal_generate_interrupt API. + @param - void + @return - void +**/ +void pal_disable_interrupt(void) +{ + pal_uart_cmsdk_disable_irq(); +} diff --git a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/spe/pal_driver_intf.h b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/spe/pal_driver_intf.h index da85a63e..cef34ca8 100644 --- a/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/spe/pal_driver_intf.h +++ b/api-tests/platform/targets/tgt_dev_apis_mbedos_fvp_mps2_m4/spe/pal_driver_intf.h @@ -23,11 +23,13 @@ #include "pal_wd_cmsdk.h" void pal_uart_init(uint32_t uart_base_addr); -void pal_print(char *str, uint32_t data); +void pal_print(char *str, int32_t data); int pal_nvmem_write(addr_t base, uint32_t offset, void *buffer, int size); int pal_nvmem_read(addr_t base, uint32_t offset, void *buffer, int size); int pal_wd_timer_init(addr_t base_addr, uint32_t time_us, uint32_t timer_tick_us); int pal_wd_timer_enable(addr_t base_addr); int pal_wd_timer_disable(addr_t base_addr); int pal_wd_timer_is_enabled(addr_t base_addr); +void pal_generate_interrupt(void); +void pal_disable_interrupt(void); #endif /* _PAL_DRIVER_INTF_H_ */ diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/Makefile b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/Makefile index 3bd8c232..473879d5 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/Makefile +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/Makefile @@ -83,6 +83,7 @@ endif ifeq (${PSA_INITIAL_ATTESTATION_IMPLEMENTED},1) SRC_C_NSPE += pal_attestation_intf.c SRC_C_NSPE += pal_attestation_eat.c +SRC_C_NSPE += pal_attestation_crypto.c else SRC_C_NSPE += pal_attestation_empty_intf.c endif @@ -98,7 +99,7 @@ INCLUDE= -I$(SOURCE)/platform/targets/$(TARGET)/nspe \ -I$(SOURCE)/platform/targets/$(TARGET)/nspe/initial_attestation \ -I$(SOURCE)/platform/targets/$(TARGET)/nspe/initial_attestation/ext/inc \ -I$(SOURCE)/platform/targets/$(TARGET)/nspe/internal_trusted_storage \ - -I$(SOURCE)/platform/targets/$(TARGET)/nspe/protected_storage + -I$(SOURCE)/platform/targets/$(TARGET)/nspe/protected_storage \ VPATH=$(SOURCE)/platform/targets/$(TARGET)/: \ $(SOURCE)/platform/targets/$(TARGET)/spe: \ diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/manifests/common/driver_partition_psa.json b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/manifests/common/driver_partition_psa.json index 5d57571c..e3cec929 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/manifests/common/driver_partition_psa.json +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/manifests/common/driver_partition_psa.json @@ -3,11 +3,9 @@ "name": "DRIVER_PARTITION", "type": "PSA-ROT", "priority": "NORMAL", - "id": "0x00000003", "description": "Implements device services such print, flash read/write,. etc.", "entry_point": "driver_main", "stack_size": "0x400", - "heap_size": "0x100", "services": [{ "name": "DRIVER_UART_SID", "sid": "0x0000FC01", @@ -33,9 +31,9 @@ "minor_policy": "RELAXED" }, { - "name": "TEST_INTR_SID", + "name": "DRIVER_TEST_SID", "sid": "0x0000FC04", - "signal": "TEST_INTR_SIG", + "signal": "DRIVER_TEST_SIG", "non_secure_clients": true, "minor_version": 1, "minor_policy": "RELAXED" @@ -69,13 +67,9 @@ ], "irqs": [ { + "description": "Using UART TX interrupt to test psa_wait and psa_eoi for irq_signal", "signal": "DRIVER_UART_INTR_SIG", - "line_num": 17 + "line_num": 37 } - ], - "linker_pattern": { - "object_list": [ - "driver_partition.a" - ] - } + ] } diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/manifests/ipc/client_partition_psa.json b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/manifests/ipc/client_partition_psa.json index 081dc95d..b93377bd 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/manifests/ipc/client_partition_psa.json +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/manifests/ipc/client_partition_psa.json @@ -3,11 +3,9 @@ "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": "0x100", "services": [{ "name": "CLIENT_TEST_DISPATCHER_SID", "sid": "0x0000FA01", @@ -20,7 +18,7 @@ "dependencies": [ "DRIVER_UART_SID", "DRIVER_NVMEM_SID", - "TEST_INTR_SID", + "DRIVER_TEST_SID", "SERVER_TEST_DISPATCHER_SID", "SERVER_UNSPECIFED_MINOR_V_SID", "SERVER_STRICT_MINOR_VERSION_SID", @@ -35,10 +33,5 @@ "size": "0x20", "permission": "READ-WRITE" } - ], - "linker_pattern": { - "object_list": [ - "client_partition.a" - ] - } + ] } diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/manifests/ipc/server_partition_psa.json b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/manifests/ipc/server_partition_psa.json index 3541387c..146b8fbc 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/manifests/ipc/server_partition_psa.json +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/manifests/ipc/server_partition_psa.json @@ -3,7 +3,6 @@ "name": "SERVER_PARTITION", "type": "APPLICATION-ROT", "priority": "NORMAL", - "id": "0x00000002", "description": "Server partition executing server test func", "entry_point": "server_main", "stack_size": "0x400", @@ -66,10 +65,5 @@ "dependencies": [ "DRIVER_UART_SID", "DRIVER_NVMEM_SID" - ], - "linker_pattern": { - "object_list": [ - "server_partition.a" - ] - } + ] } diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/common/pal_config.h b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/common/pal_config.h index daa0ec5f..e3f70ad7 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/common/pal_config.h +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/common/pal_config.h @@ -50,7 +50,7 @@ #endif #if !defined(VERBOSE) -#define VERBOSE 4 /* Print verbosity = ERROR */ +#define VERBOSE 3 /* Print verbosity = TEST */ #endif #if (!defined(VAL_NSPE_BUILD) && !defined(SPE_BUILD)) @@ -62,7 +62,15 @@ #endif #if !defined(TEST_COMBINE_ARCHIVE) -//#define TEST_COMBINE_ARCHIVE /* Test dispatcher code selection */ +#define TEST_COMBINE_ARCHIVE 0 /* Combine test archive or binary? */ +#endif + +#if !defined(WATCHDOG_AVAILABLE) +#define WATCHDOG_AVAILABLE 0 /* If zero, skip watchdog programming */ +#endif + +#if !defined(SP_HEAP_MEM_SUPP) +#define SP_HEAP_MEM_SUPP 0 /* Are Dynamic funcs available to secure partition? */ #endif /* @@ -79,6 +87,13 @@ * of this file. */ #include "psa_manifest/sid.h" + +/* + * psa_manifest/pid.h: Secure Partition IDs + * Macro definitions that map from Secure Partition names to Secure Partition IDs. + * Partition manifest parse build tool must provide the implementation of this file. +*/ +#include "psa_manifest/pid.h" #endif #if PSA_CRYPTO_IMPLEMENTED diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/common/pal_driver_ipc_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/common/pal_driver_ipc_intf.c index 32aef9c5..f8f773fb 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/common/pal_driver_ipc_intf.c +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/common/pal_driver_ipc_intf.c @@ -56,7 +56,7 @@ int pal_uart_init_ns(uint32_t uart_base_addr) @return - SUCCESS/FAILURE **/ -int pal_print_ns(char *str, uint32_t data) +int pal_print_ns(char *str, int32_t data) { int string_len = 0; char *p = str; diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/common/pal_driver_ns_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/common/pal_driver_ns_intf.c index dd6b14c7..2af6fcc7 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/common/pal_driver_ns_intf.c +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/common/pal_driver_ns_intf.c @@ -38,7 +38,7 @@ int pal_uart_init_ns(uint32_t uart_base_addr) @return - SUCCESS/FAILURE **/ -int pal_print_ns(char *str, uint32_t data) +int pal_print_ns(char *str, int32_t data) { pal_cmsdk_print(str, data); return PAL_STATUS_SUCCESS; diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/crypto/pal_crypto_config.h b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/crypto/pal_crypto_config.h index 6d71d2ec..ab11fd16 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/crypto/pal_crypto_config.h +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/crypto/pal_crypto_config.h @@ -78,10 +78,10 @@ * * Comment macros to disable the types */ -#define ARCH_TEST_DES -#define ARCH_TEST_DES_1KEY -#define ARCH_TEST_DES_2KEY -#define ARCH_TEST_DES_3KEY +//#define ARCH_TEST_DES +//#define ARCH_TEST_DES_1KEY +//#define ARCH_TEST_DES_2KEY +//#define ARCH_TEST_DES_3KEY /** * \def ARCH_TEST_RAW @@ -104,7 +104,7 @@ * * Enable the ARC4 key type. */ -#define ARCH_TEST_ARC4 +//#define ARCH_TEST_ARC4 /** * \def ARCH_TEST_CIPER_MODE_CTR @@ -227,21 +227,21 @@ * * Comment macros to disable the types */ -#define ARCH_TEST_MD2 -#define ARCH_TEST_MD4 -#define ARCH_TEST_MD5 -#define ARCH_TEST_RIPEMD160 +//#define ARCH_TEST_MD2 +//#define ARCH_TEST_MD4 +//#define ARCH_TEST_MD5 +//#define ARCH_TEST_RIPEMD160 #define ARCH_TEST_SHA1 #define ARCH_TEST_SHA224 #define ARCH_TEST_SHA256 #define ARCH_TEST_SHA384 #define ARCH_TEST_SHA512 -#define ARCH_TEST_SHA512_224 -#define ARCH_TEST_SHA512_256 -#define ARCH_TEST_SHA3_224 -#define ARCH_TEST_SHA3_256 -#define ARCH_TEST_SHA3_384 -#define ARCH_TEST_SHA3_512 +//#define ARCH_TEST_SHA512_224 +//#define ARCH_TEST_SHA512_256 +//#define ARCH_TEST_SHA3_224 +//#define ARCH_TEST_SHA3_256 +//#define ARCH_TEST_SHA3_384 +//#define ARCH_TEST_SHA3_512 /** * \def ARCH_TEST_HKDF diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/crypto/pal_crypto_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/crypto/pal_crypto_intf.c index ad838f91..3df6aa8d 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/crypto/pal_crypto_intf.c +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/crypto/pal_crypto_intf.c @@ -36,7 +36,7 @@ int32_t pal_crypto_function(int type, va_list valist) uint32_t status; const void *extra; size_t extra_size, capacity, *gen_cap, nonce_length, additional_data_length; - psa_key_handle_t handle, *key_handle; + psa_key_handle_t handle, *key_handle, target_handle; psa_key_type_t key_type, *key_type_out; psa_key_policy_t *policy; psa_key_usage_t usage, *usage_out; @@ -325,6 +325,11 @@ int32_t pal_crypto_function(int type, va_list valist) case PAL_CRYPTO_ALLOCATE_KEY: key_handle = (psa_key_handle_t *)va_arg(valist, int*); return psa_allocate_key(key_handle); + case PAL_CRYPTO_COPY_KEY: + handle = (psa_key_handle_t)va_arg(valist, int); + target_handle = (psa_key_handle_t)va_arg(valist, int); + policy = va_arg(valist, psa_key_policy_t*); + return psa_copy_key(handle, target_handle, policy); case PAL_CRYPTO_FREE: for (i = 0; i < PAL_KEY_SLOT_COUNT; i++) psa_destroy_key(i); diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/crypto/pal_crypto_intf.h b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/crypto/pal_crypto_intf.h index dfabee18..d1dabfa4 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/crypto/pal_crypto_intf.h +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/crypto/pal_crypto_intf.h @@ -68,6 +68,7 @@ enum crypto_function_code { PAL_CRYPTO_ASYMMTERIC_VERIFY = 0x31, PAL_CRYPTO_KEY_AGREEMENT = 0x32, PAL_CRYPTO_ALLOCATE_KEY = 0x33, + PAL_CRYPTO_COPY_KEY = 0x34, PAL_CRYPTO_FREE = 0xFE, }; diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_crypto.c b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_crypto.c new file mode 100644 index 00000000..ae2bdba4 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_crypto.c @@ -0,0 +1,346 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_attestation_crypto.h" + +static uint32_t public_key_registered = 0; + +static inline struct q_useful_buf_c useful_buf_head(struct q_useful_buf_c buf, + size_t amount) +{ + return UsefulBuf_Head(buf, amount); +} + +static uint32_t check_hash_sizes(void) +{ + if (T_COSE_CRYPTO_SHA256_SIZE != PSA_HASH_SIZE(PSA_ALG_SHA_256)) + { + return PAL_ATTEST_HASH_FAIL; + } + + return PAL_ATTEST_SUCCESS; +} + +static psa_ecc_curve_t attest_map_elliptic_curve_type(int32_t cose_curve) +{ + psa_ecc_curve_t psa_curve; + + /*FixMe: Mapping is not complete, missing ones: P384, P521, ED25519, ED448 */ + switch (cose_curve) + { + case P_256: + psa_curve = PSA_ECC_CURVE_SECP256R1; + break; + default: + psa_curve = USHRT_MAX; + } + + return psa_curve; +} + +static psa_algorithm_t cose_hash_alg_id_to_psa(int32_t cose_hash_alg_id) +{ + psa_algorithm_t status; + + switch (cose_hash_alg_id) + { + case COSE_ALG_SHA256_PROPRIETARY: + status = PSA_ALG_SHA_256; + break; + default: + status = PSA_ALG_MD4; + break; + } + + return status; +} + +static int32_t hash_alg_id_from_sig_alg_id(int32_t cose_sig_alg_id) +{ + switch (cose_sig_alg_id) + { + case COSE_ALGORITHM_ES256: + return COSE_ALG_SHA256_PROPRIETARY; + default: + return INT32_MAX; + } +} + +int32_t pal_cose_crypto_hash_start(struct pal_cose_crypto_hash *hash_ctx, int32_t cose_hash_alg_id) +{ + int32_t cose_ret = PAL_ATTEST_SUCCESS; + psa_status_t psa_ret; + struct pal_cose_psa_crypto_hash *psa_hash_ctx; + + cose_ret = check_hash_sizes(); + if (cose_ret) + { + goto error; + } + + if (sizeof(struct pal_cose_crypto_hash) < sizeof(struct pal_cose_psa_crypto_hash)) + { + cose_ret = PAL_ATTEST_HASH_FAIL; + goto error; + } + + psa_hash_ctx = (struct pal_cose_psa_crypto_hash *)hash_ctx; + psa_ret = psa_hash_setup(&psa_hash_ctx->operation, cose_hash_alg_id_to_psa(cose_hash_alg_id)); + + if (psa_ret == PAL_ATTEST_SUCCESS) + { + psa_hash_ctx->status = PAL_ATTEST_SUCCESS; + cose_ret = PAL_ATTEST_SUCCESS; + } + else if (psa_ret == PSA_ERROR_NOT_SUPPORTED) + { + cose_ret = PAL_ATTEST_HASH_UNSUPPORTED; + } + else + { + cose_ret = PAL_ATTEST_HASH_FAIL; + } + +error: + return cose_ret; +} + +void pal_cose_crypto_hash_update(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf_c data_to_hash) +{ + struct pal_cose_psa_crypto_hash *psa_hash_ctx; + + if (sizeof(struct pal_cose_crypto_hash) < sizeof(struct pal_cose_psa_crypto_hash)) + { + return; + } + + psa_hash_ctx = (struct pal_cose_psa_crypto_hash *)hash_ctx; + + if (psa_hash_ctx->status == PAL_ATTEST_SUCCESS) + { + if (data_to_hash.ptr != NULL) + { + psa_hash_ctx->status = psa_hash_update(&psa_hash_ctx->operation, + data_to_hash.ptr, + data_to_hash.len); + } + else + { + /* Intentionally do nothing, just computing the size of the token */ + } + } +} + +int32_t pal_cose_crypto_hash_finish(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf buffer_to_hold_result, + struct q_useful_buf_c *hash_result) +{ + uint32_t cose_ret = PAL_ATTEST_SUCCESS; + psa_status_t psa_ret; + struct pal_cose_psa_crypto_hash *psa_hash_ctx; + + if (sizeof(struct pal_cose_crypto_hash) < sizeof(struct pal_cose_psa_crypto_hash)) + { + cose_ret = PAL_ATTEST_HASH_FAIL; + goto error; + } + + psa_hash_ctx = (struct pal_cose_psa_crypto_hash *)hash_ctx; + + if (psa_hash_ctx->status == PAL_ATTEST_SUCCESS) + { + psa_ret = psa_hash_finish(&psa_hash_ctx->operation, + buffer_to_hold_result.ptr, + buffer_to_hold_result.len, + &(hash_result->len)); + + if (psa_ret == PAL_ATTEST_SUCCESS) + { + hash_result->ptr = buffer_to_hold_result.ptr; + cose_ret = 0; + } + else if (psa_ret == PSA_ERROR_BUFFER_TOO_SMALL) + { + cose_ret = PAL_ATTEST_HASH_BUFFER_SIZE; + } + else + { + cose_ret = PAL_ATTEST_HASH_FAIL; + } + } + else + { + cose_ret = PAL_ATTEST_HASH_FAIL; + } + +error: + return cose_ret; +} + +int pal_create_sha256(struct q_useful_buf_c bytes_to_hash, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash) +{ + uint32_t status = PAL_ATTEST_SUCCESS; + struct pal_cose_crypto_hash hash_ctx; + + status = pal_cose_crypto_hash_start(&hash_ctx, COSE_ALG_SHA256_PROPRIETARY); + if (status) + return status; + + pal_cose_crypto_hash_update(&hash_ctx, bytes_to_hash); + status = pal_cose_crypto_hash_finish(&hash_ctx, buffer_for_hash, hash); + + return status; +} + +uint32_t pal_compute_hash(int32_t cose_alg_id, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash, struct q_useful_buf_c protected_headers, + struct q_useful_buf_c payload) +{ + uint32_t status; + QCBOREncodeContext cbor_encode_ctx; + struct q_useful_buf_c tbs_first_part; + QCBORError qcbor_result; + struct pal_cose_crypto_hash hash_ctx = {{0}}; + int32_t hash_alg_id; + UsefulBuf_MAKE_STACK_UB (buffer_for_TBS_first_part, T_COSE_SIZE_OF_TBS); + + /* This builds the CBOR-format to-be-signed bytes */ + QCBOREncode_Init(&cbor_encode_ctx, buffer_for_TBS_first_part); + QCBOREncode_OpenArray(&cbor_encode_ctx); + /* context */ + QCBOREncode_AddSZString(&cbor_encode_ctx, + COSE_SIG_CONTEXT_STRING_SIGNATURE1); + /* body_protected */ + QCBOREncode_AddBytes(&cbor_encode_ctx, + protected_headers); + /* sign_protected */ + QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_USEFUL_BUF_C); + /* external_aad */ + QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_USEFUL_BUF_C); + /* fake payload */ + QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_USEFUL_BUF_C); + QCBOREncode_CloseArray(&cbor_encode_ctx); + + /* Get the result and convert it to struct q_useful_buf_c representation */ + qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &tbs_first_part); + if (qcbor_result) + { + /* Mainly means that the protected_headers were too big + (which should never happen) */ + status = PAL_ATTEST_ERR_SIGN_STRUCT; + goto Done; + } + + /* Start the hashing */ + hash_alg_id = hash_alg_id_from_sig_alg_id(cose_alg_id); + + /* Don't check hash_alg_id for failure. pal_cose_crypto_hash_start() + * will handle it properly + */ + status = pal_cose_crypto_hash_start(&hash_ctx, hash_alg_id); + if (status) + goto Done; + + /* Hash the first part of the TBS. Take all but the last two + * bytes. The last two bytes are the fake payload from above. It + * is replaced by the real payload which is hashed next. The fake + * payload is needed so the array count is right. This is one of + * the main things that make it possible to implement with one + * buffer for the whole cose sign1. + */ + pal_cose_crypto_hash_update(&hash_ctx, useful_buf_head(tbs_first_part, + tbs_first_part.len - 2)); + + /* Hash the payload */ + pal_cose_crypto_hash_update(&hash_ctx, payload); + + /* Finish the hash and set up to return it */ + status = pal_cose_crypto_hash_finish(&hash_ctx, + buffer_for_hash, + hash); + +Done: + return status; +} + +uint32_t pal_import_attest_key(int32_t alg) +{ + psa_key_type_t attest_key_type; + size_t public_key_size; + psa_status_t status = PSA_SUCCESS; + psa_key_policy_t policy; + psa_ecc_curve_t psa_curve; + psa_key_handle_t public_key_handle; + + /* Mapping of COSE curve type to PSA curve types */ + psa_curve = attest_map_elliptic_curve_type(P_256); + if (psa_curve == USHRT_MAX) + return PAL_ATTEST_ERROR; + + /* Setup the key policy for public key */ + policy = psa_key_policy_init(); + psa_key_policy_set_usage(&policy, PSA_KEY_USAGE_VERIFY, alg); + + status = psa_allocate_key(&public_key_handle); + if (status != PSA_SUCCESS) + return status; + + status = psa_set_key_policy(public_key_handle, &policy); + if (status != PSA_SUCCESS) + return status; + + attest_key_type = PSA_KEY_TYPE_ECC_PUBLIC_KEY(psa_curve); + + /* Register public key to crypto service */ + public_key_size = attest_key.pubx_key_size + attest_key.puby_key_size; + + status = psa_import_key(public_key_handle, + attest_key_type, + (const uint8_t *)&attest_public_key, + public_key_size + 1); + + return status; +} + + +uint32_t pal_crypto_pub_key_verify(int32_t cose_algorithm_id, + struct q_useful_buf_c token_hash, + struct q_useful_buf_c signature) +{ + uint32_t status = PAL_ATTEST_SUCCESS; + + if (!public_key_registered) + { + status = pal_import_attest_key(cose_algorithm_id); + if (status != PAL_ATTEST_SUCCESS) + return status; + + public_key_registered = 1; + } + +/* + * Enable the verify function when Trusted Firmare - M Supports + + * Verify the signature a hash or short message using a public key. + status = psa_asymmetric_verify(public_key_handle, + cose_algorithm_id, token_hash.ptr, token_hash.len, + signature.ptr, signature.len); +*/ + return status; +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_crypto.h b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_crypto.h new file mode 100644 index 00000000..2d63ad13 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_crypto.h @@ -0,0 +1,102 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_attestation_eat.h" + +#define ATTEST_PUBLIC_KEY_SLOT 4 + +typedef struct{ + uint8_t *pubx_key; + uint32_t pubx_key_size; + uint8_t *puby_key; + uint32_t puby_key_size; +} ecc_key_t; + +struct ecc_public_key_t { + const uint8_t a; + uint8_t public_key[]; /* X-coordinate || Y-coordinate */ +}; + +static const struct ecc_public_key_t attest_public_key = { + /* Constant byte */ + 0x04, + /* X-coordinate */ + {0x79, 0xEB, 0xA9, 0x0E, 0x8B, 0xF4, 0x50, 0xA6, + 0x75, 0x15, 0x76, 0xAD, 0x45, 0x99, 0xB0, 0x7A, + 0xDF, 0x93, 0x8D, 0xA3, 0xBB, 0x0B, 0xD1, 0x7D, + 0x00, 0x36, 0xED, 0x49, 0xA2, 0xD0, 0xFC, 0x3F, + /* Y-coordinate */ + 0xBF, 0xCD, 0xFA, 0x89, 0x56, 0xB5, 0x68, 0xBF, + 0xDB, 0x86, 0x73, 0xE6, 0x48, 0xD8, 0xB5, 0x8D, + 0x92, 0x99, 0x55, 0xB1, 0x4A, 0x26, 0xC3, 0x08, + 0x0F, 0x34, 0x11, 0x7D, 0x97, 0x1D, 0x68, 0x64}, +}; + +struct pal_cose_crypto_hash { + /* Can't put the actual size here without creating dependecy on + * actual hash implementation, so this is a fairly large and + * accommodating size. + */ + uint8_t bytes[128]; +}; + +struct pal_cose_psa_crypto_hash { + psa_status_t status; + psa_hash_operation_t operation; +}; + +static const uint8_t initial_attestation_public_x_key[] = +{ + 0x79, 0xEB, 0xA9, 0x0E, 0x8B, 0xF4, 0x50, 0xA6, + 0x75, 0x15, 0x76, 0xAD, 0x45, 0x99, 0xB0, 0x7A, + 0xDF, 0x93, 0x8D, 0xA3, 0xBB, 0x0B, 0xD1, 0x7D, + 0x00, 0x36, 0xED, 0x49, 0xA2, 0xD0, 0xFC, 0x3F +}; + +static const uint8_t initial_attestation_public_y_key[] = +{ + 0xBF, 0xCD, 0xFA, 0x89, 0x56, 0xB5, 0x68, 0xBF, + 0xDB, 0x86, 0x73, 0xE6, 0x48, 0xD8, 0xB5, 0x8D, + 0x92, 0x99, 0x55, 0xB1, 0x4A, 0x26, 0xC3, 0x08, + 0x0F, 0x34, 0x11, 0x7D, 0x97, 0x1D, 0x68, 0x64 +}; + +/* Initialize the structure with given public key */ +static const ecc_key_t attest_key = { + (uint8_t *)initial_attestation_public_x_key, + sizeof(initial_attestation_public_x_key), + (uint8_t *)initial_attestation_public_y_key, + sizeof(initial_attestation_public_y_key) +}; + +int32_t pal_cose_crypto_hash_start(struct pal_cose_crypto_hash *hash_ctx, int32_t cose_hash_alg_id); +void pal_cose_crypto_hash_update(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf_c data_to_hash); +int32_t pal_cose_crypto_hash_finish(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf buffer_to_hold_result, + struct q_useful_buf_c *hash_result); +int pal_create_sha256(struct q_useful_buf_c bytes_to_hash, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash); +uint32_t pal_compute_hash(int32_t cose_alg_id, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash, struct q_useful_buf_c protected_headers, + struct q_useful_buf_c payload); +uint32_t pal_import_attest_key(int32_t alg); +uint32_t pal_crypto_pub_key_verify(int32_t cose_algorithm_id, struct q_useful_buf_c token_hash, + struct q_useful_buf_c signature); + + diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_eat.c b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_eat.c index 262dc5dd..178fdc9c 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_eat.c +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_eat.c @@ -15,10 +15,56 @@ * limitations under the License. **/ -#include "pal_attestation_eat.h" +#include "pal_attestation_crypto.h" + +uint32_t mandatory_claims = 0; +uint32_t mandaroty_sw_components = 0; +bool_t sw_component_present = 0; + +static int pal_encode_cose_key(struct q_useful_buf_c *cose_key, + struct q_useful_buf buffer_for_cose_key, + struct q_useful_buf_c x_cord, struct q_useful_buf_c y_cord) +{ + uint32_t return_value; + QCBORError qcbor_result; + QCBOREncodeContext cbor_encode_ctx; + int32_t cose_curve_id = P_256; + struct q_useful_buf_c encoded_key_id; + + /* Get the public key x and y */ + /* Encode it into a COSE_Key structure */ + QCBOREncode_Init(&cbor_encode_ctx, buffer_for_cose_key); + QCBOREncode_OpenMap(&cbor_encode_ctx); + QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx, + COSE_KEY_COMMON_KTY, + COSE_KEY_TYPE_EC2); + QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx, + COSE_KEY_PARAM_CRV, + cose_curve_id); + QCBOREncode_AddBytesToMapN(&cbor_encode_ctx, + COSE_KEY_PARAM_X_COORDINATE, + x_cord); + QCBOREncode_AddBytesToMapN(&cbor_encode_ctx, + COSE_KEY_PARAM_Y_COORDINATE, + y_cord); + QCBOREncode_CloseMap(&cbor_encode_ctx); + + qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &encoded_key_id); + if (qcbor_result != QCBOR_SUCCESS) + { + /* Mainly means that the COSE_Key was too big for buffer_for_cose_key */ + return_value = PAL_ATTEST_ERR_PROTECTED_HEADERS; + goto Done; + } + + /* Finish up and return */ + *cose_key = encoded_key_id; + return_value = PAL_ATTEST_SUCCESS; + +Done: + return return_value; +} -uint32_t mandatory_claims = 0, mandaroty_sw_components = 0; -bool_t sw_component_present = 0; static int get_items_in_map(QCBORDecodeContext *decode_context, struct items_to_get_t *item_list) @@ -90,7 +136,7 @@ static int get_item_in_map(QCBORDecodeContext *decode_context, } static int parse_unprotected_headers(QCBORDecodeContext *decode_context, - struct useful_buf_c *child, + struct q_useful_buf_c *child, bool *loop_back) { struct items_to_get_t item_list[3]; @@ -120,7 +166,7 @@ static int parse_unprotected_headers(QCBORDecodeContext *decode_context, return PAL_ATTEST_SUCCESS; } -static int parse_protected_headers(struct useful_buf_c protected_headers, +static int parse_protected_headers(struct q_useful_buf_c protected_headers, int32_t *alg_id) { QCBORDecodeContext decode_context; @@ -156,7 +202,7 @@ static int parse_protected_headers(struct useful_buf_c protected_headers, @return - error status **/ static int parse_claims(QCBORDecodeContext *decode_context, QCBORItem item, - struct useful_buf_c completed_challenge) + struct q_useful_buf_c completed_challenge) { int i, count = 0; int status = PAL_ATTEST_SUCCESS; @@ -281,16 +327,42 @@ static int parse_claims(QCBORDecodeContext *decode_context, QCBORItem item, int32_t pal_initial_attest_verify_token(uint8_t *challenge, uint32_t challenge_size, uint8_t *token, uint32_t token_size) { - int status = PAL_ATTEST_SUCCESS; + int32_t status = PAL_ATTEST_SUCCESS; bool short_circuit; int32_t cose_algorithm_id; QCBORItem item; QCBORDecodeContext decode_context; - struct useful_buf_c completed_challenge; - struct useful_buf_c completed_token; - struct useful_buf_c payload; - struct useful_buf_c protected_headers; - struct useful_buf_c kid; + struct q_useful_buf_c completed_challenge; + struct q_useful_buf_c completed_token; + struct q_useful_buf_c payload; + struct q_useful_buf_c signature; + struct q_useful_buf_c protected_headers; + struct q_useful_buf_c kid; + struct q_useful_buf_c x_cord; + struct q_useful_buf_c y_cord; + struct q_useful_buf_c cose_key_to_hash; + struct q_useful_buf_c key_hash; + struct q_useful_buf_c token_hash; + USEFUL_BUF_MAKE_STACK_UB(buf_to_hold_x_coord, T_COSE_CRYPTO_EC_P256_COORD_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buf_to_hold_y_coord, T_COSE_CRYPTO_EC_P256_COORD_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_kid, T_COSE_CRYPTO_SHA256_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_cose_key, MAX_ENCODED_COSE_KEY_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_encoded_key, MAX_ENCODED_COSE_KEY_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_token_hash, T_COSE_CRYPTO_SHA256_SIZE); + + kid.ptr = buffer_for_encoded_key.ptr; + + memcpy(buf_to_hold_x_coord.ptr, (const void *)attest_key.pubx_key, attest_key.pubx_key_size); + memcpy(buf_to_hold_y_coord.ptr, (const void *)attest_key.puby_key, attest_key.puby_key_size); + + /* Update size */ + buf_to_hold_x_coord.len = attest_key.pubx_key_size; + buf_to_hold_y_coord.len = attest_key.puby_key_size; + + x_cord.ptr = buf_to_hold_x_coord.ptr; + x_cord.len = buf_to_hold_x_coord.len; + y_cord.ptr = buf_to_hold_y_coord.ptr; + y_cord.len = buf_to_hold_y_coord.len; /* Construct the token buffer for validation */ completed_token.ptr = token; @@ -345,6 +417,27 @@ int32_t pal_initial_attest_verify_token(uint8_t *challenge, uint32_t challenge_s if (status != PAL_ATTEST_SUCCESS) return status; + /* Encode the given public key */ + status = pal_encode_cose_key(&cose_key_to_hash, buffer_for_cose_key, x_cord, y_cord); + if (status != PAL_ATTEST_SUCCESS) + return status; + + /* Create hash of the given public key */ + status = pal_create_sha256(cose_key_to_hash, buffer_for_kid, &key_hash); + if (status != PSA_SUCCESS) + return status; + + /* Compare the hash of the public key in token and hash of the given public key */ + if (kid.len != key_hash.len) + { + return PAL_ATTEST_HASH_LENGTH_MISMATCH; + } + + if (memcmp(kid.ptr, key_hash.ptr, kid.len) != 0) + { + return PAL_ATTEST_HASH_MISMATCH; + } + /* Get the payload */ QCBORDecode_GetNext(&decode_context, &item); if (item.uDataType != QCBOR_TYPE_BYTE_STRING) @@ -357,6 +450,19 @@ int32_t pal_initial_attest_verify_token(uint8_t *challenge, uint32_t challenge_s if (item.uDataType != QCBOR_TYPE_BYTE_STRING) return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + signature = item.val.string; + + /* Compute the hash from the token */ + status = pal_compute_hash(cose_algorithm_id, buffer_for_token_hash, &token_hash, + protected_headers, payload); + if (status != PAL_ATTEST_SUCCESS) + return status; + + /* Verify the signature */ + status = pal_crypto_pub_key_verify(cose_algorithm_id, token_hash, signature); + if (status != PAL_ATTEST_SUCCESS) + return status; + /* Initialize the Decoder and validate the payload format */ QCBORDecode_Init(&decode_context, payload, QCBOR_DECODE_MODE_NORMAL); status = QCBORDecode_GetNext(&decode_context, &item); diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_eat.h b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_eat.h index 9f435fb3..8a0c5455 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_eat.h +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_eat.h @@ -17,16 +17,70 @@ #include "qcbor.h" #include "pal_common.h" +#include "psa/crypto.h" #define PAL_ATTEST_MIN_ERROR 30 +/* NIST P-256 also known as secp256r1 */ +#define P_256 1 + #define COSE_HEADER_PARAM_ALG 1 #define COSE_HEADER_PARAM_KID 4 -#define MANDATORY_CLAIM_WITH_SW_COMP 862 -#define MANDATORY_CLAIM_NO_SW_COMP 926 -#define MANDATORY_SW_COMP 36 -#define CBOR_ARM_TOTAL_CLAIM_INSTANCE 10 +#define COSE_KEY_COMMON_KTY 1 +#define COSE_KEY_TYPE_EC2 2 +#define COSE_KEY_PARAM_CRV -1 +#define COSE_KEY_PARAM_X_COORDINATE -2 +#define COSE_KEY_PARAM_Y_COORDINATE -3 +#define COSE_ALGORITHM_ES256 -7 +#define COSE_ALG_SHA256_PROPRIETARY -72000 + +/** + * The size of X and Y coordinate in 2 parameter style EC public + * key. Format is as defined in [COSE (RFC 8152)] + * (https://tools.ietf.org/html/rfc8152) and [SEC 1: Elliptic Curve + * Cryptography](http://www.secg.org/sec1-v2.pdf). + * + * This size is well-known and documented in public standards. + */ +#define T_COSE_CRYPTO_EC_P256_COORD_SIZE 32 +#define T_COSE_CRYPTO_SHA256_SIZE 32 + +#define MAX_ENCODED_COSE_KEY_SIZE \ + 1 + /* 1 byte to encode map */ \ + 2 + /* 2 bytes to encode key type */ \ + 2 + /* 2 bytes to encode curve */ \ + 2 * /* the X and Y coordinates at 32 bytes each */ \ + (T_COSE_CRYPTO_EC_P256_COORD_SIZE + 1 + 2) +#define USEFUL_BUF_MAKE_STACK_UB UsefulBuf_MAKE_STACK_UB + +#define COSE_SIG_CONTEXT_STRING_SIGNATURE1 "Signature1" + +/* Private value. Intentionally not documented for Doxygen. + * This is the size allocated for the encoded protected headers. It + * needs to be big enough for make_protected_header() to succeed. It + * currently sized for one header with an algorithm ID up to 32 bits + * long -- one byte for the wrapping map, one byte for the label, 5 + * bytes for the ID. If this is made accidentially too small, QCBOR will + * only return an error, and not overrun any buffers. + * + * 9 extra bytes are added, rounding it up to 16 total, in case some + * other protected header is to be added. + */ +#define T_COSE_SIGN1_MAX_PROT_HEADER (1+1+5+9) + +/** + * This is the size of the first part of the CBOR encoded TBS + * bytes. It is around 20 bytes. See create_tbs_hash(). + */ +#define T_COSE_SIZE_OF_TBS \ + 1 + /* For opening the array */ \ + sizeof(COSE_SIG_CONTEXT_STRING_SIGNATURE1) + /* "Signature1" */ \ + 2 + /* Overhead for encoding string */ \ + T_COSE_SIGN1_MAX_PROT_HEADER + /* entire protected headers */ \ + 3 * ( /* 3 NULL bstrs for fields not used */ \ + 1 /* size of a NULL bstr */ \ + ) /* CBOR Label for proprietary header indicating short-circuit @@ -47,6 +101,8 @@ #define EAT_CBOR_ARM_LABEL_UEID (EAT_CBOR_ARM_RANGE_BASE - 9) #define EAT_CBOR_ARM_LABEL_ORIGINATION (EAT_CBOR_ARM_RANGE_BASE - 10) +#define CBOR_ARM_TOTAL_CLAIM_INSTANCE 10 + #define EAT_CBOR_SW_COMPONENT_TYPE (1u) #define EAT_CBOR_SW_COMPONENT_MEASUREMENT (2u) #define EAT_CBOR_SW_COMPONENT_EPOCH (3u) @@ -54,6 +110,40 @@ #define EAT_CBOR_SW_COMPONENT_SIGNER_ID (5u) #define EAT_CBOR_SW_COMPONENT_MEASUREMENT_DESC (6u) +#define MANDATORY_CLAIM_WITH_SW_COMP (1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_NONCE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_UEID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_CLIENT_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_BOOT_SEED) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_SW_COMPONENTS)) + +#define MANDATORY_CLAIM_NO_SW_COMP (1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_NONCE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_UEID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_CLIENT_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_BOOT_SEED) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_NO_SW_COMPONENTS)) + +#define MANDATORY_SW_COMP (1 << EAT_CBOR_SW_COMPONENT_MEASUREMENT | \ + 1 << EAT_CBOR_SW_COMPONENT_SIGNER_ID) + +#define NULL_USEFUL_BUF_C NULLUsefulBufC enum attestation_error_code { PAL_ATTEST_SUCCESS = 0, @@ -61,6 +151,13 @@ enum attestation_error_code { PAL_ATTEST_TOKEN_CHALLENGE_MISMATCH, PAL_ATTEST_TOKEN_NOT_SUPPORTED, PAL_ATTEST_TOKEN_NOT_ALL_MANDATORY_CLAIMS, + PAL_ATTEST_HASH_LENGTH_MISMATCH, + PAL_ATTEST_HASH_MISMATCH, + PAL_ATTEST_HASH_FAIL, + PAL_ATTEST_HASH_UNSUPPORTED, + PAL_ATTEST_HASH_BUFFER_SIZE, + PAL_ATTEST_ERR_PROTECTED_HEADERS, + PAL_ATTEST_ERR_SIGN_STRUCT, PAL_ATTEST_ERROR, }; diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_intf.h b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_intf.h index ef132b5b..12f6ee94 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_intf.h +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/nspe/initial_attestation/pal_attestation_intf.h @@ -18,7 +18,7 @@ #ifndef _PAL_INITIAL_ATTESTATION_H_ #define _PAL_INITIAL_ATTESTATION_H_ -#include "pal_attestation_eat.h" +#include "pal_attestation_crypto.h" enum attestation_function_code { PAL_INITIAL_ATTEST_GET_TOKEN = 0x1, diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/spe/pal_driver_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/spe/pal_driver_intf.c index cc8b5373..fd307839 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/spe/pal_driver_intf.c +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/spe/pal_driver_intf.c @@ -33,7 +33,7 @@ void pal_uart_init(uint32_t uart_base_addr) - data : Value for format specifier **/ -void pal_print(char *str, uint32_t data) +void pal_print(char *str, int32_t data) { pal_cmsdk_print(str,data); @@ -110,3 +110,23 @@ int pal_wd_timer_is_enabled(addr_t base_addr) return (pal_wd_cmsdk_is_enabled(base_addr)); } +/** + @brief - Trigger interrupt for irq signal assigned to driver partition + before return to caller. + @param - void + @return - void +**/ +void pal_generate_interrupt(void) +{ + pal_uart_cmsdk_generate_irq(); +} + +/** + @brief - Disable interrupt that was generated using pal_generate_interrupt API. + @param - void + @return - void +**/ +void pal_disable_interrupt(void) +{ + pal_uart_cmsdk_disable_irq(); +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/spe/pal_driver_intf.h b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/spe/pal_driver_intf.h index da85a63e..cef34ca8 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_an521/spe/pal_driver_intf.h +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_an521/spe/pal_driver_intf.h @@ -23,11 +23,13 @@ #include "pal_wd_cmsdk.h" void pal_uart_init(uint32_t uart_base_addr); -void pal_print(char *str, uint32_t data); +void pal_print(char *str, int32_t data); int pal_nvmem_write(addr_t base, uint32_t offset, void *buffer, int size); int pal_nvmem_read(addr_t base, uint32_t offset, void *buffer, int size); int pal_wd_timer_init(addr_t base_addr, uint32_t time_us, uint32_t timer_tick_us); int pal_wd_timer_enable(addr_t base_addr); int pal_wd_timer_disable(addr_t base_addr); int pal_wd_timer_is_enabled(addr_t base_addr); +void pal_generate_interrupt(void); +void pal_disable_interrupt(void); #endif /* _PAL_DRIVER_INTF_H_ */ diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/Makefile b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/Makefile index 00829ae7..aa7e10b7 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/Makefile +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/Makefile @@ -83,6 +83,7 @@ endif ifeq (${PSA_INITIAL_ATTESTATION_IMPLEMENTED},1) SRC_C_NSPE += pal_attestation_intf.c SRC_C_NSPE += pal_attestation_eat.c +SRC_C_NSPE += pal_attestation_crypto.c else SRC_C_NSPE += pal_attestation_empty_intf.c endif @@ -98,7 +99,7 @@ INCLUDE= -I$(SOURCE)/platform/targets/$(TARGET)/nspe \ -I$(SOURCE)/platform/targets/$(TARGET)/nspe/initial_attestation \ -I$(SOURCE)/platform/targets/$(TARGET)/nspe/initial_attestation/ext/inc \ -I$(SOURCE)/platform/targets/$(TARGET)/nspe/internal_trusted_storage \ - -I$(SOURCE)/platform/targets/$(TARGET)/nspe/protected_storage + -I$(SOURCE)/platform/targets/$(TARGET)/nspe/protected_storage \ VPATH=$(SOURCE)/platform/targets/$(TARGET)/: \ $(SOURCE)/platform/targets/$(TARGET)/spe: \ diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/manifests/common/driver_partition_psa.json b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/manifests/common/driver_partition_psa.json index 5d57571c..7010c9b5 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/manifests/common/driver_partition_psa.json +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/manifests/common/driver_partition_psa.json @@ -3,11 +3,9 @@ "name": "DRIVER_PARTITION", "type": "PSA-ROT", "priority": "NORMAL", - "id": "0x00000003", "description": "Implements device services such print, flash read/write,. etc.", "entry_point": "driver_main", "stack_size": "0x400", - "heap_size": "0x100", "services": [{ "name": "DRIVER_UART_SID", "sid": "0x0000FC01", @@ -33,9 +31,9 @@ "minor_policy": "RELAXED" }, { - "name": "TEST_INTR_SID", + "name": "DRIVER_TEST_SID", "sid": "0x0000FC04", - "signal": "TEST_INTR_SIG", + "signal": "DRIVER_TEST_SIG", "non_secure_clients": true, "minor_version": 1, "minor_policy": "RELAXED" @@ -69,13 +67,9 @@ ], "irqs": [ { + "description": "Using UART TX interrupt to test psa_wait and psa_eoi for irq_signal", "signal": "DRIVER_UART_INTR_SIG", - "line_num": 17 + "line_num": 46 } - ], - "linker_pattern": { - "object_list": [ - "driver_partition.a" - ] - } + ] } diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/manifests/ipc/client_partition_psa.json b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/manifests/ipc/client_partition_psa.json index 081dc95d..b93377bd 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/manifests/ipc/client_partition_psa.json +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/manifests/ipc/client_partition_psa.json @@ -3,11 +3,9 @@ "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": "0x100", "services": [{ "name": "CLIENT_TEST_DISPATCHER_SID", "sid": "0x0000FA01", @@ -20,7 +18,7 @@ "dependencies": [ "DRIVER_UART_SID", "DRIVER_NVMEM_SID", - "TEST_INTR_SID", + "DRIVER_TEST_SID", "SERVER_TEST_DISPATCHER_SID", "SERVER_UNSPECIFED_MINOR_V_SID", "SERVER_STRICT_MINOR_VERSION_SID", @@ -35,10 +33,5 @@ "size": "0x20", "permission": "READ-WRITE" } - ], - "linker_pattern": { - "object_list": [ - "client_partition.a" - ] - } + ] } diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/manifests/ipc/server_partition_psa.json b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/manifests/ipc/server_partition_psa.json index 3541387c..146b8fbc 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/manifests/ipc/server_partition_psa.json +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/manifests/ipc/server_partition_psa.json @@ -3,7 +3,6 @@ "name": "SERVER_PARTITION", "type": "APPLICATION-ROT", "priority": "NORMAL", - "id": "0x00000002", "description": "Server partition executing server test func", "entry_point": "server_main", "stack_size": "0x400", @@ -66,10 +65,5 @@ "dependencies": [ "DRIVER_UART_SID", "DRIVER_NVMEM_SID" - ], - "linker_pattern": { - "object_list": [ - "server_partition.a" - ] - } + ] } diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/common/pal_config.h b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/common/pal_config.h index daa0ec5f..e3f70ad7 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/common/pal_config.h +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/common/pal_config.h @@ -50,7 +50,7 @@ #endif #if !defined(VERBOSE) -#define VERBOSE 4 /* Print verbosity = ERROR */ +#define VERBOSE 3 /* Print verbosity = TEST */ #endif #if (!defined(VAL_NSPE_BUILD) && !defined(SPE_BUILD)) @@ -62,7 +62,15 @@ #endif #if !defined(TEST_COMBINE_ARCHIVE) -//#define TEST_COMBINE_ARCHIVE /* Test dispatcher code selection */ +#define TEST_COMBINE_ARCHIVE 0 /* Combine test archive or binary? */ +#endif + +#if !defined(WATCHDOG_AVAILABLE) +#define WATCHDOG_AVAILABLE 0 /* If zero, skip watchdog programming */ +#endif + +#if !defined(SP_HEAP_MEM_SUPP) +#define SP_HEAP_MEM_SUPP 0 /* Are Dynamic funcs available to secure partition? */ #endif /* @@ -79,6 +87,13 @@ * of this file. */ #include "psa_manifest/sid.h" + +/* + * psa_manifest/pid.h: Secure Partition IDs + * Macro definitions that map from Secure Partition names to Secure Partition IDs. + * Partition manifest parse build tool must provide the implementation of this file. +*/ +#include "psa_manifest/pid.h" #endif #if PSA_CRYPTO_IMPLEMENTED diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/common/pal_driver_ipc_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/common/pal_driver_ipc_intf.c index 32aef9c5..f8f773fb 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/common/pal_driver_ipc_intf.c +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/common/pal_driver_ipc_intf.c @@ -56,7 +56,7 @@ int pal_uart_init_ns(uint32_t uart_base_addr) @return - SUCCESS/FAILURE **/ -int pal_print_ns(char *str, uint32_t data) +int pal_print_ns(char *str, int32_t data) { int string_len = 0; char *p = str; diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/common/pal_driver_ns_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/common/pal_driver_ns_intf.c index 5985f00c..338df6cb 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/common/pal_driver_ns_intf.c +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/common/pal_driver_ns_intf.c @@ -38,7 +38,7 @@ int pal_uart_init_ns(uint32_t uart_base_addr) @return - SUCCESS/FAILURE **/ -int pal_print_ns(char *str, uint32_t data) +int pal_print_ns(char *str, int32_t data) { pal_uart_pl011_print(str, data); return PAL_STATUS_SUCCESS; diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/crypto/pal_crypto_config.h b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/crypto/pal_crypto_config.h index 6d71d2ec..ab11fd16 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/crypto/pal_crypto_config.h +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/crypto/pal_crypto_config.h @@ -78,10 +78,10 @@ * * Comment macros to disable the types */ -#define ARCH_TEST_DES -#define ARCH_TEST_DES_1KEY -#define ARCH_TEST_DES_2KEY -#define ARCH_TEST_DES_3KEY +//#define ARCH_TEST_DES +//#define ARCH_TEST_DES_1KEY +//#define ARCH_TEST_DES_2KEY +//#define ARCH_TEST_DES_3KEY /** * \def ARCH_TEST_RAW @@ -104,7 +104,7 @@ * * Enable the ARC4 key type. */ -#define ARCH_TEST_ARC4 +//#define ARCH_TEST_ARC4 /** * \def ARCH_TEST_CIPER_MODE_CTR @@ -227,21 +227,21 @@ * * Comment macros to disable the types */ -#define ARCH_TEST_MD2 -#define ARCH_TEST_MD4 -#define ARCH_TEST_MD5 -#define ARCH_TEST_RIPEMD160 +//#define ARCH_TEST_MD2 +//#define ARCH_TEST_MD4 +//#define ARCH_TEST_MD5 +//#define ARCH_TEST_RIPEMD160 #define ARCH_TEST_SHA1 #define ARCH_TEST_SHA224 #define ARCH_TEST_SHA256 #define ARCH_TEST_SHA384 #define ARCH_TEST_SHA512 -#define ARCH_TEST_SHA512_224 -#define ARCH_TEST_SHA512_256 -#define ARCH_TEST_SHA3_224 -#define ARCH_TEST_SHA3_256 -#define ARCH_TEST_SHA3_384 -#define ARCH_TEST_SHA3_512 +//#define ARCH_TEST_SHA512_224 +//#define ARCH_TEST_SHA512_256 +//#define ARCH_TEST_SHA3_224 +//#define ARCH_TEST_SHA3_256 +//#define ARCH_TEST_SHA3_384 +//#define ARCH_TEST_SHA3_512 /** * \def ARCH_TEST_HKDF diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/crypto/pal_crypto_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/crypto/pal_crypto_intf.c index ad838f91..3df6aa8d 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/crypto/pal_crypto_intf.c +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/crypto/pal_crypto_intf.c @@ -36,7 +36,7 @@ int32_t pal_crypto_function(int type, va_list valist) uint32_t status; const void *extra; size_t extra_size, capacity, *gen_cap, nonce_length, additional_data_length; - psa_key_handle_t handle, *key_handle; + psa_key_handle_t handle, *key_handle, target_handle; psa_key_type_t key_type, *key_type_out; psa_key_policy_t *policy; psa_key_usage_t usage, *usage_out; @@ -325,6 +325,11 @@ int32_t pal_crypto_function(int type, va_list valist) case PAL_CRYPTO_ALLOCATE_KEY: key_handle = (psa_key_handle_t *)va_arg(valist, int*); return psa_allocate_key(key_handle); + case PAL_CRYPTO_COPY_KEY: + handle = (psa_key_handle_t)va_arg(valist, int); + target_handle = (psa_key_handle_t)va_arg(valist, int); + policy = va_arg(valist, psa_key_policy_t*); + return psa_copy_key(handle, target_handle, policy); case PAL_CRYPTO_FREE: for (i = 0; i < PAL_KEY_SLOT_COUNT; i++) psa_destroy_key(i); diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/crypto/pal_crypto_intf.h b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/crypto/pal_crypto_intf.h index dfabee18..d1dabfa4 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/crypto/pal_crypto_intf.h +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/crypto/pal_crypto_intf.h @@ -68,6 +68,7 @@ enum crypto_function_code { PAL_CRYPTO_ASYMMTERIC_VERIFY = 0x31, PAL_CRYPTO_KEY_AGREEMENT = 0x32, PAL_CRYPTO_ALLOCATE_KEY = 0x33, + PAL_CRYPTO_COPY_KEY = 0x34, PAL_CRYPTO_FREE = 0xFE, }; diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_crypto.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_crypto.c new file mode 100644 index 00000000..ae2bdba4 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_crypto.c @@ -0,0 +1,346 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_attestation_crypto.h" + +static uint32_t public_key_registered = 0; + +static inline struct q_useful_buf_c useful_buf_head(struct q_useful_buf_c buf, + size_t amount) +{ + return UsefulBuf_Head(buf, amount); +} + +static uint32_t check_hash_sizes(void) +{ + if (T_COSE_CRYPTO_SHA256_SIZE != PSA_HASH_SIZE(PSA_ALG_SHA_256)) + { + return PAL_ATTEST_HASH_FAIL; + } + + return PAL_ATTEST_SUCCESS; +} + +static psa_ecc_curve_t attest_map_elliptic_curve_type(int32_t cose_curve) +{ + psa_ecc_curve_t psa_curve; + + /*FixMe: Mapping is not complete, missing ones: P384, P521, ED25519, ED448 */ + switch (cose_curve) + { + case P_256: + psa_curve = PSA_ECC_CURVE_SECP256R1; + break; + default: + psa_curve = USHRT_MAX; + } + + return psa_curve; +} + +static psa_algorithm_t cose_hash_alg_id_to_psa(int32_t cose_hash_alg_id) +{ + psa_algorithm_t status; + + switch (cose_hash_alg_id) + { + case COSE_ALG_SHA256_PROPRIETARY: + status = PSA_ALG_SHA_256; + break; + default: + status = PSA_ALG_MD4; + break; + } + + return status; +} + +static int32_t hash_alg_id_from_sig_alg_id(int32_t cose_sig_alg_id) +{ + switch (cose_sig_alg_id) + { + case COSE_ALGORITHM_ES256: + return COSE_ALG_SHA256_PROPRIETARY; + default: + return INT32_MAX; + } +} + +int32_t pal_cose_crypto_hash_start(struct pal_cose_crypto_hash *hash_ctx, int32_t cose_hash_alg_id) +{ + int32_t cose_ret = PAL_ATTEST_SUCCESS; + psa_status_t psa_ret; + struct pal_cose_psa_crypto_hash *psa_hash_ctx; + + cose_ret = check_hash_sizes(); + if (cose_ret) + { + goto error; + } + + if (sizeof(struct pal_cose_crypto_hash) < sizeof(struct pal_cose_psa_crypto_hash)) + { + cose_ret = PAL_ATTEST_HASH_FAIL; + goto error; + } + + psa_hash_ctx = (struct pal_cose_psa_crypto_hash *)hash_ctx; + psa_ret = psa_hash_setup(&psa_hash_ctx->operation, cose_hash_alg_id_to_psa(cose_hash_alg_id)); + + if (psa_ret == PAL_ATTEST_SUCCESS) + { + psa_hash_ctx->status = PAL_ATTEST_SUCCESS; + cose_ret = PAL_ATTEST_SUCCESS; + } + else if (psa_ret == PSA_ERROR_NOT_SUPPORTED) + { + cose_ret = PAL_ATTEST_HASH_UNSUPPORTED; + } + else + { + cose_ret = PAL_ATTEST_HASH_FAIL; + } + +error: + return cose_ret; +} + +void pal_cose_crypto_hash_update(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf_c data_to_hash) +{ + struct pal_cose_psa_crypto_hash *psa_hash_ctx; + + if (sizeof(struct pal_cose_crypto_hash) < sizeof(struct pal_cose_psa_crypto_hash)) + { + return; + } + + psa_hash_ctx = (struct pal_cose_psa_crypto_hash *)hash_ctx; + + if (psa_hash_ctx->status == PAL_ATTEST_SUCCESS) + { + if (data_to_hash.ptr != NULL) + { + psa_hash_ctx->status = psa_hash_update(&psa_hash_ctx->operation, + data_to_hash.ptr, + data_to_hash.len); + } + else + { + /* Intentionally do nothing, just computing the size of the token */ + } + } +} + +int32_t pal_cose_crypto_hash_finish(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf buffer_to_hold_result, + struct q_useful_buf_c *hash_result) +{ + uint32_t cose_ret = PAL_ATTEST_SUCCESS; + psa_status_t psa_ret; + struct pal_cose_psa_crypto_hash *psa_hash_ctx; + + if (sizeof(struct pal_cose_crypto_hash) < sizeof(struct pal_cose_psa_crypto_hash)) + { + cose_ret = PAL_ATTEST_HASH_FAIL; + goto error; + } + + psa_hash_ctx = (struct pal_cose_psa_crypto_hash *)hash_ctx; + + if (psa_hash_ctx->status == PAL_ATTEST_SUCCESS) + { + psa_ret = psa_hash_finish(&psa_hash_ctx->operation, + buffer_to_hold_result.ptr, + buffer_to_hold_result.len, + &(hash_result->len)); + + if (psa_ret == PAL_ATTEST_SUCCESS) + { + hash_result->ptr = buffer_to_hold_result.ptr; + cose_ret = 0; + } + else if (psa_ret == PSA_ERROR_BUFFER_TOO_SMALL) + { + cose_ret = PAL_ATTEST_HASH_BUFFER_SIZE; + } + else + { + cose_ret = PAL_ATTEST_HASH_FAIL; + } + } + else + { + cose_ret = PAL_ATTEST_HASH_FAIL; + } + +error: + return cose_ret; +} + +int pal_create_sha256(struct q_useful_buf_c bytes_to_hash, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash) +{ + uint32_t status = PAL_ATTEST_SUCCESS; + struct pal_cose_crypto_hash hash_ctx; + + status = pal_cose_crypto_hash_start(&hash_ctx, COSE_ALG_SHA256_PROPRIETARY); + if (status) + return status; + + pal_cose_crypto_hash_update(&hash_ctx, bytes_to_hash); + status = pal_cose_crypto_hash_finish(&hash_ctx, buffer_for_hash, hash); + + return status; +} + +uint32_t pal_compute_hash(int32_t cose_alg_id, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash, struct q_useful_buf_c protected_headers, + struct q_useful_buf_c payload) +{ + uint32_t status; + QCBOREncodeContext cbor_encode_ctx; + struct q_useful_buf_c tbs_first_part; + QCBORError qcbor_result; + struct pal_cose_crypto_hash hash_ctx = {{0}}; + int32_t hash_alg_id; + UsefulBuf_MAKE_STACK_UB (buffer_for_TBS_first_part, T_COSE_SIZE_OF_TBS); + + /* This builds the CBOR-format to-be-signed bytes */ + QCBOREncode_Init(&cbor_encode_ctx, buffer_for_TBS_first_part); + QCBOREncode_OpenArray(&cbor_encode_ctx); + /* context */ + QCBOREncode_AddSZString(&cbor_encode_ctx, + COSE_SIG_CONTEXT_STRING_SIGNATURE1); + /* body_protected */ + QCBOREncode_AddBytes(&cbor_encode_ctx, + protected_headers); + /* sign_protected */ + QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_USEFUL_BUF_C); + /* external_aad */ + QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_USEFUL_BUF_C); + /* fake payload */ + QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_USEFUL_BUF_C); + QCBOREncode_CloseArray(&cbor_encode_ctx); + + /* Get the result and convert it to struct q_useful_buf_c representation */ + qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &tbs_first_part); + if (qcbor_result) + { + /* Mainly means that the protected_headers were too big + (which should never happen) */ + status = PAL_ATTEST_ERR_SIGN_STRUCT; + goto Done; + } + + /* Start the hashing */ + hash_alg_id = hash_alg_id_from_sig_alg_id(cose_alg_id); + + /* Don't check hash_alg_id for failure. pal_cose_crypto_hash_start() + * will handle it properly + */ + status = pal_cose_crypto_hash_start(&hash_ctx, hash_alg_id); + if (status) + goto Done; + + /* Hash the first part of the TBS. Take all but the last two + * bytes. The last two bytes are the fake payload from above. It + * is replaced by the real payload which is hashed next. The fake + * payload is needed so the array count is right. This is one of + * the main things that make it possible to implement with one + * buffer for the whole cose sign1. + */ + pal_cose_crypto_hash_update(&hash_ctx, useful_buf_head(tbs_first_part, + tbs_first_part.len - 2)); + + /* Hash the payload */ + pal_cose_crypto_hash_update(&hash_ctx, payload); + + /* Finish the hash and set up to return it */ + status = pal_cose_crypto_hash_finish(&hash_ctx, + buffer_for_hash, + hash); + +Done: + return status; +} + +uint32_t pal_import_attest_key(int32_t alg) +{ + psa_key_type_t attest_key_type; + size_t public_key_size; + psa_status_t status = PSA_SUCCESS; + psa_key_policy_t policy; + psa_ecc_curve_t psa_curve; + psa_key_handle_t public_key_handle; + + /* Mapping of COSE curve type to PSA curve types */ + psa_curve = attest_map_elliptic_curve_type(P_256); + if (psa_curve == USHRT_MAX) + return PAL_ATTEST_ERROR; + + /* Setup the key policy for public key */ + policy = psa_key_policy_init(); + psa_key_policy_set_usage(&policy, PSA_KEY_USAGE_VERIFY, alg); + + status = psa_allocate_key(&public_key_handle); + if (status != PSA_SUCCESS) + return status; + + status = psa_set_key_policy(public_key_handle, &policy); + if (status != PSA_SUCCESS) + return status; + + attest_key_type = PSA_KEY_TYPE_ECC_PUBLIC_KEY(psa_curve); + + /* Register public key to crypto service */ + public_key_size = attest_key.pubx_key_size + attest_key.puby_key_size; + + status = psa_import_key(public_key_handle, + attest_key_type, + (const uint8_t *)&attest_public_key, + public_key_size + 1); + + return status; +} + + +uint32_t pal_crypto_pub_key_verify(int32_t cose_algorithm_id, + struct q_useful_buf_c token_hash, + struct q_useful_buf_c signature) +{ + uint32_t status = PAL_ATTEST_SUCCESS; + + if (!public_key_registered) + { + status = pal_import_attest_key(cose_algorithm_id); + if (status != PAL_ATTEST_SUCCESS) + return status; + + public_key_registered = 1; + } + +/* + * Enable the verify function when Trusted Firmare - M Supports + + * Verify the signature a hash or short message using a public key. + status = psa_asymmetric_verify(public_key_handle, + cose_algorithm_id, token_hash.ptr, token_hash.len, + signature.ptr, signature.len); +*/ + return status; +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_crypto.h b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_crypto.h new file mode 100644 index 00000000..2d63ad13 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_crypto.h @@ -0,0 +1,102 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_attestation_eat.h" + +#define ATTEST_PUBLIC_KEY_SLOT 4 + +typedef struct{ + uint8_t *pubx_key; + uint32_t pubx_key_size; + uint8_t *puby_key; + uint32_t puby_key_size; +} ecc_key_t; + +struct ecc_public_key_t { + const uint8_t a; + uint8_t public_key[]; /* X-coordinate || Y-coordinate */ +}; + +static const struct ecc_public_key_t attest_public_key = { + /* Constant byte */ + 0x04, + /* X-coordinate */ + {0x79, 0xEB, 0xA9, 0x0E, 0x8B, 0xF4, 0x50, 0xA6, + 0x75, 0x15, 0x76, 0xAD, 0x45, 0x99, 0xB0, 0x7A, + 0xDF, 0x93, 0x8D, 0xA3, 0xBB, 0x0B, 0xD1, 0x7D, + 0x00, 0x36, 0xED, 0x49, 0xA2, 0xD0, 0xFC, 0x3F, + /* Y-coordinate */ + 0xBF, 0xCD, 0xFA, 0x89, 0x56, 0xB5, 0x68, 0xBF, + 0xDB, 0x86, 0x73, 0xE6, 0x48, 0xD8, 0xB5, 0x8D, + 0x92, 0x99, 0x55, 0xB1, 0x4A, 0x26, 0xC3, 0x08, + 0x0F, 0x34, 0x11, 0x7D, 0x97, 0x1D, 0x68, 0x64}, +}; + +struct pal_cose_crypto_hash { + /* Can't put the actual size here without creating dependecy on + * actual hash implementation, so this is a fairly large and + * accommodating size. + */ + uint8_t bytes[128]; +}; + +struct pal_cose_psa_crypto_hash { + psa_status_t status; + psa_hash_operation_t operation; +}; + +static const uint8_t initial_attestation_public_x_key[] = +{ + 0x79, 0xEB, 0xA9, 0x0E, 0x8B, 0xF4, 0x50, 0xA6, + 0x75, 0x15, 0x76, 0xAD, 0x45, 0x99, 0xB0, 0x7A, + 0xDF, 0x93, 0x8D, 0xA3, 0xBB, 0x0B, 0xD1, 0x7D, + 0x00, 0x36, 0xED, 0x49, 0xA2, 0xD0, 0xFC, 0x3F +}; + +static const uint8_t initial_attestation_public_y_key[] = +{ + 0xBF, 0xCD, 0xFA, 0x89, 0x56, 0xB5, 0x68, 0xBF, + 0xDB, 0x86, 0x73, 0xE6, 0x48, 0xD8, 0xB5, 0x8D, + 0x92, 0x99, 0x55, 0xB1, 0x4A, 0x26, 0xC3, 0x08, + 0x0F, 0x34, 0x11, 0x7D, 0x97, 0x1D, 0x68, 0x64 +}; + +/* Initialize the structure with given public key */ +static const ecc_key_t attest_key = { + (uint8_t *)initial_attestation_public_x_key, + sizeof(initial_attestation_public_x_key), + (uint8_t *)initial_attestation_public_y_key, + sizeof(initial_attestation_public_y_key) +}; + +int32_t pal_cose_crypto_hash_start(struct pal_cose_crypto_hash *hash_ctx, int32_t cose_hash_alg_id); +void pal_cose_crypto_hash_update(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf_c data_to_hash); +int32_t pal_cose_crypto_hash_finish(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf buffer_to_hold_result, + struct q_useful_buf_c *hash_result); +int pal_create_sha256(struct q_useful_buf_c bytes_to_hash, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash); +uint32_t pal_compute_hash(int32_t cose_alg_id, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash, struct q_useful_buf_c protected_headers, + struct q_useful_buf_c payload); +uint32_t pal_import_attest_key(int32_t alg); +uint32_t pal_crypto_pub_key_verify(int32_t cose_algorithm_id, struct q_useful_buf_c token_hash, + struct q_useful_buf_c signature); + + diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_eat.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_eat.c index 262dc5dd..178fdc9c 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_eat.c +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_eat.c @@ -15,10 +15,56 @@ * limitations under the License. **/ -#include "pal_attestation_eat.h" +#include "pal_attestation_crypto.h" + +uint32_t mandatory_claims = 0; +uint32_t mandaroty_sw_components = 0; +bool_t sw_component_present = 0; + +static int pal_encode_cose_key(struct q_useful_buf_c *cose_key, + struct q_useful_buf buffer_for_cose_key, + struct q_useful_buf_c x_cord, struct q_useful_buf_c y_cord) +{ + uint32_t return_value; + QCBORError qcbor_result; + QCBOREncodeContext cbor_encode_ctx; + int32_t cose_curve_id = P_256; + struct q_useful_buf_c encoded_key_id; + + /* Get the public key x and y */ + /* Encode it into a COSE_Key structure */ + QCBOREncode_Init(&cbor_encode_ctx, buffer_for_cose_key); + QCBOREncode_OpenMap(&cbor_encode_ctx); + QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx, + COSE_KEY_COMMON_KTY, + COSE_KEY_TYPE_EC2); + QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx, + COSE_KEY_PARAM_CRV, + cose_curve_id); + QCBOREncode_AddBytesToMapN(&cbor_encode_ctx, + COSE_KEY_PARAM_X_COORDINATE, + x_cord); + QCBOREncode_AddBytesToMapN(&cbor_encode_ctx, + COSE_KEY_PARAM_Y_COORDINATE, + y_cord); + QCBOREncode_CloseMap(&cbor_encode_ctx); + + qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &encoded_key_id); + if (qcbor_result != QCBOR_SUCCESS) + { + /* Mainly means that the COSE_Key was too big for buffer_for_cose_key */ + return_value = PAL_ATTEST_ERR_PROTECTED_HEADERS; + goto Done; + } + + /* Finish up and return */ + *cose_key = encoded_key_id; + return_value = PAL_ATTEST_SUCCESS; + +Done: + return return_value; +} -uint32_t mandatory_claims = 0, mandaroty_sw_components = 0; -bool_t sw_component_present = 0; static int get_items_in_map(QCBORDecodeContext *decode_context, struct items_to_get_t *item_list) @@ -90,7 +136,7 @@ static int get_item_in_map(QCBORDecodeContext *decode_context, } static int parse_unprotected_headers(QCBORDecodeContext *decode_context, - struct useful_buf_c *child, + struct q_useful_buf_c *child, bool *loop_back) { struct items_to_get_t item_list[3]; @@ -120,7 +166,7 @@ static int parse_unprotected_headers(QCBORDecodeContext *decode_context, return PAL_ATTEST_SUCCESS; } -static int parse_protected_headers(struct useful_buf_c protected_headers, +static int parse_protected_headers(struct q_useful_buf_c protected_headers, int32_t *alg_id) { QCBORDecodeContext decode_context; @@ -156,7 +202,7 @@ static int parse_protected_headers(struct useful_buf_c protected_headers, @return - error status **/ static int parse_claims(QCBORDecodeContext *decode_context, QCBORItem item, - struct useful_buf_c completed_challenge) + struct q_useful_buf_c completed_challenge) { int i, count = 0; int status = PAL_ATTEST_SUCCESS; @@ -281,16 +327,42 @@ static int parse_claims(QCBORDecodeContext *decode_context, QCBORItem item, int32_t pal_initial_attest_verify_token(uint8_t *challenge, uint32_t challenge_size, uint8_t *token, uint32_t token_size) { - int status = PAL_ATTEST_SUCCESS; + int32_t status = PAL_ATTEST_SUCCESS; bool short_circuit; int32_t cose_algorithm_id; QCBORItem item; QCBORDecodeContext decode_context; - struct useful_buf_c completed_challenge; - struct useful_buf_c completed_token; - struct useful_buf_c payload; - struct useful_buf_c protected_headers; - struct useful_buf_c kid; + struct q_useful_buf_c completed_challenge; + struct q_useful_buf_c completed_token; + struct q_useful_buf_c payload; + struct q_useful_buf_c signature; + struct q_useful_buf_c protected_headers; + struct q_useful_buf_c kid; + struct q_useful_buf_c x_cord; + struct q_useful_buf_c y_cord; + struct q_useful_buf_c cose_key_to_hash; + struct q_useful_buf_c key_hash; + struct q_useful_buf_c token_hash; + USEFUL_BUF_MAKE_STACK_UB(buf_to_hold_x_coord, T_COSE_CRYPTO_EC_P256_COORD_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buf_to_hold_y_coord, T_COSE_CRYPTO_EC_P256_COORD_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_kid, T_COSE_CRYPTO_SHA256_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_cose_key, MAX_ENCODED_COSE_KEY_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_encoded_key, MAX_ENCODED_COSE_KEY_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_token_hash, T_COSE_CRYPTO_SHA256_SIZE); + + kid.ptr = buffer_for_encoded_key.ptr; + + memcpy(buf_to_hold_x_coord.ptr, (const void *)attest_key.pubx_key, attest_key.pubx_key_size); + memcpy(buf_to_hold_y_coord.ptr, (const void *)attest_key.puby_key, attest_key.puby_key_size); + + /* Update size */ + buf_to_hold_x_coord.len = attest_key.pubx_key_size; + buf_to_hold_y_coord.len = attest_key.puby_key_size; + + x_cord.ptr = buf_to_hold_x_coord.ptr; + x_cord.len = buf_to_hold_x_coord.len; + y_cord.ptr = buf_to_hold_y_coord.ptr; + y_cord.len = buf_to_hold_y_coord.len; /* Construct the token buffer for validation */ completed_token.ptr = token; @@ -345,6 +417,27 @@ int32_t pal_initial_attest_verify_token(uint8_t *challenge, uint32_t challenge_s if (status != PAL_ATTEST_SUCCESS) return status; + /* Encode the given public key */ + status = pal_encode_cose_key(&cose_key_to_hash, buffer_for_cose_key, x_cord, y_cord); + if (status != PAL_ATTEST_SUCCESS) + return status; + + /* Create hash of the given public key */ + status = pal_create_sha256(cose_key_to_hash, buffer_for_kid, &key_hash); + if (status != PSA_SUCCESS) + return status; + + /* Compare the hash of the public key in token and hash of the given public key */ + if (kid.len != key_hash.len) + { + return PAL_ATTEST_HASH_LENGTH_MISMATCH; + } + + if (memcmp(kid.ptr, key_hash.ptr, kid.len) != 0) + { + return PAL_ATTEST_HASH_MISMATCH; + } + /* Get the payload */ QCBORDecode_GetNext(&decode_context, &item); if (item.uDataType != QCBOR_TYPE_BYTE_STRING) @@ -357,6 +450,19 @@ int32_t pal_initial_attest_verify_token(uint8_t *challenge, uint32_t challenge_s if (item.uDataType != QCBOR_TYPE_BYTE_STRING) return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + signature = item.val.string; + + /* Compute the hash from the token */ + status = pal_compute_hash(cose_algorithm_id, buffer_for_token_hash, &token_hash, + protected_headers, payload); + if (status != PAL_ATTEST_SUCCESS) + return status; + + /* Verify the signature */ + status = pal_crypto_pub_key_verify(cose_algorithm_id, token_hash, signature); + if (status != PAL_ATTEST_SUCCESS) + return status; + /* Initialize the Decoder and validate the payload format */ QCBORDecode_Init(&decode_context, payload, QCBOR_DECODE_MODE_NORMAL); status = QCBORDecode_GetNext(&decode_context, &item); diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_eat.h b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_eat.h index 9f435fb3..8a0c5455 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_eat.h +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_eat.h @@ -17,16 +17,70 @@ #include "qcbor.h" #include "pal_common.h" +#include "psa/crypto.h" #define PAL_ATTEST_MIN_ERROR 30 +/* NIST P-256 also known as secp256r1 */ +#define P_256 1 + #define COSE_HEADER_PARAM_ALG 1 #define COSE_HEADER_PARAM_KID 4 -#define MANDATORY_CLAIM_WITH_SW_COMP 862 -#define MANDATORY_CLAIM_NO_SW_COMP 926 -#define MANDATORY_SW_COMP 36 -#define CBOR_ARM_TOTAL_CLAIM_INSTANCE 10 +#define COSE_KEY_COMMON_KTY 1 +#define COSE_KEY_TYPE_EC2 2 +#define COSE_KEY_PARAM_CRV -1 +#define COSE_KEY_PARAM_X_COORDINATE -2 +#define COSE_KEY_PARAM_Y_COORDINATE -3 +#define COSE_ALGORITHM_ES256 -7 +#define COSE_ALG_SHA256_PROPRIETARY -72000 + +/** + * The size of X and Y coordinate in 2 parameter style EC public + * key. Format is as defined in [COSE (RFC 8152)] + * (https://tools.ietf.org/html/rfc8152) and [SEC 1: Elliptic Curve + * Cryptography](http://www.secg.org/sec1-v2.pdf). + * + * This size is well-known and documented in public standards. + */ +#define T_COSE_CRYPTO_EC_P256_COORD_SIZE 32 +#define T_COSE_CRYPTO_SHA256_SIZE 32 + +#define MAX_ENCODED_COSE_KEY_SIZE \ + 1 + /* 1 byte to encode map */ \ + 2 + /* 2 bytes to encode key type */ \ + 2 + /* 2 bytes to encode curve */ \ + 2 * /* the X and Y coordinates at 32 bytes each */ \ + (T_COSE_CRYPTO_EC_P256_COORD_SIZE + 1 + 2) +#define USEFUL_BUF_MAKE_STACK_UB UsefulBuf_MAKE_STACK_UB + +#define COSE_SIG_CONTEXT_STRING_SIGNATURE1 "Signature1" + +/* Private value. Intentionally not documented for Doxygen. + * This is the size allocated for the encoded protected headers. It + * needs to be big enough for make_protected_header() to succeed. It + * currently sized for one header with an algorithm ID up to 32 bits + * long -- one byte for the wrapping map, one byte for the label, 5 + * bytes for the ID. If this is made accidentially too small, QCBOR will + * only return an error, and not overrun any buffers. + * + * 9 extra bytes are added, rounding it up to 16 total, in case some + * other protected header is to be added. + */ +#define T_COSE_SIGN1_MAX_PROT_HEADER (1+1+5+9) + +/** + * This is the size of the first part of the CBOR encoded TBS + * bytes. It is around 20 bytes. See create_tbs_hash(). + */ +#define T_COSE_SIZE_OF_TBS \ + 1 + /* For opening the array */ \ + sizeof(COSE_SIG_CONTEXT_STRING_SIGNATURE1) + /* "Signature1" */ \ + 2 + /* Overhead for encoding string */ \ + T_COSE_SIGN1_MAX_PROT_HEADER + /* entire protected headers */ \ + 3 * ( /* 3 NULL bstrs for fields not used */ \ + 1 /* size of a NULL bstr */ \ + ) /* CBOR Label for proprietary header indicating short-circuit @@ -47,6 +101,8 @@ #define EAT_CBOR_ARM_LABEL_UEID (EAT_CBOR_ARM_RANGE_BASE - 9) #define EAT_CBOR_ARM_LABEL_ORIGINATION (EAT_CBOR_ARM_RANGE_BASE - 10) +#define CBOR_ARM_TOTAL_CLAIM_INSTANCE 10 + #define EAT_CBOR_SW_COMPONENT_TYPE (1u) #define EAT_CBOR_SW_COMPONENT_MEASUREMENT (2u) #define EAT_CBOR_SW_COMPONENT_EPOCH (3u) @@ -54,6 +110,40 @@ #define EAT_CBOR_SW_COMPONENT_SIGNER_ID (5u) #define EAT_CBOR_SW_COMPONENT_MEASUREMENT_DESC (6u) +#define MANDATORY_CLAIM_WITH_SW_COMP (1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_NONCE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_UEID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_CLIENT_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_BOOT_SEED) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_SW_COMPONENTS)) + +#define MANDATORY_CLAIM_NO_SW_COMP (1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_NONCE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_UEID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_CLIENT_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_BOOT_SEED) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_NO_SW_COMPONENTS)) + +#define MANDATORY_SW_COMP (1 << EAT_CBOR_SW_COMPONENT_MEASUREMENT | \ + 1 << EAT_CBOR_SW_COMPONENT_SIGNER_ID) + +#define NULL_USEFUL_BUF_C NULLUsefulBufC enum attestation_error_code { PAL_ATTEST_SUCCESS = 0, @@ -61,6 +151,13 @@ enum attestation_error_code { PAL_ATTEST_TOKEN_CHALLENGE_MISMATCH, PAL_ATTEST_TOKEN_NOT_SUPPORTED, PAL_ATTEST_TOKEN_NOT_ALL_MANDATORY_CLAIMS, + PAL_ATTEST_HASH_LENGTH_MISMATCH, + PAL_ATTEST_HASH_MISMATCH, + PAL_ATTEST_HASH_FAIL, + PAL_ATTEST_HASH_UNSUPPORTED, + PAL_ATTEST_HASH_BUFFER_SIZE, + PAL_ATTEST_ERR_PROTECTED_HEADERS, + PAL_ATTEST_ERR_SIGN_STRUCT, PAL_ATTEST_ERROR, }; diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_intf.h b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_intf.h index ef132b5b..12f6ee94 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_intf.h +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/nspe/initial_attestation/pal_attestation_intf.h @@ -18,7 +18,7 @@ #ifndef _PAL_INITIAL_ATTESTATION_H_ #define _PAL_INITIAL_ATTESTATION_H_ -#include "pal_attestation_eat.h" +#include "pal_attestation_crypto.h" enum attestation_function_code { PAL_INITIAL_ATTEST_GET_TOKEN = 0x1, diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/reference_logs/attestation.txt b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/reference_logs/attestation.txt new file mode 100755 index 00000000..1e347a30 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/reference_logs/attestation.txt @@ -0,0 +1,36 @@ +***** PSA Architecture Test Suite - Version 0.9 ***** + +Running.. Attestation Suite +****************************************** + +TEST: 801 | DESCRIPTION: Testing initial attestation APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_initial_attestation_get_token with Challenge 32 +[Check 2] Test psa_initial_attestation_get_token with Challenge 48 +[Check 3] Test psa_initial_attestation_get_token with Challenge 64 +[Check 4] Test psa_initial_attestation_get_token with zero challenge size +[Check 5] Test psa_initial_attestation_get_token with small challenge size +[Check 6] Test psa_initial_attestation_get_token with invalid challenge size +[Check 7] Test psa_initial_attestation_get_token with large challenge size +[Check 8] Test psa_initial_attestation_get_token with zero as token size +[Check 9] Test psa_initial_attestation_get_token with small token size +[Check 10] Test psa_initial_attestation_get_token_size with Challenge 32 +[Check 11] Test psa_initial_attestation_get_token_size with Challenge 48 +[Check 12] Test psa_initial_attestation_get_token_size with Challenge 64 +[Check 13] Test psa_initial_attestation_get_token_size with zero challenge size +[Check 14] Test psa_initial_attestation_get_token_size with small challenge size +[Check 15] Test psa_initial_attestation_get_token_size with invalid challenge size +[Check 16] Test psa_initial_attestation_get_token_size with large challenge size +TEST RESULT: PASSED + +****************************************** + +************ Attestation Suite Report ********** +TOTAL TESTS : 1 +TOTAL PASSED : 1 +TOTAL SIM ERROR : 0 +TOTAL FAILED : 0 +TOTAL SKIPPED : 0 +****************************************** + +Entering standby.. diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/reference_logs/crypto.txt b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/reference_logs/crypto.txt new file mode 100644 index 00000000..a61bd671 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/reference_logs/crypto.txt @@ -0,0 +1,766 @@ +***** PSA Architecture Test Suite - Version 0.9 ***** + +Running.. Crypto Suite +****************************************** + +TEST: 201 | DESCRIPTION: Testing psa_crypto_init API: Basic +[Info] Executing tests from non-secure +[Check 1] Test calling crypto functions before psa_crypto_init +[Check 2] Test psa_crypto_init +[Check 3] Test multiple psa_crypto_init +TEST RESULT: PASSED + +****************************************** + +TEST: 202 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_import_key 16 Byte AES +[Check 2] Test psa_import_key 24 Byte AES +[Check 3] Test psa_import_key 32 Byte AES +[Check 4] Test psa_import_key 2048 RSA public key +[Check 5] Test psa_import_key with RSA 2048 keypair +[Check 6] Test psa_import_key with DES 64 bit key +[Check 7] Test psa_import_key with Triple DES 2-Key +[Check 8] Test psa_import_key with Triple DES 3-Key +[Check 9] Test psa_import_key with EC Public key +[Check 10] Test psa_import_key with EC keypair +[Check 11] Test psa_import_key with key data greater than the algorithm size +[Check 12] Test psa_import_key with incorrect key data size +[Check 13] Test psa_import_key with incorrect key type +[Check 14] Test psa_import_key with already occupied key slot +[Check 15] Test psa_import_key with zero as key handle +[Check 16] Test psa_import_key with destroyed handle +[Check 17] Test psa_import_key with unallocated key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 203 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_export_key 16 Byte AES +[Check 2] Test psa_export_key 24 Byte AES +[Check 3] Test psa_export_key 32 Byte AES +[Check 4] Test psa_export_key 2048 RSA public key +[Check 5] Test psa_export_key with RSA 2048 keypair +[Check 6] Test psa_export_key with DES 64 bit key +[Check 7] Test psa_export_key with Triple DES 2-Key +[Check 8] Test psa_export_key with Triple DES 3-Key +[Check 9] Test psa_export_key with EC Public key +[Check 10] Test psa_export_key with EC keypair +[Check 11] Test psa_export_key with key policy verify +[Check 12] Test psa_export_key with less buffer size +[Check 13] Test psa_export_key with unallocated key handle +[Check 14] Test psa_export_key with empty key handle +[Check 15] Test psa_export_key with zero as key handle +[Check 16] Test psa_export_key with destroyed key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 204 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_export_public_key 16 Byte AES +[Check 2] Test psa_export_public_key 24 Byte AES +[Check 3] Test psa_export_public_key 32 Byte AES +[Check 4] Test psa_export_public_key 2048 RSA public key +[Check 5] Test psa_export_public_key with RSA 2048 keypair +[Check 6] Test psa_export_public_key with DES 64 bit key +[Check 7] Test psa_export_public_key with Triple DES 2-Key +[Check 8] Test psa_export_public_key with Triple DES 3-Key +[Check 9] Test psa_export_public_key with EC Public key +[Check 10] Test psa_export_public_key with EC keypair +[Check 11] Test psa_export_public_key with less buffer size +[Check 12] Test psa_export_key with unallocated key handle +[Check 13] Test psa_export_key with empty key handle +[Check 14] Test psa_export_key with zero as key handle +[Check 15] Test psa_export_key with destroyed key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 205 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_destroy_key 16 Byte AES +[Check 2] Test psa_destroy_key 24 Byte AES +[Check 3] Test psa_destroy_key 32 Byte AES +[Check 4] Test psa_destroy_key 2048 RSA public key +[Check 5] Test psa_destroy_key with RSA 2048 keypair +[Check 6] Test psa_destroy_key with DES 64 bit key +[Check 7] Test psa_destroy_key with Triple DES 2-Key +[Check 8] Test psa_destroy_key with Triple DES 3-Key +[Check 9] Test psa_destroy_key with EC Public key +[Check 10] Test psa_destroy_key with EC keypair +[Check 11] Test psa_destroy_key with unallocated key handle +[Check 12] Test psa_destroy_key with zero as key handle +[Check 13] Test psa_destroy_key with empty key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 206 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_get_key_information 16 Byte AES +[Check 2] Test psa_get_key_information 24 Byte AES +[Check 3] Test psa_get_key_information 32 Byte AES +[Check 4] Test psa_get_key_information 2048 RSA public key +[Check 5] Test psa_get_key_information with RSA 2048 keypair +[Check 6] Test psa_get_key_information with DES 64 bit key +[Check 7] Test psa_get_key_information with Triple DES 2-Key +[Check 8] Test psa_get_key_information with Triple DES 3-Key +[Check 9] Test psa_get_key_information with EC Public key +[Check 10] Test psa_get_key_information with EC keypair +[Check 11] Test psa_get_key_information with unallocated key handle +[Check 12] Test psa_get_key_information with zero as key handle +[Check 13] Test psa_get_key_information with empty key handle +[Check 14] Test psa_get_key_information with destroyed key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 207 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_set_key_policy 16 Byte AES +[Check 2] Test psa_set_key_policy 24 Byte AES +[Check 3] Test psa_set_key_policy 32 Byte AES +[Check 4] Test psa_set_key_policy 2048 RSA public key +[Check 5] Test psa_set_key_policy with RSA 2048 keypair +[Check 6] Test psa_set_key_policy with DES 64 bit key +[Check 7] Test psa_set_key_policy with Triple DES 2-Key +[Check 8] Test psa_set_key_policy with Triple DES 3-Key +[Check 9] Test psa_set_key_policy with EC Public key +[Check 10] Test psa_set_key_policy with EC keypair +[Check 11] Test psa_set_key_policy with invalid usage +[Check 12] Test psa_set_key_policy with unallocated key handle +[Check 13] Test psa_set_key_policy with zero as key handle +[Check 14] Test psa_set_key_policy with already occupied handle +TEST RESULT: PASSED + +****************************************** + +TEST: 208 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_get_key_policy 16 Byte AES +[Check 2] Test psa_get_key_policy 24 Byte AES +[Check 3] Test psa_get_key_policy 32 Byte AES +[Check 4] Test psa_get_key_policy 2048 RSA public key +[Check 5] Test psa_get_key_policy with RSA 2048 keypair +[Check 6] Test psa_get_key_policy with DES 64 bit key +[Check 7] Test psa_get_key_policy with Triple DES 2-Key +[Check 8] Test psa_get_key_policy with Triple DES 3-Key +[Check 9] Test psa_get_key_policy with EC Public key +[Check 10] Test psa_get_key_policy with EC keypair +[Check 11] Test psa_get_key_policy negative cases +[Check 12] Test psa_get_key_policy with unallocated key handle +[Check 13] Test psa_get_key_policy with zero as key handle +[Check 14] Test psa_get_key_policy with empty key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 209 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_allocate_key 16 Byte AES +[Check 2] Test psa_allocate_key 24 Byte AES +[Check 3] Test psa_allocate_key 32 Byte AES +[Check 4] Test psa_allocate_key 2048 RSA public key +[Check 5] Test psa_allocate_key with RSA 2048 keypair +[Check 6] Test psa_allocate_key with DES 64 bit key +[Check 7] Test psa_allocate_key with Triple DES 2-Key +[Check 8] Test psa_allocate_key with Triple DES 3-Key +[Check 9] Test psa_allocate_key with EC Public key +[Check 10] Test psa_allocate_key with EC keypair +[Check 11] Testing the insufficient memory +TEST RESULT: PASSED + +****************************************** + +TEST: 210 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_get_key_lifetime 16 Byte AES +[Check 2] Test psa_get_key_lifetime 2048 RSA public key +[Check 3] Test psa_get_key_lifetime with Triple DES 2-Key +[Check 4] Test psa_get_key_lifetime with EC Public key +[Check 5] Test psa_get_key_lifetime with EC keypair +[Check 6] Test psa_get_key_lifetime with invalid key handle +[Check 7] Test psa_get_key_lifetime with zero as key handle +[Check 8] Test psa_get_key_lifetime with empty key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 211 | DESCRIPTION: Testing crypto hash functions APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_hash_setup with MD2 algorithm +[Check 2] Test psa_hash_setup with MD4 algorithm +[Check 3] Test psa_hash_setup with MD5 algorithm +[Check 4] Test psa_hash_setup with RIPEMD160 algorithm +[Check 5] Test psa_hash_setup with SHA1 algorithm +[Check 6] Test psa_hash_setup with SHA224 algorithm +[Check 7] Test psa_hash_setup with SHA256 algorithm +[Check 8] Test psa_hash_setup with SHA384 algorithm +[Check 9] Test psa_hash_setup with SHA512 algorithm +TEST RESULT: PASSED + +****************************************** + +TEST: 212 | DESCRIPTION: Testing crypto hash functions APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_hash_update with MD2 algorithm +[Check 2] Test psa_hash_update with MD4 algorithm +[Check 3] Test psa_hash_update with MD5 algorithm +[Check 4] Test psa_hash_update with RIPEMD160 algorithm +[Check 5] Test psa_hash_update with SHA1 algorithm +[Check 6] Test psa_hash_update with SHA224 algorithm +[Check 7] Test psa_hash_update with SHA256 algorithm +[Check 8] Test psa_hash_update with SHA384 algorithm +[Check 9] Test psa_hash_update with SHA512 algorithm +[Check 10] Test psa_hash_update without hash setup +[Check 11] Test psa_hash_update with completed opertaion handle +TEST RESULT: PASSED + +****************************************** + +TEST: 213 | DESCRIPTION: Testing crypto hash functions APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_hash_verify with MD2 algorithm +[Check 2] Test psa_hash_verify with MD4 algorithm +[Check 3] Test psa_hash_verify with MD5 algorithm +[Check 4] Test psa_hash_verify with RIPEMD160 algorithm +[Check 5] Test psa_hash_verify with SHA1 algorithm +[Check 6] Test psa_hash_verify with SHA224 algorithm +[Check 7] Test psa_hash_verify with SHA256 algorithm +[Check 8] Test psa_hash_verify with SHA384 algorithm +[Check 9] Test psa_hash_verify with SHA512 algorithm +[Check 10] Test psa_hash_verify with incorrect expected hash +[Check 11] Test psa_hash_verify with incorrect hash length +[Check 12] test psa_hash_verify with inactive & invalid operation handle +TEST RESULT: PASSED + +****************************************** + +TEST: 214 | DESCRIPTION: Testing crypto hash functions APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_hash_finish with MD2 algorithm +[Check 2] Test psa_hash_finish with MD4 algorithm +[Check 3] Test psa_hash_finish with MD5 algorithm +[Check 4] Test psa_hash_finish with RIPEMD160 algorithm +[Check 5] Test psa_hash_finish with SHA1 algorithm +[Check 6] Test psa_hash_finish with SHA224 algorithm +[Check 7] Test psa_hash_finish with SHA256 algorithm +[Check 8] Test psa_hash_finish with SHA384 algorithm +[Check 9] Test psa_hash_finish with SHA512 algorithm +[Check 10] test psa_hash_finish with inactive operation handle +[Check 11] test psa_hash_finish with invalid hash buffer size +TEST RESULT: PASSED + +****************************************** + +TEST: 215 | DESCRIPTION: Testing crypto hash functions APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_hash_abort with MD2 algorithm +[Check 2] Test psa_hash_abort with MD4 algorithm +[Check 3] Test psa_hash_abort with MD5 algorithm +[Check 4] Test psa_hash_abort with RIPEMD160 algorithm +[Check 5] Test psa_hash_abort with SHA1 algorithm +[Check 6] Test psa_hash_abort with SHA224 algorithm +[Check 7] Test psa_hash_abort with SHA256 algorithm +[Check 8] Test psa_hash_abort with SHA384 algorithm +[Check 9] Test psa_hash_abort with SHA512 algorithm +[Check 10] Test psa_hash_finish after calling psa_hash_abort +TEST RESULT: PASSED + +****************************************** + +TEST: 216 | DESCRIPTION: Testing crypto generator functions APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_generate_key 16 Byte AES +[Check 2] Test psa_generate_key 24 Byte AES +[Check 3] Test psa_generate_key 32 Byte AES +[Check 4] Test psa_generate_key with DES 64 bit key +[Check 5] Test psa_generate_key with Triple DES 2-Key +[Check 6] Test psa_generate_key with Triple DES 3-Key +[Check 7] Test psa_generate_key with Null extra and Non-Zero extra size +[Check 8] Test psa_generate_key with ECC KeyPair +[Check 9] Test psa_generate_key with RSA 2048 Public key +[Check 10] Test psa_generate_key with unallocated key handle +[Check 11] Test psa_generate_key with zero as key handle +[Check 12] Test psa_generate_key with pre-occupied key handle +[Check 13] Test psa_generate_key with destroyed key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 217 | DESCRIPTION: Testing crypto generator functions APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_generate_random to get 0 Byte data +[Check 2] Test psa_generate_random to get 16 Byte data +[Check 3] Test psa_generate_random to get 24 Byte data +[Check 4] Test psa_generate_random to get 32 Byte data +[Check 5] Test psa_generate_random to get 64 Byte data +[Check 6] Test psa_generate_random to get 128 Byte data +[Check 7] Test psa_generate_random to get 256 Byte data +[Check 8] Test psa_generate_random to get 512 Byte data +[Check 9] Test psa_generate_random to get 1000 Byte data +TEST RESULT: PASSED + +****************************************** + +TEST: 218 | DESCRIPTION: Testing crypto generator functions APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_generator_read to get 16 Byte data with SHA-256 +[Check 2] Test psa_generator_read to get 32 Byte data with SHA-512 +[Check 3] Test psa_generator_read to get 8 Byte data with SHA-1 +[Check 4] Test psa_generator_read to request maximum capacity +[Check 5] Test psa_generator_read to request maximum capacity +1 +[Check 6] Test psa_generator_read without setup +TEST RESULT: PASSED + +****************************************** + +TEST: 219 | DESCRIPTION: Testing crypto generator functions APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_generator_get_capacity to get 16 Byte data with SHA-256 +[Check 2] Test psa_generator_get_capacity to get 32 Byte data with SHA-512 +[Check 3] Test psa_get_generator_capacity without setup +TEST RESULT: PASSED + +****************************************** + +TEST: 220 | DESCRIPTION: Testing crypto generator functions APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_generator_import_key for 16 Byte AES Key +[Check 2] Test psa_generator_import_key for Triple DES 3-Key +[Check 3] Test psa_generator_import_key output greater than capacity +[Check 4] Test psa_generator_import_key for RSA Public Key - Invalid type +[Check 5] Test psa_generator_import_key for invalid byte for generation +[Check 6] Test psa_generator_import_key with invalid handle +[Check 7] Test psa_generator_import_key with zero handle +[Check 8] Test psa_generator_import_key with pre-occupied key slot +TEST RESULT: PASSED + +****************************************** + +TEST: 221 | DESCRIPTION: Testing crypto generator functions APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_generator_abort on empty generator +[Check 2] Test psa_generator_abort +[Check 3] Multiple psa_generator_abort test +TEST RESULT: PASSED + +****************************************** + +TEST: 222 | DESCRIPTION: Testing crypto generator functions APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_key_derivation to get 16 Byte data with SHA-256 +[Check 2] Test psa_key_derivation to get 32 Byte data with SHA-512 +[Check 3] Test psa_key_derivation to get 32 Byte data with MD-5 +[Check 4] Test psa_key_derivation to get 16 Byte data with salt and label +[Check 5] Test psa_key_derivation with too large capacity for alg and key +[Check 6] Test psa_key_derivation with unsupported key type +[Check 7] Test psa_key_derivation with incorrect usage +[Check 8] Test psa_key_derivation with unsupported key derivation algorithm +[Check 9] Test psa_key_derivation with invalid algorithm +[Check 10] Test psa_key_derivation with Invalid key handle +[Check 11] Test psa_key_derivation with Zero as key handle +[Check 12] Test psa_key_derivation with Empty key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 223 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_key_policy_get_usage with usage as encrypt +[Check 2] Test psa_key_policy_get_usage with usage as decrypt +[Check 3] Test psa_key_policy_get_usage with usage as derive +[Check 4] Test psa_key_policy_get_usage with usage as export +[Check 5] Test psa_key_policy_get_usage with usage as sign +[Check 6] Test psa_key_policy_get_usage with usage as verify +TEST RESULT: PASSED + +****************************************** + +TEST: 224 | DESCRIPTION: Testing crypto AEAD APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_aead_encrypt - CCM - 16B AES - 13B nounce & 8B addi data +[Check 2] Test psa_aead_encrypt - AES-CCM +[Check 3] Test psa_aead_encrypt - AES-CCM 24 bytes Tag length = 4 +[Check 4] Test psa_aead_encrypt - GCM - 16B AES - 12B Nounce & 12B addi data +[Check 5] Test psa_aead_encrypt - DES Key +[Check 6] Test psa_aead_encrypt - Unsupported Algorithm +[Check 7] Test psa_aead_encrypt - Invalid key usage +[Check 8] Test psa_aead_encrypt - Small output buffer size +[Check 9] Test psa_aead_encrypt - Invalid key handle +[Check 10] Test psa_aead_encrypt - Zero as key handle +[Check 11] Test psa_aead_encrypt - Empty key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 225 | DESCRIPTION: Testing crypto AEAD APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_aead_decrypt - CCM - 16B AES - 13B nounce & 8B addi data +[Check 2] Test psa_aead_encrypt - AES-CCM +[Check 3] Test psa_aead_encrypt - AES-CCM 24 bytes Tag length = 4 +[Check 4] Test psa_aead_decrypt - GCM - 16B AES - 12B Nounce & 12B addi data +[Check 5] Test psa_aead_decrypt - DES Key +[Check 6] Test psa_aead_decrypt - Unsupported Algorithm +[Check 7] Test psa_aead_decrypt - Invalid key usage +[Check 8] Test psa_aead_decrypt - Small output buffer size +[Check 9] Test psa_aead_decrypt - Invalid cipher text +[Check 10] Test psa_aead_decrypt - Invalid cipher text size +[Check 11] Test psa_aead_decrypt - Invalid tag length 0 +[Check 12] Test psa_aead_decrypt - Invalid key handle +[Check 13] Test psa_aead_decrypt - Zero as key handle +[Check 14] Test psa_aead_decrypt - Empty key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 226 | DESCRIPTION: Testing crypto MAC APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_mac_sign_setup 64 Byte HMAC +[Check 2] Test psa_mac_sign_setup 16 Byte AES - CMAC +[Check 3] Test psa_mac_sign_setup 16 Byte AES - GMAC +[Check 4] Test psa_mac_sign_setup incompactible HMAC for CMAC +[Check 5] Test psa_mac_sign_setup invalid usage +[Check 6] Test psa_mac_sign_setup invalid key type +[Check 7] Test psa_mac_sign_setup truncated MAC too large +[Check 8] Test psa_mac_sign_setup truncated MAC too small +[Check 9] Test psa_mac_sign_setup bad algorithm (unknown MAC algorithm) +[Check 10] Test psa_mac_sign_setup bad algorithm (not a MAC algorithm) +[Check 11] Test psa_mac_sign_setup with invalid key handle +[Check 12] Test psa_mac_sign_setup with zero key handle +[Check 13] Test psa_mac_sign_setup with empty key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 227 | DESCRIPTION: Testing crypto MAC APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_mac_update 64 Byte HMAC SHA256 +[Check 2] Test psa_mac_update 16 Byte AES - CMAC +[Check 3] Test psa_mac_update 32 Byte HMAC SHA512 +[Check 4] Test psa_mac_update without mac setup +TEST RESULT: PASSED + +****************************************** + +TEST: 228 | DESCRIPTION: Testing crypto MAC APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_mac_sign_finish HMAC SHA 224 +[Check 2] Test psa_mac_sign_finish HMAC SHA 256 +[Check 3] Test psa_mac_sign_finish HMAC SHA 512 +[Check 4] Test psa_mac_sign_finish HMAC SHA 224 (truncated to 8 Byte) +[Check 5] Test psa_mac_sign_finish CMAC AES 128 +[Check 6] Test psa_mac_sign_finish small size buffer +TEST RESULT: PASSED + +****************************************** + +TEST: 229 | DESCRIPTION: Testing crypto MAC APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_mac_verify_setup 64 Byte HMAC +[Check 2] Test psa_mac_verify_setup 16 Byte AES - CMAC +[Check 3] Test psa_mac_verify_setup 16 Byte AES - GMAC +[Check 4] Test psa_mac_verify_setup incompactible HMAC for CMAC +[Check 5] Test psa_mac_verify_setup invalid usage +[Check 6] Test psa_mac_verify_setup invalid key type +[Check 7] Test psa_mac_verify_setup truncated MAC too large +[Check 8] Test psa_mac_verify_setup truncated MAC too small +[Check 9] Test psa_mac_verify_setup bad algorithm (unknown MAC algorithm) +[Check 10] Test psa_mac_verify_setup bad algorithm (not a MAC algorithm) +[Check 11] Test psa_mac_verify_setup invalid key handle +[Check 12] Test psa_mac_verify_setup zero as key handle +[Check 13] Test psa_mac_verify_setup empty key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 230 | DESCRIPTION: Testing crypto MAC APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_mac_verify_finish HMAC SHA 224 +[Check 2] Test psa_mac_verify_finish HMAC SHA 256 +[Check 3] Test psa_mac_verify_finish HMAC SHA 512 +[Check 4] Test psa_mac_verify_finish HMAC SHA 224 (truncated to 8 Byte) +[Check 5] Test psa_mac_verify_finish CMAC AES 128 +[Check 6] Test psa_mac_verify_finish small size buffer +[Check 7] Test psa_mac_verify_finish incorrect expected MAC +TEST RESULT: PASSED + +****************************************** + +TEST: 231 | DESCRIPTION: Testing crypto MAC APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_mac_abort HMAC SHA 224 +[Check 2] Test psa_mac_abort HMAC SHA 256 +[Check 3] Test psa_mac_abort HMAC SHA 512 +[Check 4] Test psa_mac_abort HMAC SHA 224 (truncated to 8 Byte) +[Check 5] Test psa_mac_abort CMAC AES 128 +[Check 6] Test psa_mac_sign_finish after calling psa_mac_abort +TEST RESULT: PASSED + +****************************************** + +TEST: 232 | DESCRIPTION: Testing crypto symmetric cipher APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_cipher_encrypt_setup 16 Byte AES +[Check 2] Test psa_cipher_encrypt_setup 24 Byte AES +[Check 3] Test psa_cipher_encrypt_setup 32 Byte AES +[Check 4] Test psa_cipher_encrypt_setup DES 64 bit key +[Check 5] Test psa_cipher_encrypt_setup Triple DES 2-Key +[Check 6] Test psa_cipher_encrypt_setup Triple DES 3-Key +[Check 7] Test psa_cipher_encrypt_setup 16 Byte raw data +[Check 8] Test psa_cipher_encrypt_setup - not a cipher algorithm +[Check 9] Test psa_cipher_encrypt_setup - unknown cipher algorithm +[Check 10] Test psa_cipher_encrypt_setup - incompatible key ARC4 +[Check 11] Test psa_cipher_encrypt_setup - incorrect usage +[Check 12] Test psa_cipher_encrypt_setup - RSA public key +[Check 13] Test psa_cipher_encrypt_setup - RSA keypair +[Check 14] Test psa_cipher_encrypt_setup - EC Public key +[Check 15] Test psa_cipher_encrypt_setup - EC keypair +[Check 16] Test psa_cipher_encrypt_setup - Invalid key handle +[Check 17] Test psa_cipher_encrypt_setup - Zero as key handle +[Check 18] Test psa_cipher_encrypt_setup - Empty key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 233 | DESCRIPTION: Testing crypto symmetric cipher APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_cipher_decrypt_setup 16 Byte AES +[Check 2] Test psa_cipher_decrypt_setup 24 Byte AES +[Check 3] Test psa_cipher_decrypt_setup 32 Byte AES +[Check 4] Test psa_cipher_decrypt_setup DES 64 bit key +[Check 5] Test psa_cipher_decrypt_setup Triple DES 2-Key +[Check 6] Test psa_cipher_decrypt_setup Triple DES 3-Key +[Check 7] Test psa_cipher_decrypt_setup 16 Byte raw data +[Check 8] Test psa_cipher_decrypt_setup - not a cipher algorithm +[Check 9] Test psa_cipher_decrypt_setup - unknown cipher algorithm +[Check 10] Test psa_cipher_decrypt_setup - incompatible key ARC4 +[Check 11] Test psa_cipher_decrypt_setup - incorrect usage +[Check 12] Test psa_cipher_decrypt_setup - RSA public key +[Check 13] Test psa_cipher_decrypt_setup - RSA keypair +[Check 14] Test psa_cipher_decrypt_setup - EC Public key +[Check 15] Test psa_cipher_decrypt_setup - EC keypair +[Check 16] Test psa_cipher_decrypt_setup - Invalid key handle +[Check 17] Test psa_cipher_decrypt_setup - Zero as key handle +[Check 18] Test psa_cipher_decrypt_setup - Empty key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 234 | DESCRIPTION: Testing crypto symmetric cipher APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_cipher_generate_iv 16 Byte AES +[Check 2] Test psa_cipher_generate_iv 24 Byte AES +[Check 3] Test psa_cipher_generate_iv 32 Byte AES +[Check 4] Test psa_cipher_generate_iv DES 64 bit key +[Check 5] Test psa_cipher_generate_iv Triple DES 2-Key +[Check 6] Test psa_cipher_generate_iv Triple DES 3-Key +[Check 7] Test psa_cipher_generate_iv AES - small iv buffer +[Check 8] Test psa_cipher_generate_iv DES - small iv buffer +[Check 9] Test psa_cipher_generate_iv AES - large iv buffer +[Check 10] Test psa_cipher_generate_iv DES - large iv buffer +TEST RESULT: PASSED + +****************************************** + +TEST: 235 | DESCRIPTION: Testing crypto symmetric cipher APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_cipher_set_iv 16 Byte AES +[Check 2] Test psa_cipher_set_iv 24 Byte AES +[Check 3] Test psa_cipher_set_iv 32 Byte AES +[Check 4] Test psa_cipher_set_iv DES 64 bit key +[Check 5] Test psa_cipher_set_iv Triple DES 2-Key +[Check 6] Test psa_cipher_set_iv Triple DES 3-Key +[Check 7] Test psa_cipher_set_iv AES - small iv buffer +[Check 8] Test psa_cipher_set_iv DES - small iv buffer +[Check 9] Test psa_cipher_set_iv AES - large iv buffer +[Check 10] Test psa_cipher_set_iv DES - large iv buffer +TEST RESULT: PASSED + +****************************************** + +TEST: 236 | DESCRIPTION: Testing crypto symmetric cipher APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_cipher_update - Encrypt - AES CBC_NO_PADDING +[Check 2] Test psa_cipher_update - Encrypt - AES CBC_NO_PADDING (Short input) +[Check 3] Test psa_cipher_update - Encrypt - AES CBC_PKCS7 +[Check 4] Test psa_cipher_update - Encrypt - AES CBC_PKCS7 (Short input) +[Check 5] Test psa_cipher_update - Encrypt - AES CTR +[Check 6] Test psa_cipher_update - Encrypt - DES CBC (nopad) +[Check 7] Test psa_cipher_update - Encrypt - 2-key 3DE -CBC (nopad) +[Check 8] Test psa_cipher_update - Encrypt - 3-key 3DE -CBC (nopad) +[Check 9] Test psa_cipher_update - small output buffer size +[Check 10] Test psa_cipher_update - Decrypt - AES CBC_NO_PADDING +[Check 11] Test psa_cipher_update - Decrypt - AES CBC_NO_PADDING (Short input) +[Check 12] Test psa_cipher_update - Decrypt - AES CBC_PKCS7 +[Check 13] Test psa_cipher_update - Decrypt - AES CBC_PKCS7 (Short input) +[Check 14] Test psa_cipher_update - Decrypt - AES CTR +[Check 15] Test psa_cipher_update - Decrypt - DES CBC (nopad) +[Check 16] Test psa_cipher_update - Decrypt - 2-key 3DE -CBC (nopad) +[Check 17] Test psa_cipher_update - Decrypt - 3-key 3DE -CBC (nopad) +[Check 18] Test psa_cipher_update without cipher setup +TEST RESULT: PASSED + +****************************************** + +TEST: 237 | DESCRIPTION: Testing crypto symmetric cipher APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_cipher_finish - Encrypt - AES CBC_NO_PADDING +[Check 2] Test psa_cipher_finish - Encrypt - AES CBC_NO_PADDING (Short input) +[Check 3] Test psa_cipher_finish - Encrypt - AES CBC_PKCS7 +[Check 4] Test psa_cipher_finish - Encrypt - AES CBC_PKCS7 (Short input) +[Check 5] Test psa_cipher_finish - Encrypt - AES CTR +[Check 6] Test psa_cipher_finish - Encrypt - AES CTR (short input) +[Check 7] Test psa_cipher_finish - Encrypt - DES CBC (nopad) +[Check 8] Test psa_cipher_finish - Encrypt - 2-key 3DE -CBC (nopad) +[Check 9] Test psa_cipher_finish - Encrypt - 3-key 3DE -CBC (nopad) +[Check 10] Test psa_cipher_finish - small output buffer size +[Check 11] Test psa_cipher_finish - Decrypt - AES CBC_NO_PADDING +[Check 12] Test psa_cipher_finish - Decrypt - AES CBC_NO_PADDING (Short input) +[Check 13] Test psa_cipher_finish - Decrypt - AES CBC_PKCS7 +[Check 14] Test psa_cipher_finish - Decrypt - AES CBC_PKCS7 (Short input) +[Check 15] Test psa_cipher_finish - Decrypt - AES CTR +[Check 16] Test psa_cipher_finish - Decrypt - AES CTR (short input) +[Check 17] Test psa_cipher_finish - Decrypt - DES CBC (nopad) +[Check 18] Test psa_cipher_finish - Decrypt - 2-key 3DE -CBC (nopad) +[Check 19] Test psa_cipher_finish - 3-key 3DE -CBC (nopad) +TEST RESULT: PASSED + +****************************************** + +TEST: 238 | DESCRIPTION: Testing crypto symmetric cipher APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_cipher_abort - Encrypt - AES CBC_NO_PADDING +[Check 2] Test psa_cipher_abort - Encrypt - AES CBC_PKCS7 +[Check 3] Test psa_cipher_abort - Encrypt - AES CTR +[Check 4] Test psa_cipher_abort - Encrypt - DES CBC (nopad) +[Check 5] Test psa_cipher_abort - Encrypt - 2-key 3DE -CBC (nopad) +[Check 6] Test psa_cipher_abort - Encrypt - 3-key 3DE -CBC (nopad) +[Check 7] Test psa_cipher_abort - Decrypt - AES CBC_NO_PADDING +[Check 8] Test psa_cipher_abort - Decrypt - AES CBC_PKCS7 +[Check 9] Test psa_cipher_abort - Decrypt - AES CTR +[Check 10] Test psa_cipher_abort - Decrypt - DES CBC (nopad) +[Check 11] Test psa_cipher_abort - Decrypt - 2-key 3DE -CBC (nopad) +[Check 12] Test psa_cipher_abort - Decrypt - 3-key 3DE -CBC (nopad) +[Check 13] Test psa_cipher_update after psa_cipher_abort should fail +TEST RESULT: PASSED + +****************************************** + +TEST: 239 | DESCRIPTION: Testing crypto asymmetric APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_asymmetric_encrypt - RSA PKCS1V15 +[Check 2] Test psa_asymmetric_encrypt - RSA OAEP SHA256 +[Check 3] Test psa_asymmetric_encrypt - RSA OAEP SHA256 with label +[Check 4] Test psa_asymmetric_encrypt - RSA KEYPAIR PKCS1V15 +[Check 5] Test psa_asymmetric_encrypt - Small output buffer +[Check 6] Test psa_asymmetric_encrypt - Invalid algorithm +[Check 7] Test psa_asymmetric_encrypt - Invalid key type +[Check 8] Test psa_asymmetric_encrypt - Invalid usage +[Check 9] Test psa_asymmetric_encrypt - Invalid key handle +[Check 10] Test psa_asymmetric_encrypt - Zero as key handle +[Check 11] Test psa_asymmetric_encrypt - Empty key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 240 | DESCRIPTION: Testing crypto asymmetric APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_asymmetric_decrypt - RSA KEYPAIR PKCS1V15 +[Check 2] Test psa_asymmetric_decrypt - RSA KEYPAIR OAEP SHA256 +[Check 3] Test psa_asymmetric_decrypt - RSA KEYPAIR OAEP SHA256 with label +[Check 4] Test psa_asymmetric_decrypt - Invalid key type (RSA public key) +[Check 5] Test psa_asymmetric_decrypt - Small output buffer +[Check 6] Test psa_asymmetric_decrypt - Invalid algorithm +[Check 7] Test psa_asymmetric_decrypt - Invalid key type (AES Key) +[Check 8] Test psa_asymmetric_decrypt - Invalid usage +[Check 9] Test psa_asymmetric_decrypt - Invalid key handle +[Check 10] Test psa_asymmetric_decrypt - Zero as key handle +[Check 11] Test psa_asymmetric_decrypt - Empty key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 241 | DESCRIPTION: Testing crypto asymmetric APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_asymmetric_sign - RSA KEYPAIR PKCS1V15 RAW +[Check 2] Test psa_asymmetric_sign - RSA KEYPAIR PKCS1V15 SHA-256 +[Check 3] Test psa_asymmetric_sign - ECDSA SECP256R1 SHA-256 +[Check 4] Test psa_asymmetric_sign - Invalid key type (RSA public key) +[Check 5] Test psa_asymmetric_sign - Small output buffer +[Check 6] Test psa_asymmetric_sign - Invalid algorithm +[Check 7] Test psa_asymmetric_sign - Invalid key type (AES Key) +[Check 8] Test psa_asymmetric_sign - Invalid usage +[Check 9] Test psa_asymmetric_sign - Wrong hash size +[Check 10] Test psa_asymmetric_sign - Invalid key handle +[Check 11] Test psa_asymmetric_sign - Zero as key handle +[Check 12] Test psa_asymmetric_sign - Empty key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 242 | DESCRIPTION: Testing crypto asymmetric APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_asymmetric_verify - RSA KEYPAIR PKCS1V15 RAW +[Check 2] Test psa_asymmetric_verify - RSA KEYPAIR PKCS1V15 SHA-256 +[Check 3] Test psa_asymmetric_verify - ECDSA KEYPAIR SECP256R1 SHA-256 +[Check 4] Test psa_asymmetric_verify - RSA public key +[Check 5] Test psa_asymmetric_verify - Small output buffer +[Check 6] Test psa_asymmetric_verify - Invalid algorithm +[Check 7] Test psa_asymmetric_verify - Invalid key type (AES Key) +[Check 8] Test psa_asymmetric_verify - Invalid usage +[Check 9] Test psa_asymmetric_verify - Wrong hash size +[Check 10] Test psa_asymmetric_verify - Wrong signature +[Check 11] Test psa_asymmetric_verify - EC public key +[Check 12] Test psa_asymmetric_verify - Wrong signature size +[Check 13] Test psa_asymmetric_verify - Invalid key handle +[Check 14] Test psa_asymmetric_verify - Invalid key handle +[Check 15] Test psa_asymmetric_verify - Zero as key handle +[Check 16] Test psa_asymmetric_verify - Empty key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 243 | DESCRIPTION: Testing crypto generator APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_key_agreement - ECDH SECP256R1 +[Check 2] Test psa_key_agreement - ECDH SECP384R1 +[Check 3] Test psa_key_agreement - Invalid usage +[Check 4] Test psa_key_agreement - Unknown KDF +[Check 5] Test psa_key_agreement - Not a key agreement alg +[Check 6] Test psa_key_agreement - Public key on different curve +[Check 7] Test psa_key_agreement - Public key instead of private key +[Check 8] Test psa_key_agreement - Invalid key handle +[Check 9] Test psa_key_agreement - Invalid key handle +[Check 10] Test psa_key_agreement - Zero as key handle +[Check 11] Test psa_key_agreement - Empty key handle +TEST RESULT: PASSED + +****************************************** + +TEST: 244 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_copy_key 16 Byte AES +[Check 2] Test psa_copy_key 24 Byte AES +[Check 3] Test psa_copy_key 32 Byte AES +[Check 4] Test psa_copy_key 2048 RSA public key +[Check 5] Test psa_copy_key with RSA 2048 keypair +[Check 6] Test psa_copy_key with Incompatible target policy(source and target) +[Check 7] Test psa_copy_key with Incompatible constraint +[Check 8] Test psa_copy_key with unexport source key usage +TEST RESULT: PASSED + +****************************************** + +************ Crypto Suite Report ********** +TOTAL TESTS : 44 +TOTAL PASSED : 44 +TOTAL SIM ERROR : 0 +TOTAL FAILED : 0 +TOTAL SKIPPED : 0 +******************** + +Entering standby.. + diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/reference_logs/protected_storage.txt b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/reference_logs/protected_storage.txt new file mode 100755 index 00000000..93b7ba38 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/reference_logs/protected_storage.txt @@ -0,0 +1,157 @@ +***** PSA Architecture Test Suite - Version 0.9 ***** + +Running.. Protected Storage Suite +****************************************** + +TEST: 401 | DESCRIPTION: UID not found check +[Info] Executing tests from non-secure +[Check 1] Call get API for UID 6 which is not set +[Check 2] Call get_info API for UID 6 which is not set +[Check 3] Call remove API for UID 6 which is not set +[Check 4] Call get API for UID 6 which is removed +[Check 5] Call get_info API for UID 6 which is removed +[Check 6] Call remove API for UID 6 which is removed +Set storage for UID 6 +[Check 7] Call get API for different UID 6 +[Check 8] Call get_info API for different UID 6 +[Check 9] Call remove API for different UID 6 +TEST RESULT: PASSED + +****************************************** + +TEST: 402 | DESCRIPTION: Write once error check +[Info] Executing tests from non-secure +[Check 1] Update the flag of UID 1 with WRITE_ONCE flag +[Check 2] Try to remove the UID 1 having WRITE_ONCE flag +[Check 3] Create a new UID 2 with WRITE_ONCE flag +[Check 4] Try to remove the UID 2 having WRITE_ONCE flag +[Check 5] Try to change the length of write_once UID 2 +[Check 6] Check UID removal still fails +[Check 7] Try to change the WRITE_ONCE flag to None for UID 2 +[Check 8] Check UID removal still fails +TEST RESULT: PASSED + +****************************************** + +TEST: 403 | DESCRIPTION: Insufficient space check +[Info] Executing tests from non-secure +[Check 1] Overload storage space +Remove all registered UIDs +[Check 2] Overload storage again to verify all previous UID removed + +Remove all registered UIDs +TEST RESULT: PASSED + +****************************************** + +TEST: 404 | DESCRIPTION: Data Consistency check +[Info] Executing tests from non-secure +[Check 1] Call get API with incorrect length +[Check 2] Old buffer invalid after length change +TEST RESULT: PASSED + +****************************************** + +TEST: 405 | DESCRIPTION: Success scenarios check +[Info] Executing tests from non-secure +[Check 1] Set UID with data length zero and call storage APIs +[Check 2] Resetting the length check +TEST RESULT: PASSED + +****************************************** + +TEST: 406 | DESCRIPTION: Flags not supported check +[Info] Executing tests from non-secure +[Check 1] Call set API with valid flag values +TEST RESULT: PASSED + +****************************************** + +TEST: 407 | DESCRIPTION: Incorrect Size check +[Info] Executing tests from non-secure +Create a valid Storage +Increase the length of storage +[Check 1] Call get API with old length +Decrease the length of storage +[Check 2] Call get API with old length +[Check 3] Call get API with valid length +TEST RESULT: PASSED + +****************************************** + +TEST: 408 | DESCRIPTION: Invalid offset check +[Info] Executing tests from non-secure +[Check 1] Try to access data with varying valid offset +[Check 2] Try to access data with varying invalid offset +TEST RESULT: PASSED + +****************************************** + +TEST: 409 | DESCRIPTION: Invalid Arguments check +[Info] Executing tests from non-secure +[Check 1] Call set API with NULL pointer and data length 0 +[Check 2] Call get API with NULL read buffer and data length 0 +[Check 3] Remove the UID +[Check 4] Call get_info API to verify UID removed +[Check 5] Create UID with zero data_len and valid write buffer +[Check 8] Call get API with NULL read buffer and data length 0 +[Check 9] Increase the length +TEST RESULT: PASSED + +****************************************** + +TEST: 410 | DESCRIPTION: UID value zero check +[Info] Executing tests from non-secure +[Check 1] Creating storage with UID 0 should fail +TEST RESULT: PASSED + +****************************************** + +TEST: 411 | DESCRIPTION: Optional APIs: UID not found check +[Info] Executing tests from non-secure +Test Case skipped as Optional PS APIs are not supported. +TEST RESULT: SKIPPED (Skip Code=0x2B) + +****************************************** + +TEST: 412 | DESCRIPTION: Optional APIs: Invalid arguments and offset invalid +[Info] Executing tests from non-secure +Test Case skipped as Optional PS APIs are not supported. +TEST RESULT: SKIPPED (Skip Code=0x2B) + +****************************************** + +TEST: 413 | DESCRIPTION: Set_Extended and Create api : Success +[Info] Executing tests from non-secure +Test Case skipped as Optional PS APIs are not supported. +TEST RESULT: SKIPPED (Skip Code=0x2B) + +****************************************** + +TEST: 414 | DESCRIPTION: Optional APIs not supported check +[Info] Executing tests from non-secure +Optional PS APIs are not supported. +[Check 1] Call to create API should fail as API not supported +[Check 2] Create valid storage with set API +[Check 3] Call to set_extended API call should fail +[Check 4] Verify data is unchanged +TEST RESULT: PASSED + +****************************************** + +TEST: 415 | DESCRIPTION: Create API write_once flag value check +[Info] Executing tests from non-secure +Test Case skipped as Optional PS APIs are not supported. +TEST RESULT: SKIPPED (Skip Code=0x2B) + +****************************************** + +************ Protected Storage Suite Report ********** +TOTAL TESTS : 15 +TOTAL PASSED : 11 +TOTAL SIM ERROR : 0 +TOTAL FAILED : 0 +TOTAL SKIPPED : 4 +****************************************** + +Entering standby.. diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/spe/pal_driver_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/spe/pal_driver_intf.c index eb468b9b..4d522071 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/spe/pal_driver_intf.c +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/spe/pal_driver_intf.c @@ -33,7 +33,7 @@ void pal_uart_init(uint32_t uart_base_addr) - data : Value for format specifier **/ -void pal_print(char *str, uint32_t data) +void pal_print(char *str, int32_t data) { pal_uart_pl011_print(str,data); } @@ -109,3 +109,23 @@ int pal_wd_timer_is_enabled(addr_t base_addr) return (pal_wd_cmsdk_is_enabled(base_addr)); } +/** + @brief - Trigger interrupt for irq signal assigned to driver partition + before return to caller. + @param - void + @return - void +**/ +void pal_generate_interrupt(void) +{ + pal_uart_pl011_generate_irq(); +} + +/** + @brief - Disable interrupt that was generated using pal_generate_interrupt API. + @param - void + @return - void +**/ +void pal_disable_interrupt(void) +{ + pal_uart_pl011_disable_irq(); +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/spe/pal_driver_intf.h b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/spe/pal_driver_intf.h index da85a63e..cef34ca8 100644 --- a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/spe/pal_driver_intf.h +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_a/spe/pal_driver_intf.h @@ -23,11 +23,13 @@ #include "pal_wd_cmsdk.h" void pal_uart_init(uint32_t uart_base_addr); -void pal_print(char *str, uint32_t data); +void pal_print(char *str, int32_t data); int pal_nvmem_write(addr_t base, uint32_t offset, void *buffer, int size); int pal_nvmem_read(addr_t base, uint32_t offset, void *buffer, int size); int pal_wd_timer_init(addr_t base_addr, uint32_t time_us, uint32_t timer_tick_us); int pal_wd_timer_enable(addr_t base_addr); int pal_wd_timer_disable(addr_t base_addr); int pal_wd_timer_is_enabled(addr_t base_addr); +void pal_generate_interrupt(void); +void pal_disable_interrupt(void); #endif /* _PAL_DRIVER_INTF_H_ */ diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/Makefile b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/Makefile new file mode 100644 index 00000000..aa7e10b7 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/Makefile @@ -0,0 +1,160 @@ +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT 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 + +# Make variables to select correct instances of PAL files + +## PSA_IPC_IMPLEMENTED must be true for IPC SUITE +PSA_IPC_IMPLEMENTED:=0 + +## PSA_CRYPTO_IMPLEMENTED must be true for CRYPTO SUITE +PSA_CRYPTO_IMPLEMENTED:=1 + +## PSA_PROTECTED_STORAGE_IMPLEMENTED must be true for PROTECTED_STORAGE SUITE +PSA_PROTECTED_STORAGE_IMPLEMENTED:=1 + +## PSA_INTERNAL_TRUSTED_STORAGE_IMPLEMENTED must be true for INTERNAL_TRUSTED_STORAGE SUITE +PSA_INTERNAL_TRUSTED_STORAGE_IMPLEMENTED:=0 + +## PSA_INITIAL_ATTESTATION_IMPLEMENTED must be true for INITIAL_ATTESTATION SUITE +PSA_INITIAL_ATTESTATION_IMPLEMENTED:=1 + +# Make variables holding NSPE/SPE source files + +## PAL C source files part of NSPE library +SRC_C_NSPE= + +## PAL ASM source files part of NSPE library +SRC_ASM_NSPE= + +## PAL C source files part of SPE library - driver partition +SRC_C_DRIVER_SP= + +## PAL ASM source files part of SPE library - driver partition +SRC_ASM_DRIVER_SP= + +ifeq (${PSA_IPC_IMPLEMENTED},1) +# When PSA_IPC_IMPLEMENTED=1, driver functionalities are implemented as RoT-services +# and secure and non-secure clients will call to these RoT-services to get appropriate driver services. +SRC_C_NSPE += pal_client_api_intf.c +SRC_C_NSPE += pal_driver_ipc_intf.c + +# Driver files will be compiled as part of driver partition +SRC_C_DRIVER_SP += pal_driver_intf.c pal_nvmem.c pal_uart.c pal_wd_cmsdk.c +else + +# When PSA_IPC_IMPLEMENTED=0, driver files will be compiled as part of NSPE +SRC_C_NSPE += pal_client_api_empty_intf.c +SRC_C_NSPE += pal_driver_ns_intf.c pal_nvmem.c pal_uart.c pal_wd_cmsdk.c +endif + +ifeq (${PSA_CRYPTO_IMPLEMENTED},1) +SRC_C_NSPE += pal_crypto_intf.c +else +SRC_C_NSPE += pal_crypto_empty_intf.c +endif + +ifeq (${PSA_PROTECTED_STORAGE_IMPLEMENTED},1) +SRC_C_NSPE += pal_protected_storage_intf.c +else +SRC_C_NSPE += pal_protected_storage_empty_intf.c +endif + +ifeq (${PSA_INTERNAL_TRUSTED_STORAGE_IMPLEMENTED},1) +SRC_C_NSPE += pal_internal_trusted_storage_intf.c +else +SRC_C_NSPE += pal_internal_trusted_storage_empty_intf.c +endif + +ifeq (${PSA_INITIAL_ATTESTATION_IMPLEMENTED},1) +SRC_C_NSPE += pal_attestation_intf.c +SRC_C_NSPE += pal_attestation_eat.c +SRC_C_NSPE += pal_attestation_crypto.c +else +SRC_C_NSPE += pal_attestation_empty_intf.c +endif + +INCLUDE= -I$(SOURCE)/platform/targets/$(TARGET)/nspe \ + -I$(SOURCE)/platform/targets/$(TARGET)/spe \ + -I$(BUILD)/platform/$(TARGET)/ \ + -I$(SOURCE)/platform/drivers/uart/pl011 \ + -I$(SOURCE)/platform/drivers/nvmem/ \ + -I$(SOURCE)/platform/drivers/watchdog/cmsdk \ + -I$(SOURCE)/platform/targets/$(TARGET)/nspe/common \ + -I$(SOURCE)/platform/targets/$(TARGET)/nspe/crypto \ + -I$(SOURCE)/platform/targets/$(TARGET)/nspe/initial_attestation \ + -I$(SOURCE)/platform/targets/$(TARGET)/nspe/initial_attestation/ext/inc \ + -I$(SOURCE)/platform/targets/$(TARGET)/nspe/internal_trusted_storage \ + -I$(SOURCE)/platform/targets/$(TARGET)/nspe/protected_storage \ + +VPATH=$(SOURCE)/platform/targets/$(TARGET)/: \ + $(SOURCE)/platform/targets/$(TARGET)/spe: \ + $(SOURCE)/platform/targets/$(TARGET)/nspe: \ + $(SOURCE)/platform/drivers/uart/pl011: \ + $(SOURCE)/platform/drivers/nvmem: \ + $(SOURCE)/platform/drivers/watchdog/cmsdk: \ + $(SOURCE)/platform/targets/$(TARGET)/nspe/common: \ + $(SOURCE)/platform/targets/$(TARGET)/nspe/crypto: \ + $(SOURCE)/platform/targets/$(TARGET)/nspe/initial_attestation: \ + $(SOURCE)/platform/targets/$(TARGET)/nspe/initial_attestation/ext/src: \ + $(SOURCE)/platform/targets/$(TARGET)/nspe/internal_trusted_storage: \ + $(SOURCE)/platform/targets/$(TARGET)/nspe/protected_storage: \ + +all: build + +ifeq (${PSA_IPC_IMPLEMENTED},1) +build: mkdir build_nspe_pal build_spe_pal +else +build: mkdir build_nspe_pal +endif + +mkdir: + @mkdir -p $(BUILD)/platform/nspe/ + @mkdir -p $(BUILD)/platform/spe/ + +# BUILD NSPE PAL +build_nspe_pal: build_c_nspe build_asm_nspe pal_nspe.a + +build_c_nspe: $(SRC_C_NSPE:%.c=$(BUILD)/platform/nspe/%.o) +build_asm_nspe: $(SRC_ASM_NSPE:%.s=$(BUILD)/platform/nspe/%.o) + +$(BUILD)/platform/nspe/%.o : %.c + $(CC) $(PAL_CDEFS) $(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 + +# BUILD SPE PAL +build_spe_pal: build_driver_sp + +build_driver_sp: build_c_driver_sp build_asm_driver_sp + +build_c_driver_sp: $(SRC_C_DRIVER_SP:%.c=$(BUILD)/platform/spe/%_driver_sp.o) +build_asm_driver_sp: $(SRC_ASM_DRIVER_SP:%.s=$(BUILD)/platform/spe/%_driver_sp.o) + +# Generated %_driver_sp.o(s) are used in spbuild.mk to create final driver_partition.a +$(BUILD)/platform/spe/%_driver_sp.o : %.c + $(CC) $(INCLUDE) -DSPE_BUILD -o $@ -c $< + +$(BUILD)/platform/spe/%_driver_sp.o : %.s + $(AS) $(INCLUDE) -o $@ $< + +clean: + @rm -rf $(BUILD)/platform/nspe/* $(BUILD)/platform/spe/*.a diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/manifests/common/driver_partition_psa.json b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/manifests/common/driver_partition_psa.json new file mode 100644 index 00000000..7010c9b5 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/manifests/common/driver_partition_psa.json @@ -0,0 +1,75 @@ +{ + "psa_framework_version": 1.0, + "name": "DRIVER_PARTITION", + "type": "PSA-ROT", + "priority": "NORMAL", + "description": "Implements device services such print, flash read/write,. etc.", + "entry_point": "driver_main", + "stack_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_TEST_SID", + "sid": "0x0000FC04", + "signal": "DRIVER_TEST_SIG", + "non_secure_clients": true, + "minor_version": 1, + "minor_policy": "RELAXED" + } + ], + "mmio_regions" : [ + { + "name": "UART_REGION", + "base": "0x40004000", + "size": "0x1000", + "permission": "READ-WRITE" + }, + { + "name": "WATCHDOG_REGION", + "base": "0x40008000", + "size": "0x1000", + "permission": "READ-WRITE" + }, + { + "name": "NVMEM_REGION", + "base": "0x2002F000", + "size": "0x400", + "permission": "READ-WRITE" + }, + { + "name": "DRIVER_PARTITION_MMIO", + "base": "0x200AF040", + "size": "0x20", + "permission": "READ-WRITE" + } + ], + "irqs": [ + { + "description": "Using UART TX interrupt to test psa_wait and psa_eoi for irq_signal", + "signal": "DRIVER_UART_INTR_SIG", + "line_num": 46 + } + ] +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/manifests/ipc/client_partition_psa.json b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/manifests/ipc/client_partition_psa.json new file mode 100644 index 00000000..b93377bd --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/manifests/ipc/client_partition_psa.json @@ -0,0 +1,37 @@ +{ + "psa_framework_version": 1.0, + "name": "CLIENT_PARTITION", + "type": "APPLICATION-ROT", + "priority": "NORMAL", + "description": "Client partition executing client test func from SPE", + "entry_point": "client_main", + "stack_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" + } + ], + "dependencies": [ + "DRIVER_UART_SID", + "DRIVER_NVMEM_SID", + "DRIVER_TEST_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" + ], + "mmio_regions" : [ + { + "name": "CLIENT_PARTITION_MMIO", + "base": "0x200AF000", + "size": "0x20", + "permission": "READ-WRITE" + } + ] +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/manifests/ipc/server_partition_psa.json b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/manifests/ipc/server_partition_psa.json new file mode 100644 index 00000000..146b8fbc --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/manifests/ipc/server_partition_psa.json @@ -0,0 +1,69 @@ +{ + "psa_framework_version": 1.0, + "name": "SERVER_PARTITION", + "type": "APPLICATION-ROT", + "priority": "NORMAL", + "description": "Server partition executing server test func", + "entry_point": "server_main", + "stack_size": "0x400", + "heap_size": "0x100", + "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" + } + ], + "dependencies": [ + "DRIVER_UART_SID", + "DRIVER_NVMEM_SID" + ] +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_client_api_empty_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_client_api_empty_intf.c new file mode 100644 index 00000000..578b4cef --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_client_api_empty_intf.c @@ -0,0 +1,93 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_api_intf.h" + +/** + * @brief - Retrieve the version of the PSA Framework API that is implemented. + * This is a wrapper API for psa_framework_version API. + * @param - void + * @return - The PSA Framework API version. + */ + +uint32_t pal_ipc_framework_version(void) +{ + return 0; +} + +/** + * @brief - Retrieve the minor version of a Root of Trust Service by its SID. + * This is a wrapper API for the psa_version API. + * @param - sid The Root of Trust Service ID + * @return - Minor version of Root of Trust Service or PSA_VERSION_NONE if Root of Trust + * Service not present on the system. + */ + +uint32_t pal_ipc_version(uint32_t sid) +{ + return PSA_VERSION_NONE; +} + +/** + * @brief - Connect to given sid. + * This is a wrapper API for the psa_connect API. + * @param - sid : RoT service id + * @param - minor_version : minor_version of RoT service + * @return - psa_handle_t : return connection handle + */ + +psa_handle_t pal_ipc_connect(uint32_t sid, uint32_t minor_version) +{ + return PSA_NULL_HANDLE; +} + +/** + * @brief Call a connected Root of Trust Service. + * This is a wrapper API for the psa_call API. + * 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 -psa_status_t + */ + +psa_status_t pal_ipc_call(psa_handle_t handle, + const psa_invec *in_vec, + size_t in_len, + psa_outvec *out_vec, + size_t out_len) +{ + return (PSA_SUCCESS - 1); +} + +/** + * @brief Close a connection to a Root of Trust Service. + * This is a wrapper API for the psa_close API. + * 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 pal_ipc_close(psa_handle_t handle) +{ + return; +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_client_api_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_client_api_intf.c new file mode 100644 index 00000000..20ddd118 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_client_api_intf.c @@ -0,0 +1,97 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_api_intf.h" + +/** + * @brief - Retrieve the version of the PSA Framework API that is implemented. + * This is a wrapper API for psa_framework_version API. + * @param - void + * @return - The PSA Framework API version. + * Note - Return PAL_STATUS_ERROR if PSA IPC is not implemented. + */ + +uint32_t pal_ipc_framework_version(void) +{ + return (psa_framework_version()); +} + +/** + * @brief - Retrieve the minor version of a Root of Trust Service by its SID. + * This is a wrapper API for the psa_version API. + * @param - sid The Root of Trust Service ID + * @return - Minor version of Root of Trust Service or PSA_VERSION_NONE if Root of Trust + * Service not present on the system. + * Note - Return PAL_STATUS_ERROR if PSA IPC is not implemented. + */ + +uint32_t pal_ipc_version(uint32_t sid) +{ + return (psa_version(sid)); +} + +/** + * @brief - Connect to given sid. + * This is a wrapper API for the psa_connect API. + * @param - sid : RoT service id + * @param - minor_version : minor_version of RoT service + * @return - psa_handle_t : return connection handle + * Note - Return PSA_NULL_HANDLE if PSA IPC is not implemented. + */ + +psa_handle_t pal_ipc_connect(uint32_t sid, uint32_t minor_version) +{ + return (psa_connect(sid, minor_version)); +} + +/** + * @brief Call a connected Root of Trust Service. + * This is a wrapper API for the psa_call API. + * 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 -psa_status_t + * Note - Return -1 if PSA IPC is not implemented. + */ + +psa_status_t pal_ipc_call(psa_handle_t handle, + const psa_invec *in_vec, + size_t in_len, + psa_outvec *out_vec, + size_t out_len) +{ + return (psa_call(handle, in_vec, in_len, out_vec, out_len)); +} + +/** + * @brief Close a connection to a Root of Trust Service. + * This is a wrapper API for the psa_close API. + * 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 pal_ipc_close(psa_handle_t handle) +{ + psa_close(handle); +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_client_api_intf.h b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_client_api_intf.h new file mode 100644 index 00000000..3f5741e0 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_client_api_intf.h @@ -0,0 +1,32 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_CLIENT_API_H_ +#define _PAL_CLIENT_API_H_ + +#include "pal_common.h" + +uint32_t pal_ipc_framework_version(void); +uint32_t pal_ipc_version(uint32_t sid); +psa_handle_t pal_ipc_connect(uint32_t sid, uint32_t minor_version); +psa_status_t pal_ipc_call(psa_handle_t handle, + const psa_invec *in_vec, + size_t in_len, + psa_outvec *out_vec, + size_t out_len); +void pal_ipc_close(psa_handle_t handle); +#endif /* _PAL_CLIENT_API_H_ */ diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_common.h b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_common.h new file mode 100644 index 00000000..3ebe1e10 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_common.h @@ -0,0 +1,118 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include +#include + +#ifndef TARGET_CFG_BUILD +#include "pal_config.h" +#include "pal_crypto_config.h" +#endif + +/* 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; + +#define PAL_STATUS_UNSUPPORTED_FUNC 0xFF + +typedef enum +{ + PAL_STATUS_SUCCESS = 0x0, + PAL_STATUS_ERROR = 0x80 +} pal_status_t; + +typedef enum { + NVMEM_READ = 0x1, + NVMEM_WRITE = 0x2, +} nvmem_fn_type_t; + +typedef struct { + nvmem_fn_type_t nvmem_fn_type; + addr_t base; + uint32_t offset; + int size; +} nvmem_param_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_CRYPTO_TIMEOUT = 0x4, +} wd_timeout_type_t; + +typedef struct { + wd_fn_type_t wd_fn_type; + addr_t wd_base_addr; + uint32_t wd_time_us; + uint32_t wd_timer_tick_us; +} wd_param_t; + +typedef enum { + UART_INIT = 0x1, + UART_PRINT = 0x2, +} uart_fn_type_t; + +/* + * Redefining some of the client.h elements for compilation to go through + * when PSA IPC APIs are not implemented. + */ +#if (PSA_IPC_IMPLEMENTED == 0) + +#ifndef PSA_VERSION_NONE +#define PSA_VERSION_NONE (0) +#endif + +#ifndef PSA_SUCCESS +#define PSA_SUCCESS (0) +typedef int32_t psa_status_t; +#endif +typedef int32_t psa_handle_t; + +#ifndef PSA_NULL_HANDLE +#define PSA_NULL_HANDLE ((psa_handle_t)0) +#endif + +typedef struct psa_invec { + const void *base; + size_t len; +} psa_invec; + +typedef struct psa_outvec { + void *base; + size_t len; +} psa_outvec; + +#endif /* PSA_IPC_IMPLEMENTED */ + +#endif /* _PAL_COMMON_H_ */ diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_config.h b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_config.h new file mode 100644 index 00000000..e3f70ad7 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_config.h @@ -0,0 +1,119 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_CONFIG_H_ +#define _PAL_CONFIG_H_ + +/* + * List of macros used by test suite + */ +#if !defined(PSA_IPC_IMPLEMENTED) +#define PSA_IPC_IMPLEMENTED 0 +#endif + +#if !defined(PSA_CRYPTO_IMPLEMENTED) +#define PSA_CRYPTO_IMPLEMENTED 0 +#endif + +#if !defined(PSA_INTERNAL_TRUSTED_STORAGE_IMPLEMENTED) +#define PSA_INTERNAL_TRUSTED_STORAGE_IMPLEMENTED 0 +#endif + +#if !defined(PSA_PROTECTED_STORAGE_IMPLEMENTED) +#define PSA_PROTECTED_STORAGE_IMPLEMENTED 0 +#endif + +#if !defined(PSA_INITIAL_ATTESTATION_IMPLEMENTED) +#define PSA_INITIAL_ATTESTATION_IMPLEMENTED 0 +#endif + +#if (PSA_IPC_IMPLEMENTED == 0) && \ + (PSA_CRYPTO_IMPLEMENTED == 0) && \ + (PSA_INTERNAL_TRUSTED_STORAGE_IMPLEMENTED == 0) && \ + (PSA_PROTECTED_STORAGE_IMPLEMENTED == 0) && \ + (PSA_INITIAL_ATTESTATION_IMPLEMENTED == 0) +#error "You must define at least one of these macros to run test suite" +#endif + +#if !defined(VERBOSE) +#define VERBOSE 3 /* Print verbosity = TEST */ +#endif + +#if (!defined(VAL_NSPE_BUILD) && !defined(SPE_BUILD)) +#define VAL_NSPE_BUILD 1 +#endif + +#if (!defined(NONSECURE_TEST_BUILD) && !defined(SPE_BUILD)) +#define NONSECURE_TEST_BUILD 1 +#endif + +#if !defined(TEST_COMBINE_ARCHIVE) +#define TEST_COMBINE_ARCHIVE 0 /* Combine test archive or binary? */ +#endif + +#if !defined(WATCHDOG_AVAILABLE) +#define WATCHDOG_AVAILABLE 0 /* If zero, skip watchdog programming */ +#endif + +#if !defined(SP_HEAP_MEM_SUPP) +#define SP_HEAP_MEM_SUPP 0 /* Are Dynamic funcs available to secure partition? */ +#endif + +/* + * Include of PSA defined Header files + */ + +#if PSA_IPC_IMPLEMENTED +/* psa/client.h: Contains the PSA Client API elements */ +#include "psa/client.h" + +/* + * psa_manifest/sid.h: Macro definitions derived from manifest files that map from RoT Service + * names to Service IDs (SIDs). Partition manifest parse build tool must provide the implementation + * of this file. +*/ +#include "psa_manifest/sid.h" + +/* + * psa_manifest/pid.h: Secure Partition IDs + * Macro definitions that map from Secure Partition names to Secure Partition IDs. + * Partition manifest parse build tool must provide the implementation of this file. +*/ +#include "psa_manifest/pid.h" +#endif + +#if PSA_CRYPTO_IMPLEMENTED +/* psa/crypto.h: Contains the PSA Crypto API elements */ +#include "psa/crypto.h" +#endif + +#if PSA_INTERNAL_TRUSTED_STORAGE_IMPLEMENTED +/* psa/internal_trusted_storage.h: Contains the PSA ITS API elements */ +#include "psa/internal_trusted_storage.h" +#endif + +#if PSA_PROTECTED_STORAGE_IMPLEMENTED +/* psa/protected_storage.h: Contains the PSA PS API elements */ +#include "psa/protected_storage.h" +#endif + +#if PSA_INITIAL_ATTESTATION_IMPLEMENTED +/* psa/initial_attestation.h: Contains the PSA Initial Attestation API elements */ +#include "psa/initial_attestation.h" +#endif + +#endif /* _PAL_CONFIG_H_ */ diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_driver_ipc_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_driver_ipc_intf.c new file mode 100644 index 00000000..f8f773fb --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_driver_ipc_intf.c @@ -0,0 +1,305 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_client_api_intf.h" + +/** + @brief - This function initializes the UART + @param - uart base addr + @return - SUCCESS/FAILURE +**/ +int pal_uart_init_ns(uint32_t uart_base_addr) +{ + psa_handle_t print_handle = 0; + psa_status_t status_of_call = PSA_SUCCESS; + uart_fn_type_t uart_fn = UART_INIT; + + psa_invec data[3] = {{&uart_fn, sizeof(uart_fn)}, + {&uart_base_addr, sizeof(uart_base_addr)}, + {NULL, 0}}; + + print_handle = pal_ipc_connect(DRIVER_UART_SID, 0); + if (print_handle < 0) + { + return(PAL_STATUS_ERROR); + } + + status_of_call = pal_ipc_call(print_handle, data, 3, NULL, 0); + if (status_of_call != PSA_SUCCESS) + { + return(PAL_STATUS_ERROR); + } + + pal_ipc_close(print_handle); + return PAL_STATUS_SUCCESS; +} + +/** + @brief - This function parses the input string and writes bytes into UART TX FIFO + @param - str : Input String + - data : Value for format specifier + @return - SUCCESS/FAILURE +**/ + +int pal_print_ns(char *str, int32_t data) +{ + int string_len = 0; + char *p = str; + psa_handle_t print_handle = 0; + psa_status_t status_of_call = PSA_SUCCESS; + pal_status_t status = PAL_STATUS_SUCCESS; + uart_fn_type_t uart_fn = UART_PRINT; + + while (*p != '\0') + { + string_len++; + p++; + } + + psa_invec data1[3] = {{&uart_fn, sizeof(uart_fn)}, + {str, string_len+1}, + {&data, sizeof(data)}}; + print_handle = pal_ipc_connect(DRIVER_UART_SID, 0); + + if (print_handle < 0) + { + return PAL_STATUS_ERROR; + } + else + { + status_of_call = pal_ipc_call(print_handle, data1, 3, NULL, 0); + if (status_of_call != PSA_SUCCESS) + { + status = PAL_STATUS_ERROR; + } + } + pal_ipc_close(print_handle); + return status; +} + +/** + @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_ns(addr_t base_addr, uint32_t time_us, uint32_t timer_tick_us) +{ + 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_base_addr = base_addr; + wd_param.wd_time_us = time_us; + wd_param.wd_timer_tick_us = timer_tick_us; + psa_invec invec[1] = {{&wd_param, sizeof(wd_param)}}; + + handle = pal_ipc_connect(DRIVER_WATCHDOG_SID, 0); + if (handle < 0) + { + return PAL_STATUS_ERROR; + } + else + { + status_of_call = pal_ipc_call(handle, invec, 1, NULL, 0); + if (status_of_call != PSA_SUCCESS) + { + pal_ipc_close(handle); + return PAL_STATUS_ERROR; + } + } + pal_ipc_close(handle); + return PAL_STATUS_SUCCESS; +} + +/** + @brief - Enables a hardware watchdog timer + @param - base_addr : Base address of the watchdog module + @return - SUCCESS/FAILURE +**/ +int pal_wd_timer_enable_ns(addr_t base_addr) +{ + 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; + wd_param.wd_base_addr = base_addr; + wd_param.wd_time_us = 0; + wd_param.wd_timer_tick_us = 0; + psa_invec invec[1] = {{&wd_param, sizeof(wd_param)}}; + + handle = pal_ipc_connect(DRIVER_WATCHDOG_SID, 0); + if (handle < 0) + { + return PAL_STATUS_ERROR; + } + else + { + status_of_call = pal_ipc_call(handle, invec, 1, NULL, 0); + if (status_of_call != PSA_SUCCESS) + { + pal_ipc_close(handle); + return PAL_STATUS_ERROR; + } + } + pal_ipc_close(handle); + return PAL_STATUS_SUCCESS; +} + +/** + @brief - Disables a hardware watchdog timer + @param - base_addr : Base address of the watchdog module + @return - SUCCESS/FAILURE +**/ +int pal_wd_timer_disable_ns(addr_t base_addr) +{ + 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; + wd_param.wd_base_addr = base_addr; + wd_param.wd_time_us = 0; + wd_param.wd_timer_tick_us = 0; + psa_invec invec[1] = {{&wd_param, sizeof(wd_param)}}; + + handle = pal_ipc_connect(DRIVER_WATCHDOG_SID, 0); + if (handle < 0) + { + return PAL_STATUS_ERROR; + } + else + { + status_of_call = pal_ipc_call(handle, invec, 1, NULL, 0); + if (status_of_call != PSA_SUCCESS) + { + pal_ipc_close(handle); + return PAL_STATUS_ERROR; + } + } + pal_ipc_close(handle); + return PAL_STATUS_SUCCESS; +} + +/** + @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 - SUCCESS/FAILURE +**/ +int pal_nvmem_read_ns(addr_t base, 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.base = base; + nvmem_param.offset = offset; + nvmem_param.size = size; + psa_invec invec[1] = {{&nvmem_param, sizeof(nvmem_param)}}; + psa_outvec outvec[1] = {{buffer, size}}; + + handle = pal_ipc_connect(DRIVER_NVMEM_SID, 0); + if (handle < 0) + { + return PAL_STATUS_ERROR; + } + else + { + status_of_call = pal_ipc_call(handle, invec, 1, outvec, 1); + if (status_of_call != PSA_SUCCESS) + { + pal_ipc_close(handle); + return PAL_STATUS_ERROR; + } + } + psa_close(handle); + return PAL_STATUS_SUCCESS; +} + +/** + @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 - SUCCESS/FAILURE +**/ +int pal_nvmem_write_ns(addr_t base, 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.base = base; + nvmem_param.offset = offset; + nvmem_param.size = size; + psa_invec invec[2] = {{&nvmem_param, sizeof(nvmem_param)}, {buffer, size}}; + + handle = pal_ipc_connect(DRIVER_NVMEM_SID, 0); + if (handle < 0) + { + return PAL_STATUS_ERROR; + } + else + { + status_of_call = pal_ipc_call(handle, invec, 2, NULL, 0); + if (status_of_call != PSA_SUCCESS) + { + pal_ipc_close(handle); + return PAL_STATUS_ERROR; + } + } + pal_ipc_close(handle); + return PAL_STATUS_SUCCESS; +} + +/** + * @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; +} + +/** + * @brief - Terminates the simulation at the end of all tests completion. + * By default, it put cpus into power down mode. + * @param - void + * @return - void +**/ +void pal_terminate_simulation(void) +{ + /* Add logic to terminate the simluation */ + + while(1) + { + asm volatile("WFI"); + } +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_driver_ns_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_driver_ns_intf.c new file mode 100644 index 00000000..338df6cb --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/common/pal_driver_ns_intf.c @@ -0,0 +1,145 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_uart.h" +#include "pal_nvmem.h" +#include "pal_wd_cmsdk.h" + +/** + @brief - This function initializes the UART + @param - uart base addr + @return - SUCCESS/FAILURE +**/ +int pal_uart_init_ns(uint32_t uart_base_addr) +{ + pal_uart_pl011_init(uart_base_addr); + return PAL_STATUS_SUCCESS; +} + +/** + @brief - This function parses the input string and writes bytes into UART TX FIFO + @param - str : Input String + - data : Value for format specifier + @return - SUCCESS/FAILURE +**/ + +int pal_print_ns(char *str, int32_t data) +{ + pal_uart_pl011_print(str, data); + return PAL_STATUS_SUCCESS; +} + +/** + @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_ns(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_ns(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_ns(addr_t base_addr) +{ + return (pal_wd_cmsdk_disable(base_addr)); +} + +/** + @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 - SUCCESS/FAILURE +**/ +int pal_nvmem_read_ns(addr_t base, uint32_t offset, void *buffer, int size) +{ + if (nvmem_read(base, offset, buffer, size)) + { + return PAL_STATUS_SUCCESS; + } + else + { + return PAL_STATUS_ERROR; + } +} + +/** + @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 - SUCCESS/FAILURE +**/ +int pal_nvmem_write_ns(addr_t base, uint32_t offset, void *buffer, int size) +{ + if (nvmem_write(base, offset, buffer, size)) + { + return PAL_STATUS_SUCCESS; + } + else + { + return PAL_STATUS_ERROR; + } +} + +/** + * @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; +} + +/** + * @brief - Terminates the simulation at the end of all tests completion. + * By default, it put cpus into power down mode. + * @param - void + * @return - void +**/ +void pal_terminate_simulation(void) +{ + /* Add logic to terminate the simluation */ + + while(1) + { + asm volatile("WFI"); + } +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_config.h b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_config.h new file mode 100644 index 00000000..ab11fd16 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_config.h @@ -0,0 +1,323 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +/* + * \file pal_crypto_config.h + * + * \brief Configuration options for crypto tests (set of defines) + * + * This set of compile-time options may be used to enable + * or disable features selectively for crypto test suite + */ + +#ifndef _PAL_CRYPTO_CONFIG_H_ +#define _PAL_CRYPTO_CONFIG_H_ +/** + * \def ARCH_TEST_RSA + * + * Enable the RSA public-key cryptosystem. + * By default all supported keys are enabled. + * + * Comment macros to disable the types + */ +#define ARCH_TEST_RSA +#define ARCH_TEST_RSA_1024 +#define ARCH_TEST_RSA_2048 +#define ARCH_TEST_RSA_3072 + +/** + * \def ARCH_TEST_ECC + * \def ARCH_TEST_ECC_CURVE_SECPXXXR1 + * + * Enable the elliptic curve + * Enable specific curves within the Elliptic Curve + * module. By default all supported curves are enabled. + * + * Requires: ARCH_TEST_ECC + * Comment macros to disable the curve + */ +#define ARCH_TEST_ECC +#define ARCH_TEST_ECC_CURVE_SECP192R1 +#define ARCH_TEST_ECC_CURVE_SECP224R1 +#define ARCH_TEST_ECC_CURVE_SECP256R1 +#define ARCH_TEST_ECC_CURVE_SECP384R1 + +/** + * \def ARCH_TEST_AES + * + * Enable the AES block cipher. + * By default all supported keys are enabled. + * + * Comment macros to disable the types + */ +#define ARCH_TEST_AES +#define ARCH_TEST_AES_128 +#define ARCH_TEST_AES_192 +#define ARCH_TEST_AES_256 +#define ARCH_TEST_AES_512 + +/** + * \def ARCH_TEST_DES + * + * Enable the DES block cipher. + * By default all supported keys are enabled. + * + * Comment macros to disable the types + */ +//#define ARCH_TEST_DES +//#define ARCH_TEST_DES_1KEY +//#define ARCH_TEST_DES_2KEY +//#define ARCH_TEST_DES_3KEY + +/** + * \def ARCH_TEST_RAW + * + * A "key" of this type cannot be used for any cryptographic operation. + * Applications may use this type to store arbitrary data in the keystore. + */ +#define ARCH_TEST_RAW + +/** + * \def ARCH_TEST_CIPER + * + * Enable the generic cipher layer. + */ + +#define ARCH_TEST_CIPER + +/** + * \def ARCH_TEST_ARC4 + * + * Enable the ARC4 key type. + */ +//#define ARCH_TEST_ARC4 + +/** + * \def ARCH_TEST_CIPER_MODE_CTR + * + * Enable Counter Block Cipher mode (CTR) for symmetric ciphers. + * + * Requires: ARCH_TEST_CIPER + */ +#define ARCH_TEST_CIPER_MODE_CTR + +/** + * \def ARCH_TEST_CIPER_MODE_CFB + * + * Enable Cipher Feedback mode (CFB) for symmetric ciphers. + * + * Requires: ARCH_TEST_CIPER + */ +#define ARCH_TEST_CIPER_MODE_CFB + +/** + * \def ARCH_TEST_CIPER_MODE_CBC + * + * Enable Cipher Block Chaining mode (CBC) for symmetric ciphers. + * + * Requires: ARCH_TEST_CIPER + */ +#define ARCH_TEST_CIPER_MODE_CBC + +/** + * \def ARCH_TEST_CTR_AES + * + * Requires: ARCH_TEST_CIPER, ARCH_TEST_AES, ARCH_TEST_CIPER_MODE_CTR + */ +#define ARCH_TEST_CTR_AES + +/** + * \def ARCH_TEST_CBC_AES + * + * Requires: ARCH_TEST_CIPER, ARCH_TEST_AES, ARCH_TEST_CIPER_MODE_CBC + * + * Comment macros to disable the types + */ +#define ARCH_TEST_CBC_AES +#define ARCH_TEST_CBC_AES_NO_PADDING + +/** + * \def ARCH_TEST_CBC_NO_PADDING + * + * Requires: ARCH_TEST_CIPER, ARCH_TEST_CIPER_MODE_CBC + * + * Comment macros to disable the types + */ +#define ARCH_TEST_CBC_NO_PADDING + +/** + * \def ARCH_TEST_CFB_AES + * + * Requires: ARCH_TEST_CIPER, ARCH_TEST_AES, ARCH_TEST_CIPER_MODE_CFB + */ +#define ARCH_TEST_CFB_AES + +/** + * \def ARCH_TEST_PKCS1V15_* + * + * Enable support for PKCS#1 v1.5 encoding. + * Enable support for PKCS#1 v1.5 operations. + * Enable support for RSA-OAEP + * + * Requires: ARCH_TEST_RSA, ARCH_TEST_PKCS1V15 + * + * Comment macros to disable the types + */ +#define ARCH_TEST_PKCS1V15 +#define ARCH_TEST_RSA_PKCS1V15_SIGN +#define ARCH_TEST_RSA_PKCS1V15_SIGN_RAW +#define ARCH_TEST_RSA_PKCS1V15_CRYPT +#define ARCH_TEST_RSA_OAEP + +/** + * \def ARCH_TEST_CBC_PKCS7 + * + * Requires: ARCH_TEST_CIPER_MODE_CBC + * + * Comment macros to disable the types + */ +#define ARCH_TEST_CBC_PKCS7 + +/** + * \def ARCH_TEST_ASYMMETRIC_ENCRYPTION + * + * Enable support for Asymmetric encryption algorithms + */ +#define ARCH_TEST_ASYMMETRIC_ENCRYPTION + +/** + * \def ARCH_TEST_HASH + * + * Enable the hash algorithm. + */ +#define ARCH_TEST_HASH + +/** + * \def ARCH_TEST_HMAC + * + * The key policy determines which underlying hash algorithm the key can be + * used for. + * + * Requires: ARCH_TEST_HASH + */ +#define ARCH_TEST_HMAC + +/** + * \def ARCH_TEST_MDX + * \def ARCH_TEST_SHAXXX + * + * Enable the MDX algorithm. + * Enable the SHAXXX algorithm. + * + * Requires: ARCH_TEST_HASH + * + * Comment macros to disable the types + */ +//#define ARCH_TEST_MD2 +//#define ARCH_TEST_MD4 +//#define ARCH_TEST_MD5 +//#define ARCH_TEST_RIPEMD160 +#define ARCH_TEST_SHA1 +#define ARCH_TEST_SHA224 +#define ARCH_TEST_SHA256 +#define ARCH_TEST_SHA384 +#define ARCH_TEST_SHA512 +//#define ARCH_TEST_SHA512_224 +//#define ARCH_TEST_SHA512_256 +//#define ARCH_TEST_SHA3_224 +//#define ARCH_TEST_SHA3_256 +//#define ARCH_TEST_SHA3_384 +//#define ARCH_TEST_SHA3_512 + +/** + * \def ARCH_TEST_HKDF + * + * Enable the HKDF algorithm (RFC 5869). + * + * Requires: ARCH_TEST_HASH +*/ +#define ARCH_TEST_HKDF + +/** + * \def ARCH_TEST_xMAC + * + * Enable the xMAC (Cipher/Hash/G-based Message Authentication Code) mode for block + * ciphers. + * Requires: ARCH_TEST_AES or ARCH_TEST_DES + * + * Comment macros to disable the types + */ +#define ARCH_TEST_CMAC +#define ARCH_TEST_GMAC +#define ARCH_TEST_HMAC + +/** + * \def ARCH_TEST_CCM + * + * Enable the Counter with CBC-MAC (CCM) mode for 128-bit block cipher. + * + * Requires: ARCH_TEST_AES + */ +#define ARCH_TEST_CCM + +/** + * \def ARCH_TEST_GCM + * + * Enable the Galois/Counter Mode (GCM) for AES. + * + * Requires: ARCH_TEST_AES + * + */ +#define ARCH_TEST_GCM + +/** + * \def ARCH_TEST_TRUNCATED_MAC + * + * Enable support for RFC 6066 truncated HMAC in SSL. + * + * Comment this macro to disable support for truncated HMAC in SSL + */ +#define ARCH_TEST_TRUNCATED_MAC + + +/** + * \def ARCH_TEST_ECDH + * + * Enable the elliptic curve Diffie-Hellman library. + * + * Requires: ARCH_TEST_ECC + */ +#define ARCH_TEST_ECDH + +/** + * \def ARCH_TEST_ECDSA + * + * Enable the elliptic curve DSA library. + * Requires: ARCH_TEST_ECC + */ +#define ARCH_TEST_ECDSA + +/** + * \def ARCH_TEST_DETERMINISTIC_ECDSA + * + * Enable deterministic ECDSA (RFC 6979). +*/ +#define ARCH_TEST_DETERMINISTIC_ECDSA + +#include "pal_crypto_config_check.h" + +#endif /* _PAL_CRYPTO_CONFIG_H_ */ diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_config_check.h b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_config_check.h new file mode 100644 index 00000000..f18a7852 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_config_check.h @@ -0,0 +1,223 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +**/ + +/** + * \file pal_crypto_config_check.h + * + * \brief Consistency checks for configuration options + * + */ + +#ifndef _PAL_CRYPTO_CONFIG_CHECK_H_ +#define _PAL_CRYPTO_CONFIG_CHECK_H_ + +#if defined(ARCH_TEST_RSA_1024) && !defined(ARCH_TEST_RSA) +#error "ARCH_TEST_RSA_1024 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_RSA_2048) && !defined(ARCH_TEST_RSA) +#error "ARCH_TEST_RSA_2048 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_RSA_3072) && !defined(ARCH_TEST_RSA) +#error "ARCH_TEST_RSA_3072 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_ECC_CURVE_SECP192R1) && !defined(ARCH_TEST_ECC) +#error "ARCH_TEST_ECC_CURVE_SECP192R1 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_ECC_CURVE_SECP224R1) && !defined(ARCH_TEST_ECC) +#error "ARCH_TEST_ECC_CURVE_SECP224R1 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_ECC_CURVE_SECP256R1) && !defined(ARCH_TEST_ECC) +#error "ARCH_TEST_ECC_CURVE_SECP256R1 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_ECC_CURVE_SECP384R1) && !defined(ARCH_TEST_ECC) +#error "ARCH_TEST_ECC_CURVE_SECP384R1 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_AES_128) && !defined(ARCH_TEST_AES) +#error "ARCH_TEST_AES_128 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_AES_256) && !defined(ARCH_TEST_AES) +#error "ARCH_TEST_AES_256 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_AES_512) && !defined(ARCH_TEST_AES) +#error "ARCH_TEST_AES_512 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_DES_1KEY) && !defined(ARCH_TEST_DES) +#error "ARCH_TEST_DES_1KEY defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_DES_2KEY) && !defined(ARCH_TEST_DES) +#error "ARCH_TEST_DES_2KEY defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_DES_3KEY) && !defined(ARCH_TEST_DES) +#error "ARCH_TEST_DES_3KEY defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_CIPER_MODE_CTR) && !defined(ARCH_TEST_CIPER) +#error "ARCH_TEST_CIPER_MODE_CTR defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_CIPER_MODE_CFB) && !defined(ARCH_TEST_CIPER) +#error "ARCH_TEST_CIPER_MODE_CFB defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_CIPER_MODE_CBC) && !defined(ARCH_TEST_CIPER) +#error "ARCH_TEST_CIPER_MODE_CBC defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_CTR_AES) &&\ + (!defined(ARCH_TEST_CIPER) || !defined(ARCH_TEST_AES) || !defined(ARCH_TEST_CIPER_MODE_CTR)) +#error "ARCH_TEST_CTR_AES defined, but not all prerequisites" +#endif + +#if (defined(ARCH_TEST_CBC_AES)|| defined(ARCH_TEST_CBC_AES_NO_PADDING)) &&\ + (!defined(ARCH_TEST_CIPER) || !defined(ARCH_TEST_AES) || !defined(ARCH_TEST_CIPER_MODE_CBC)) +#error "ARCH_TEST_CBC_AES defined, but not all prerequisites" +#endif + +#if (defined(ARCH_TEST_CBC_NO_PADDING)) &&\ + (!defined(ARCH_TEST_CIPER) ||!defined(ARCH_TEST_CIPER_MODE_CBC)) +#error "ARCH_TEST_CBC_NO_PADDING defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_CFB_AES) &&\ + (!defined(ARCH_TEST_CIPER) || !defined(ARCH_TEST_AES) || !defined(ARCH_TEST_CIPER_MODE_CFB)) +#error "ARCH_TEST_CFB_AES defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_RSA_PKCS1V15_SIGN) &&\ + (!defined(ARCH_TEST_RSA) || !defined(ARCH_TEST_PKCS1V15)) +#error "ARCH_TEST_RSA_PKCS1V15_SIGN defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_RSA_PKCS1V15_SIGN_RAW) &&\ + (!defined(ARCH_TEST_RSA) || !defined(ARCH_TEST_PKCS1V15)) +#error "ARCH_TEST_RSA_PKCS1V15_SIGN_RAW defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_RSA_PKCS1V15_CRYPT) &&\ + (!defined(ARCH_TEST_RSA) || !defined(ARCH_TEST_PKCS1V15)) +#error "ARCH_TEST_RSA_PKCS1V15_CRYPT defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_CBC_PKCS7) && !defined(ARCH_TEST_CIPER_MODE_CBC) +#error "ARCH_TEST_CBC_PKCS7 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_HMAC) && !defined(ARCH_TEST_HASH) +#error "ARCH_TEST_HMAC defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_MD2) && !defined(ARCH_TEST_HASH) +#error "ARCH_TEST_MD2 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_MD4) && !defined(ARCH_TEST_HASH) +#error "ARCH_TEST_MD4 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_MD5) && !defined(ARCH_TEST_HASH) +#error "ARCH_TEST_MD5 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_RIPEMD160) && !defined(ARCH_TEST_HASH) +#error "ARCH_TEST_RIPEMD160 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_SHA1) && !defined(ARCH_TEST_HASH) +#error "ARCH_TEST_SHA1 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_SHA224) && !defined(ARCH_TEST_HASH) +#error "ARCH_TEST_SHA224 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_SHA256) && !defined(ARCH_TEST_HASH) +#error "ARCH_TEST_SHA256 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_SHA512) && !defined(ARCH_TEST_HASH) +#error "ARCH_TEST_SHA512 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_SHA512_224) && !defined(ARCH_TEST_HASH) +#error "ARCH_TEST_SHA512_224 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_SHA512_256) && !defined(ARCH_TEST_HASH) +#error "ARCH_TEST_SHA512_256 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_SHA3_224) && !defined(ARCH_TEST_HASH) +#error "ARCH_TEST_SHA3_224 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_SHA3_256) && !defined(ARCH_TEST_HASH) +#error "ARCH_TEST_SHA3_256 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_SHA3_384) && !defined(ARCH_TEST_HASH) +#error "ARCH_TEST_SHA3_256 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_SHA3_512) && !defined(ARCH_TEST_HASH) +#error "ARCH_TEST_SHA3_256 defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_HKDF) && !defined(ARCH_TEST_HASH) +#error "ARCH_TEST_HKDF defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_CMAC) && !defined(ARCH_TEST_AES) +#error "ARCH_TEST_CMAC defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_GMAC) && !defined(ARCH_TEST_AES) +#error "ARCH_TEST_GMAC defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_HMAC) && !defined(ARCH_TEST_AES) +#error "ARCH_TEST_HMAC defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_CCM) && !defined(ARCH_TEST_AES) +#error "ARCH_TEST_CCM defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_GCM) && !defined(ARCH_TEST_AES) +#error "ARCH_TEST_GCM defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_ECDH) && !defined(ARCH_TEST_ECC) +#error "ARCH_TEST_ECDH defined, but not all prerequisites" +#endif + +#if defined(ARCH_TEST_ECDSA) && !defined(ARCH_TEST_ECC) +#error "ARCH_TEST_ECDSA defined, but not all prerequisites" +#endif + +#endif /* _PAL_CRYPTO_CONFIG_CHECK_H_ */ diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_empty_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_empty_intf.c new file mode 100644 index 00000000..2a28f397 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_empty_intf.c @@ -0,0 +1,30 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include "pal_common.h" + +/** + @brief - This API will call the requested crypto function + @param - type : function code + valist : variable argument list + @return - error status +**/ +int32_t pal_crypto_function(int type, va_list valist) +{ + return PAL_STATUS_ERROR; +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_intf.c new file mode 100644 index 00000000..3df6aa8d --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_intf.c @@ -0,0 +1,340 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_crypto_intf.h" + +#define PAL_KEY_SLOT_COUNT 32 + +/** + @brief - This API will call the requested crypto function + @param - type : function code + valist : variable argument list + @return - error status +**/ +int32_t pal_crypto_function(int type, va_list valist) +{ + int i; + size_t size, *length, salt_length, label_length, ciphertext_size; + uint8_t *buffer, *ciphertext; + const uint8_t *salt, *label, *nonce, *additional_data; + uint8_t *plaintext; + uint32_t status; + const void *extra; + size_t extra_size, capacity, *gen_cap, nonce_length, additional_data_length; + psa_key_handle_t handle, *key_handle, target_handle; + 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_out; + psa_algorithm_t alg, *alg_out; + psa_hash_operation_t *hash_operation; + psa_mac_operation_t *mac_operation; + psa_cipher_operation_t *cipher_operation; + psa_crypto_generator_t *generator; + + 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: + handle = (psa_key_handle_t)va_arg(valist, int); + key_type = va_arg(valist, psa_key_type_t); + buffer = va_arg(valist, uint8_t*); + size = va_arg(valist, int); + status = psa_import_key(handle, key_type, buffer, size); + return status; + case PAL_CRYPTO_EXPORT_KEY: + handle = (psa_key_handle_t)va_arg(valist, int); + 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(handle, buffer, size, length); + return status; + case PAL_CRYPTO_EXPORT_PUBLIC_KEY: + handle = (psa_key_handle_t)va_arg(valist, int); + 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(handle, buffer, size, length); + return status; + case PAL_CRYPTO_KEY_POLICY_INIT: + policy = va_arg(valist, psa_key_policy_t*); + memset(policy, 0, sizeof(psa_key_policy_t)); + 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: + handle = (psa_key_handle_t)va_arg(valist, int); + policy = va_arg(valist, psa_key_policy_t*); + return psa_set_key_policy(handle, policy); + case PAL_CRYPTO_DESTROY_KEY: + handle = (psa_key_handle_t)va_arg(valist, int); + status = psa_destroy_key(handle); + return status; + case PAL_CRYPTO_GET_KEY_INFORMATION: + handle = (psa_key_handle_t)va_arg(valist, int); + key_type_out = va_arg(valist, psa_key_type_t*); + length = (size_t *)va_arg(valist, size_t*); + status = psa_get_key_information(handle, key_type_out, length); + return status; + case PAL_CRYPTO_GET_KEY_POLICY: + handle = (psa_key_handle_t)va_arg(valist, int); + policy = va_arg(valist, psa_key_policy_t*); + return psa_get_key_policy(handle, 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: + handle = (psa_key_handle_t)va_arg(valist, int); + lifetime_out = va_arg(valist, psa_key_lifetime_t*); + return psa_get_key_lifetime(handle, lifetime_out); + case PAL_CRYPTO_HASH_SETUP: + hash_operation = va_arg(valist, psa_hash_operation_t*); + alg = va_arg(valist, psa_algorithm_t); + return psa_hash_setup(hash_operation, alg); + case PAL_CRYPTO_HASH_UPDATE: + hash_operation = va_arg(valist, psa_hash_operation_t*); + buffer = va_arg(valist, uint8_t*); + size = va_arg(valist, size_t); + return psa_hash_update(hash_operation, buffer, size); + case PAL_CRYPTO_HASH_VERIFY: + hash_operation = va_arg(valist, psa_hash_operation_t*); + buffer = va_arg(valist, uint8_t*); + size = va_arg(valist, size_t); + return psa_hash_verify(hash_operation, buffer, size); + case PAL_CRYPTO_HASH_FINISH: + hash_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(hash_operation, buffer, size, length); + case PAL_CRYPTO_HASH_ABORT: + hash_operation = va_arg(valist, psa_hash_operation_t*); + return psa_hash_abort(hash_operation); + case PAL_CRYPTO_GENERATE_KEY: + handle = (psa_key_handle_t)va_arg(valist, int); + key_type = va_arg(valist, psa_key_type_t); + size = va_arg(valist, size_t); + extra = va_arg(valist, const void*); + extra_size = va_arg(valist, size_t); + return psa_generate_key(handle, key_type, size, extra, extra_size); + case PAL_CRYPTO_GENERATOR_READ: + generator = va_arg(valist, psa_crypto_generator_t*); + buffer = va_arg(valist, uint8_t*); + size = va_arg(valist, int); + return psa_generator_read(generator, buffer, size); + case PAL_CRYPTO_KEY_DERIVATION: + generator = va_arg(valist, psa_crypto_generator_t*); + handle = (psa_key_handle_t)va_arg(valist, int); + alg = va_arg(valist, psa_algorithm_t); + salt = va_arg(valist, const uint8_t *); + salt_length = va_arg(valist, size_t); + label = va_arg(valist, const uint8_t *); + label_length = va_arg(valist, size_t); + capacity = va_arg(valist, size_t); + return psa_key_derivation(generator, handle, alg, salt, salt_length, label, + label_length, capacity); + case PAL_CRYPTO_GET_GENERATOR_CAPACITY: + generator = va_arg(valist, psa_crypto_generator_t*); + gen_cap = va_arg(valist, size_t*); + return psa_get_generator_capacity(generator, gen_cap); + case PAL_CRYPTO_GENERATOR_IMPORT_KEY: + handle = (psa_key_handle_t)va_arg(valist, int); + key_type = va_arg(valist, psa_key_type_t); + size = va_arg(valist, size_t); + generator = va_arg(valist, psa_crypto_generator_t*); + return psa_generator_import_key(handle, key_type, size, generator); + case PAL_CRYPTO_GENERATOR_ABORT: + generator = va_arg(valist, psa_crypto_generator_t*); + return psa_generator_abort(generator); + case PAL_CRYPTO_AEAD_ENCRYPT: + handle = (psa_key_handle_t)va_arg(valist, int); + alg = va_arg(valist, psa_algorithm_t); + nonce = va_arg(valist, const uint8_t *); + nonce_length = va_arg(valist, size_t); + additional_data = va_arg(valist, const uint8_t *); + additional_data_length = va_arg(valist, size_t); + plaintext = va_arg(valist, uint8_t *); + size = va_arg(valist, size_t); + ciphertext = va_arg(valist, uint8_t *); + ciphertext_size = va_arg(valist, size_t); + length = va_arg(valist, size_t*); + return psa_aead_encrypt(handle, alg, nonce, nonce_length, additional_data, + additional_data_length, plaintext, size, ciphertext, ciphertext_size, length); + case PAL_CRYPTO_AEAD_DECRYPT: + handle = (psa_key_handle_t)va_arg(valist, int); + alg = va_arg(valist, psa_algorithm_t); + nonce = va_arg(valist, const uint8_t *); + nonce_length = va_arg(valist, size_t); + additional_data = va_arg(valist, const uint8_t *); + additional_data_length = va_arg(valist, size_t); + ciphertext = va_arg(valist, uint8_t *); + ciphertext_size = va_arg(valist, size_t); + plaintext = va_arg(valist, uint8_t *); + size = va_arg(valist, size_t); + length = va_arg(valist, size_t*); + return psa_aead_decrypt(handle, alg, nonce, nonce_length, additional_data, + additional_data_length, ciphertext, ciphertext_size, plaintext, size, length); + case PAL_CRYPTO_MAC_SIGN_SETUP: + mac_operation = va_arg(valist, psa_mac_operation_t*); + handle = (psa_key_handle_t)va_arg(valist, int); + alg = va_arg(valist, psa_algorithm_t); + return psa_mac_sign_setup(mac_operation, handle, alg); + case PAL_CRYPTO_MAC_UPDATE: + mac_operation = va_arg(valist, psa_mac_operation_t*); + buffer = va_arg(valist, uint8_t*); + size = va_arg(valist, size_t); + return psa_mac_update(mac_operation, buffer, size); + case PAL_CRYPTO_MAC_SIGN_FINISH: + mac_operation = va_arg(valist, psa_mac_operation_t*); + buffer = va_arg(valist, uint8_t*); + size = va_arg(valist, size_t); + length = (size_t *)va_arg(valist, size_t*); + return psa_mac_sign_finish(mac_operation, buffer, size, length); + case PAL_CRYPTO_MAC_VERIFY_SETUP: + mac_operation = va_arg(valist, psa_mac_operation_t*); + handle = (psa_key_handle_t)va_arg(valist, int); + alg = va_arg(valist, psa_algorithm_t); + return psa_mac_verify_setup(mac_operation, handle, alg); + case PAL_CRYPTO_MAC_VERIFY_FINISH: + mac_operation = va_arg(valist, psa_mac_operation_t*); + buffer = va_arg(valist, uint8_t*); + size = va_arg(valist, size_t); + return psa_mac_verify_finish(mac_operation, buffer, size); + case PAL_CRYPTO_MAC_ABORT: + mac_operation = va_arg(valist, psa_mac_operation_t*); + return psa_mac_abort(mac_operation); + case PAL_CRYPTO_ASYMMTERIC_ENCRYPT: + handle = (psa_key_handle_t)va_arg(valist, int); + alg = va_arg(valist, psa_algorithm_t); + plaintext = va_arg(valist, uint8_t *); + size = va_arg(valist, size_t); + salt = va_arg(valist, const uint8_t *); + salt_length = va_arg(valist, size_t); + ciphertext = va_arg(valist, uint8_t *); + ciphertext_size = va_arg(valist, size_t); + length = va_arg(valist, size_t*); + return psa_asymmetric_encrypt(handle, alg, plaintext, size, salt, salt_length, + ciphertext, ciphertext_size, length); + case PAL_CRYPTO_ASYMMTERIC_DECRYPT: + handle = (psa_key_handle_t)va_arg(valist, int); + alg = va_arg(valist, psa_algorithm_t); + plaintext = va_arg(valist, uint8_t *); + size = va_arg(valist, size_t); + salt = va_arg(valist, const uint8_t *); + salt_length = va_arg(valist, size_t); + ciphertext = va_arg(valist, uint8_t *); + ciphertext_size = va_arg(valist, size_t); + length = va_arg(valist, size_t*); + return psa_asymmetric_decrypt(handle, alg, plaintext, size, salt, salt_length, + ciphertext, ciphertext_size, length); + case PAL_CRYPTO_CIPHER_ENCRYPT_SETUP: + cipher_operation = va_arg(valist, psa_cipher_operation_t *); + handle = (psa_key_handle_t)va_arg(valist, int); + alg = va_arg(valist, psa_algorithm_t); + return psa_cipher_encrypt_setup(cipher_operation, handle, alg); + case PAL_CRYPTO_CIPHER_DECRYPT_SETUP: + cipher_operation = va_arg(valist, psa_cipher_operation_t *); + handle = (psa_key_handle_t)va_arg(valist, int); + alg = va_arg(valist, psa_algorithm_t); + return psa_cipher_decrypt_setup(cipher_operation, handle, alg); + case PAL_CRYPTO_CIPHER_GENERATE_IV: + cipher_operation = va_arg(valist, psa_cipher_operation_t *); + buffer = va_arg(valist, uint8_t*); + size = va_arg(valist, size_t); + length = va_arg(valist, size_t*); + return psa_cipher_generate_iv(cipher_operation, buffer, size, length); + case PAL_CRYPTO_CIPHER_SET_IV: + cipher_operation = va_arg(valist, psa_cipher_operation_t *); + buffer = va_arg(valist, uint8_t*); + size = va_arg(valist, size_t); + return psa_cipher_set_iv(cipher_operation, buffer, size); + case PAL_CRYPTO_CIPHER_UPDATE: + cipher_operation = va_arg(valist, psa_cipher_operation_t *); + plaintext = va_arg(valist, uint8_t *); + size = va_arg(valist, size_t); + ciphertext = va_arg(valist, uint8_t *); + ciphertext_size = va_arg(valist, size_t); + length = va_arg(valist, size_t*); + return psa_cipher_update(cipher_operation, plaintext, size, ciphertext, ciphertext_size, + length); + case PAL_CRYPTO_CIPHER_FINISH: + cipher_operation = va_arg(valist, psa_cipher_operation_t *); + ciphertext = va_arg(valist, uint8_t *); + ciphertext_size = va_arg(valist, size_t); + length = va_arg(valist, size_t*); + return psa_cipher_finish(cipher_operation, ciphertext, ciphertext_size, length); + case PAL_CRYPTO_CIPHER_ABORT: + cipher_operation = va_arg(valist, psa_cipher_operation_t *); + return psa_cipher_abort(cipher_operation); + case PAL_CRYPTO_ASYMMTERIC_SIGN: + handle = (psa_key_handle_t)va_arg(valist, int); + alg = va_arg(valist, psa_algorithm_t); + buffer = va_arg(valist, uint8_t*); + size = va_arg(valist, size_t); + ciphertext = va_arg(valist, uint8_t *); + ciphertext_size = va_arg(valist, size_t); + length = va_arg(valist, size_t*); + return psa_asymmetric_sign(handle, alg, buffer, size, ciphertext, ciphertext_size, + length); + case PAL_CRYPTO_ASYMMTERIC_VERIFY: + handle = (psa_key_handle_t)va_arg(valist, int); + alg = va_arg(valist, psa_algorithm_t); + buffer = va_arg(valist, uint8_t*); + size = va_arg(valist, size_t); + ciphertext = va_arg(valist, uint8_t *); + ciphertext_size = va_arg(valist, size_t); + return psa_asymmetric_verify(handle, alg, buffer, size, ciphertext, ciphertext_size); + case PAL_CRYPTO_KEY_AGREEMENT: + generator = va_arg(valist, psa_crypto_generator_t*); + handle = (psa_key_handle_t)va_arg(valist, int); + buffer = va_arg(valist, uint8_t*); + size = va_arg(valist, size_t); + alg = va_arg(valist, psa_algorithm_t); + return psa_key_agreement(generator, handle, buffer, size, alg); + case PAL_CRYPTO_ALLOCATE_KEY: + key_handle = (psa_key_handle_t *)va_arg(valist, int*); + return psa_allocate_key(key_handle); + case PAL_CRYPTO_COPY_KEY: + handle = (psa_key_handle_t)va_arg(valist, int); + target_handle = (psa_key_handle_t)va_arg(valist, int); + policy = va_arg(valist, psa_key_policy_t*); + return psa_copy_key(handle, target_handle, policy); + case PAL_CRYPTO_FREE: + for (i = 0; i < PAL_KEY_SLOT_COUNT; i++) + psa_destroy_key(i); + return 0; + default: + return PAL_STATUS_UNSUPPORTED_FUNC; + } +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_intf.h b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_intf.h new file mode 100644 index 00000000..d1dabfa4 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/crypto/pal_crypto_intf.h @@ -0,0 +1,76 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "pal_common.h" + +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_HASH_SETUP = 0x11, + PAL_CRYPTO_HASH_UPDATE = 0x12, + PAL_CRYPTO_HASH_VERIFY = 0x13, + PAL_CRYPTO_HASH_FINISH = 0x14, + PAL_CRYPTO_HASH_ABORT = 0x15, + PAL_CRYPTO_GENERATE_KEY = 0x16, + PAL_CRYPTO_GENERATOR_READ = 0x17, + PAL_CRYPTO_KEY_DERIVATION = 0x18, + PAL_CRYPTO_GET_GENERATOR_CAPACITY = 0x19, + PAL_CRYPTO_GENERATOR_IMPORT_KEY = 0x1A, + PAL_CRYPTO_GENERATOR_ABORT = 0x1B, + PAL_CRYPTO_AEAD_ENCRYPT = 0x1C, + PAL_CRYPTO_AEAD_DECRYPT = 0x1D, + PAL_CRYPTO_MAC_SIGN_SETUP = 0x1E, + PAL_CRYPTO_MAC_UPDATE = 0x1F, + PAL_CRYPTO_MAC_SIGN_FINISH = 0x20, + PAL_CRYPTO_MAC_VERIFY_SETUP = 0x21, + PAL_CRYPTO_MAC_VERIFY_FINISH = 0x22, + PAL_CRYPTO_MAC_ABORT = 0x23, + PAL_CRYPTO_ASYMMTERIC_ENCRYPT = 0x24, + PAL_CRYPTO_ASYMMTERIC_DECRYPT = 0x25, + PAL_CRYPTO_CIPHER_ENCRYPT_SETUP = 0x26, + PAL_CRYPTO_CIPHER_DECRYPT_SETUP = 0x2A, + PAL_CRYPTO_CIPHER_GENERATE_IV = 0x2B, + PAL_CRYPTO_CIPHER_SET_IV = 0x2C, + PAL_CRYPTO_CIPHER_UPDATE = 0x2D, + PAL_CRYPTO_CIPHER_FINISH = 0x2E, + PAL_CRYPTO_CIPHER_ABORT = 0x2F, + PAL_CRYPTO_ASYMMTERIC_SIGN = 0x30, + PAL_CRYPTO_ASYMMTERIC_VERIFY = 0x31, + PAL_CRYPTO_KEY_AGREEMENT = 0x32, + PAL_CRYPTO_ALLOCATE_KEY = 0x33, + PAL_CRYPTO_COPY_KEY = 0x34, + PAL_CRYPTO_FREE = 0xFE, +}; + +int32_t pal_crypto_function(int type, va_list valist); +#endif /* _PAL_CRYPTO_H_ */ diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_crypto.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_crypto.c new file mode 100644 index 00000000..ae2bdba4 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_crypto.c @@ -0,0 +1,346 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_attestation_crypto.h" + +static uint32_t public_key_registered = 0; + +static inline struct q_useful_buf_c useful_buf_head(struct q_useful_buf_c buf, + size_t amount) +{ + return UsefulBuf_Head(buf, amount); +} + +static uint32_t check_hash_sizes(void) +{ + if (T_COSE_CRYPTO_SHA256_SIZE != PSA_HASH_SIZE(PSA_ALG_SHA_256)) + { + return PAL_ATTEST_HASH_FAIL; + } + + return PAL_ATTEST_SUCCESS; +} + +static psa_ecc_curve_t attest_map_elliptic_curve_type(int32_t cose_curve) +{ + psa_ecc_curve_t psa_curve; + + /*FixMe: Mapping is not complete, missing ones: P384, P521, ED25519, ED448 */ + switch (cose_curve) + { + case P_256: + psa_curve = PSA_ECC_CURVE_SECP256R1; + break; + default: + psa_curve = USHRT_MAX; + } + + return psa_curve; +} + +static psa_algorithm_t cose_hash_alg_id_to_psa(int32_t cose_hash_alg_id) +{ + psa_algorithm_t status; + + switch (cose_hash_alg_id) + { + case COSE_ALG_SHA256_PROPRIETARY: + status = PSA_ALG_SHA_256; + break; + default: + status = PSA_ALG_MD4; + break; + } + + return status; +} + +static int32_t hash_alg_id_from_sig_alg_id(int32_t cose_sig_alg_id) +{ + switch (cose_sig_alg_id) + { + case COSE_ALGORITHM_ES256: + return COSE_ALG_SHA256_PROPRIETARY; + default: + return INT32_MAX; + } +} + +int32_t pal_cose_crypto_hash_start(struct pal_cose_crypto_hash *hash_ctx, int32_t cose_hash_alg_id) +{ + int32_t cose_ret = PAL_ATTEST_SUCCESS; + psa_status_t psa_ret; + struct pal_cose_psa_crypto_hash *psa_hash_ctx; + + cose_ret = check_hash_sizes(); + if (cose_ret) + { + goto error; + } + + if (sizeof(struct pal_cose_crypto_hash) < sizeof(struct pal_cose_psa_crypto_hash)) + { + cose_ret = PAL_ATTEST_HASH_FAIL; + goto error; + } + + psa_hash_ctx = (struct pal_cose_psa_crypto_hash *)hash_ctx; + psa_ret = psa_hash_setup(&psa_hash_ctx->operation, cose_hash_alg_id_to_psa(cose_hash_alg_id)); + + if (psa_ret == PAL_ATTEST_SUCCESS) + { + psa_hash_ctx->status = PAL_ATTEST_SUCCESS; + cose_ret = PAL_ATTEST_SUCCESS; + } + else if (psa_ret == PSA_ERROR_NOT_SUPPORTED) + { + cose_ret = PAL_ATTEST_HASH_UNSUPPORTED; + } + else + { + cose_ret = PAL_ATTEST_HASH_FAIL; + } + +error: + return cose_ret; +} + +void pal_cose_crypto_hash_update(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf_c data_to_hash) +{ + struct pal_cose_psa_crypto_hash *psa_hash_ctx; + + if (sizeof(struct pal_cose_crypto_hash) < sizeof(struct pal_cose_psa_crypto_hash)) + { + return; + } + + psa_hash_ctx = (struct pal_cose_psa_crypto_hash *)hash_ctx; + + if (psa_hash_ctx->status == PAL_ATTEST_SUCCESS) + { + if (data_to_hash.ptr != NULL) + { + psa_hash_ctx->status = psa_hash_update(&psa_hash_ctx->operation, + data_to_hash.ptr, + data_to_hash.len); + } + else + { + /* Intentionally do nothing, just computing the size of the token */ + } + } +} + +int32_t pal_cose_crypto_hash_finish(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf buffer_to_hold_result, + struct q_useful_buf_c *hash_result) +{ + uint32_t cose_ret = PAL_ATTEST_SUCCESS; + psa_status_t psa_ret; + struct pal_cose_psa_crypto_hash *psa_hash_ctx; + + if (sizeof(struct pal_cose_crypto_hash) < sizeof(struct pal_cose_psa_crypto_hash)) + { + cose_ret = PAL_ATTEST_HASH_FAIL; + goto error; + } + + psa_hash_ctx = (struct pal_cose_psa_crypto_hash *)hash_ctx; + + if (psa_hash_ctx->status == PAL_ATTEST_SUCCESS) + { + psa_ret = psa_hash_finish(&psa_hash_ctx->operation, + buffer_to_hold_result.ptr, + buffer_to_hold_result.len, + &(hash_result->len)); + + if (psa_ret == PAL_ATTEST_SUCCESS) + { + hash_result->ptr = buffer_to_hold_result.ptr; + cose_ret = 0; + } + else if (psa_ret == PSA_ERROR_BUFFER_TOO_SMALL) + { + cose_ret = PAL_ATTEST_HASH_BUFFER_SIZE; + } + else + { + cose_ret = PAL_ATTEST_HASH_FAIL; + } + } + else + { + cose_ret = PAL_ATTEST_HASH_FAIL; + } + +error: + return cose_ret; +} + +int pal_create_sha256(struct q_useful_buf_c bytes_to_hash, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash) +{ + uint32_t status = PAL_ATTEST_SUCCESS; + struct pal_cose_crypto_hash hash_ctx; + + status = pal_cose_crypto_hash_start(&hash_ctx, COSE_ALG_SHA256_PROPRIETARY); + if (status) + return status; + + pal_cose_crypto_hash_update(&hash_ctx, bytes_to_hash); + status = pal_cose_crypto_hash_finish(&hash_ctx, buffer_for_hash, hash); + + return status; +} + +uint32_t pal_compute_hash(int32_t cose_alg_id, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash, struct q_useful_buf_c protected_headers, + struct q_useful_buf_c payload) +{ + uint32_t status; + QCBOREncodeContext cbor_encode_ctx; + struct q_useful_buf_c tbs_first_part; + QCBORError qcbor_result; + struct pal_cose_crypto_hash hash_ctx = {{0}}; + int32_t hash_alg_id; + UsefulBuf_MAKE_STACK_UB (buffer_for_TBS_first_part, T_COSE_SIZE_OF_TBS); + + /* This builds the CBOR-format to-be-signed bytes */ + QCBOREncode_Init(&cbor_encode_ctx, buffer_for_TBS_first_part); + QCBOREncode_OpenArray(&cbor_encode_ctx); + /* context */ + QCBOREncode_AddSZString(&cbor_encode_ctx, + COSE_SIG_CONTEXT_STRING_SIGNATURE1); + /* body_protected */ + QCBOREncode_AddBytes(&cbor_encode_ctx, + protected_headers); + /* sign_protected */ + QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_USEFUL_BUF_C); + /* external_aad */ + QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_USEFUL_BUF_C); + /* fake payload */ + QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_USEFUL_BUF_C); + QCBOREncode_CloseArray(&cbor_encode_ctx); + + /* Get the result and convert it to struct q_useful_buf_c representation */ + qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &tbs_first_part); + if (qcbor_result) + { + /* Mainly means that the protected_headers were too big + (which should never happen) */ + status = PAL_ATTEST_ERR_SIGN_STRUCT; + goto Done; + } + + /* Start the hashing */ + hash_alg_id = hash_alg_id_from_sig_alg_id(cose_alg_id); + + /* Don't check hash_alg_id for failure. pal_cose_crypto_hash_start() + * will handle it properly + */ + status = pal_cose_crypto_hash_start(&hash_ctx, hash_alg_id); + if (status) + goto Done; + + /* Hash the first part of the TBS. Take all but the last two + * bytes. The last two bytes are the fake payload from above. It + * is replaced by the real payload which is hashed next. The fake + * payload is needed so the array count is right. This is one of + * the main things that make it possible to implement with one + * buffer for the whole cose sign1. + */ + pal_cose_crypto_hash_update(&hash_ctx, useful_buf_head(tbs_first_part, + tbs_first_part.len - 2)); + + /* Hash the payload */ + pal_cose_crypto_hash_update(&hash_ctx, payload); + + /* Finish the hash and set up to return it */ + status = pal_cose_crypto_hash_finish(&hash_ctx, + buffer_for_hash, + hash); + +Done: + return status; +} + +uint32_t pal_import_attest_key(int32_t alg) +{ + psa_key_type_t attest_key_type; + size_t public_key_size; + psa_status_t status = PSA_SUCCESS; + psa_key_policy_t policy; + psa_ecc_curve_t psa_curve; + psa_key_handle_t public_key_handle; + + /* Mapping of COSE curve type to PSA curve types */ + psa_curve = attest_map_elliptic_curve_type(P_256); + if (psa_curve == USHRT_MAX) + return PAL_ATTEST_ERROR; + + /* Setup the key policy for public key */ + policy = psa_key_policy_init(); + psa_key_policy_set_usage(&policy, PSA_KEY_USAGE_VERIFY, alg); + + status = psa_allocate_key(&public_key_handle); + if (status != PSA_SUCCESS) + return status; + + status = psa_set_key_policy(public_key_handle, &policy); + if (status != PSA_SUCCESS) + return status; + + attest_key_type = PSA_KEY_TYPE_ECC_PUBLIC_KEY(psa_curve); + + /* Register public key to crypto service */ + public_key_size = attest_key.pubx_key_size + attest_key.puby_key_size; + + status = psa_import_key(public_key_handle, + attest_key_type, + (const uint8_t *)&attest_public_key, + public_key_size + 1); + + return status; +} + + +uint32_t pal_crypto_pub_key_verify(int32_t cose_algorithm_id, + struct q_useful_buf_c token_hash, + struct q_useful_buf_c signature) +{ + uint32_t status = PAL_ATTEST_SUCCESS; + + if (!public_key_registered) + { + status = pal_import_attest_key(cose_algorithm_id); + if (status != PAL_ATTEST_SUCCESS) + return status; + + public_key_registered = 1; + } + +/* + * Enable the verify function when Trusted Firmare - M Supports + + * Verify the signature a hash or short message using a public key. + status = psa_asymmetric_verify(public_key_handle, + cose_algorithm_id, token_hash.ptr, token_hash.len, + signature.ptr, signature.len); +*/ + return status; +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_crypto.h b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_crypto.h new file mode 100644 index 00000000..2d63ad13 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_crypto.h @@ -0,0 +1,102 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_attestation_eat.h" + +#define ATTEST_PUBLIC_KEY_SLOT 4 + +typedef struct{ + uint8_t *pubx_key; + uint32_t pubx_key_size; + uint8_t *puby_key; + uint32_t puby_key_size; +} ecc_key_t; + +struct ecc_public_key_t { + const uint8_t a; + uint8_t public_key[]; /* X-coordinate || Y-coordinate */ +}; + +static const struct ecc_public_key_t attest_public_key = { + /* Constant byte */ + 0x04, + /* X-coordinate */ + {0x79, 0xEB, 0xA9, 0x0E, 0x8B, 0xF4, 0x50, 0xA6, + 0x75, 0x15, 0x76, 0xAD, 0x45, 0x99, 0xB0, 0x7A, + 0xDF, 0x93, 0x8D, 0xA3, 0xBB, 0x0B, 0xD1, 0x7D, + 0x00, 0x36, 0xED, 0x49, 0xA2, 0xD0, 0xFC, 0x3F, + /* Y-coordinate */ + 0xBF, 0xCD, 0xFA, 0x89, 0x56, 0xB5, 0x68, 0xBF, + 0xDB, 0x86, 0x73, 0xE6, 0x48, 0xD8, 0xB5, 0x8D, + 0x92, 0x99, 0x55, 0xB1, 0x4A, 0x26, 0xC3, 0x08, + 0x0F, 0x34, 0x11, 0x7D, 0x97, 0x1D, 0x68, 0x64}, +}; + +struct pal_cose_crypto_hash { + /* Can't put the actual size here without creating dependecy on + * actual hash implementation, so this is a fairly large and + * accommodating size. + */ + uint8_t bytes[128]; +}; + +struct pal_cose_psa_crypto_hash { + psa_status_t status; + psa_hash_operation_t operation; +}; + +static const uint8_t initial_attestation_public_x_key[] = +{ + 0x79, 0xEB, 0xA9, 0x0E, 0x8B, 0xF4, 0x50, 0xA6, + 0x75, 0x15, 0x76, 0xAD, 0x45, 0x99, 0xB0, 0x7A, + 0xDF, 0x93, 0x8D, 0xA3, 0xBB, 0x0B, 0xD1, 0x7D, + 0x00, 0x36, 0xED, 0x49, 0xA2, 0xD0, 0xFC, 0x3F +}; + +static const uint8_t initial_attestation_public_y_key[] = +{ + 0xBF, 0xCD, 0xFA, 0x89, 0x56, 0xB5, 0x68, 0xBF, + 0xDB, 0x86, 0x73, 0xE6, 0x48, 0xD8, 0xB5, 0x8D, + 0x92, 0x99, 0x55, 0xB1, 0x4A, 0x26, 0xC3, 0x08, + 0x0F, 0x34, 0x11, 0x7D, 0x97, 0x1D, 0x68, 0x64 +}; + +/* Initialize the structure with given public key */ +static const ecc_key_t attest_key = { + (uint8_t *)initial_attestation_public_x_key, + sizeof(initial_attestation_public_x_key), + (uint8_t *)initial_attestation_public_y_key, + sizeof(initial_attestation_public_y_key) +}; + +int32_t pal_cose_crypto_hash_start(struct pal_cose_crypto_hash *hash_ctx, int32_t cose_hash_alg_id); +void pal_cose_crypto_hash_update(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf_c data_to_hash); +int32_t pal_cose_crypto_hash_finish(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf buffer_to_hold_result, + struct q_useful_buf_c *hash_result); +int pal_create_sha256(struct q_useful_buf_c bytes_to_hash, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash); +uint32_t pal_compute_hash(int32_t cose_alg_id, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash, struct q_useful_buf_c protected_headers, + struct q_useful_buf_c payload); +uint32_t pal_import_attest_key(int32_t alg); +uint32_t pal_crypto_pub_key_verify(int32_t cose_algorithm_id, struct q_useful_buf_c token_hash, + struct q_useful_buf_c signature); + + diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_eat.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_eat.c new file mode 100644 index 00000000..178fdc9c --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_eat.c @@ -0,0 +1,491 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_attestation_crypto.h" + +uint32_t mandatory_claims = 0; +uint32_t mandaroty_sw_components = 0; +bool_t sw_component_present = 0; + +static int pal_encode_cose_key(struct q_useful_buf_c *cose_key, + struct q_useful_buf buffer_for_cose_key, + struct q_useful_buf_c x_cord, struct q_useful_buf_c y_cord) +{ + uint32_t return_value; + QCBORError qcbor_result; + QCBOREncodeContext cbor_encode_ctx; + int32_t cose_curve_id = P_256; + struct q_useful_buf_c encoded_key_id; + + /* Get the public key x and y */ + /* Encode it into a COSE_Key structure */ + QCBOREncode_Init(&cbor_encode_ctx, buffer_for_cose_key); + QCBOREncode_OpenMap(&cbor_encode_ctx); + QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx, + COSE_KEY_COMMON_KTY, + COSE_KEY_TYPE_EC2); + QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx, + COSE_KEY_PARAM_CRV, + cose_curve_id); + QCBOREncode_AddBytesToMapN(&cbor_encode_ctx, + COSE_KEY_PARAM_X_COORDINATE, + x_cord); + QCBOREncode_AddBytesToMapN(&cbor_encode_ctx, + COSE_KEY_PARAM_Y_COORDINATE, + y_cord); + QCBOREncode_CloseMap(&cbor_encode_ctx); + + qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &encoded_key_id); + if (qcbor_result != QCBOR_SUCCESS) + { + /* Mainly means that the COSE_Key was too big for buffer_for_cose_key */ + return_value = PAL_ATTEST_ERR_PROTECTED_HEADERS; + goto Done; + } + + /* Finish up and return */ + *cose_key = encoded_key_id; + return_value = PAL_ATTEST_SUCCESS; + +Done: + return return_value; +} + + +static int get_items_in_map(QCBORDecodeContext *decode_context, + struct items_to_get_t *item_list) +{ + int item_index; + QCBORItem item; + struct items_to_get_t *item_ptr = item_list; + + /* initialize the data type of all items in the list */ + while (item_ptr->label != 0) + { + item_ptr->item.uDataType = QCBOR_TYPE_NONE; + item_ptr++; + } + + QCBORDecode_GetNext(decode_context, &item); + if (item.uDataType != QCBOR_TYPE_MAP) + { + return PAL_ATTEST_ERROR; + } + + for (item_index = item.val.uCount; item_index != 0; item_index--) + { + if (QCBORDecode_GetNext(decode_context, &item) != QCBOR_SUCCESS) + { + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + } + if (item.uLabelType != QCBOR_TYPE_INT64) + { + continue; + } + + item_ptr = item_list; + while (item_ptr->label != 0) + { + if (item.label.int64 == item_ptr->label) + { + item_ptr->item = item; + } + item_ptr++; + } + } + + return PAL_ATTEST_SUCCESS; +} + +static int get_item_in_map(QCBORDecodeContext *decode_context, + int32_t label, + QCBORItem *item) +{ + struct items_to_get_t item_list[2]; + + item_list[0].label = label; + item_list[1].label = 0; + + if (get_items_in_map(decode_context, item_list)) + { + return PAL_ATTEST_ERROR; + } + + if (item_list[0].item.uDataType == QCBOR_TYPE_NONE) + { + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + } + + *item = item_list[0].item; + + return PAL_ATTEST_SUCCESS; +} + +static int parse_unprotected_headers(QCBORDecodeContext *decode_context, + struct q_useful_buf_c *child, + bool *loop_back) +{ + struct items_to_get_t item_list[3]; + + item_list[0].label = COSE_HEADER_PARAM_KID; + item_list[1].label = T_COSE_SHORT_CIRCUIT_LABEL; + item_list[2].label = 0; + *loop_back = false; + + if (get_items_in_map(decode_context, item_list)) + { + return PAL_ATTEST_ERROR; + } + + if (item_list[1].item.uDataType == QCBOR_TYPE_TRUE) + { + *loop_back = true; + } + + if (item_list[0].item.uDataType != QCBOR_TYPE_BYTE_STRING) + { + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + } + + *child = item_list[0].item.val.string; + + return PAL_ATTEST_SUCCESS; +} + +static int parse_protected_headers(struct q_useful_buf_c protected_headers, + int32_t *alg_id) +{ + QCBORDecodeContext decode_context; + QCBORItem item; + + QCBORDecode_Init(&decode_context, protected_headers, 0); + + if (get_item_in_map(&decode_context, COSE_HEADER_PARAM_ALG, &item)) + { + return PAL_ATTEST_ERROR; + } + + if (QCBORDecode_Finish(&decode_context)) + { + return PAL_ATTEST_ERROR; + } + + if ((item.uDataType != QCBOR_TYPE_INT64) || (item.val.int64 > INT32_MAX)) + { + return PAL_ATTEST_ERROR; + } + + *alg_id = (int32_t)item.val.int64; + + return PAL_ATTEST_SUCCESS; +} + +/** + @brief - This API will verify the claims + @param - decode_context : The buffer containing the challenge + item : context for decoding the data items + completed_challenge : Buffer containing the challenge + @return - error status +**/ +static int parse_claims(QCBORDecodeContext *decode_context, QCBORItem item, + struct q_useful_buf_c completed_challenge) +{ + int i, count = 0; + int status = PAL_ATTEST_SUCCESS; + + /* Parse each claim and validate their data type */ + while (status == PAL_ATTEST_SUCCESS) + { + status = QCBORDecode_GetNext(decode_context, &item); + if (status != PAL_ATTEST_SUCCESS) + break; + + mandatory_claims |= 1 << (EAT_CBOR_ARM_RANGE_BASE - item.label.int64); + if (item.uLabelType == QCBOR_TYPE_INT64) + { + if (item.label.int64 == EAT_CBOR_ARM_LABEL_NONCE) + { + if (item.uDataType == QCBOR_TYPE_BYTE_STRING) + { + /* Given challenge vs challenge in token */ + if (UsefulBuf_Compare(item.val.string, completed_challenge)) + return PAL_ATTEST_TOKEN_CHALLENGE_MISMATCH; + } + else + return PAL_ATTEST_TOKEN_NOT_SUPPORTED; + } + else if (item.label.int64 == EAT_CBOR_ARM_LABEL_BOOT_SEED || + item.label.int64 == EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID || + item.label.int64 == EAT_CBOR_ARM_LABEL_UEID) + { + if (item.uDataType != QCBOR_TYPE_BYTE_STRING) + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + } + else if (item.label.int64 == EAT_CBOR_ARM_LABEL_ORIGINATION || + item.label.int64 == EAT_CBOR_ARM_LABEL_PROFILE_DEFINITION || + item.label.int64 == EAT_CBOR_ARM_LABEL_HW_VERSION) + { + if (item.uDataType != QCBOR_TYPE_TEXT_STRING) + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + } + else if (item.label.int64 == EAT_CBOR_ARM_LABEL_CLIENT_ID || + item.label.int64 == EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE) + { + if (item.uDataType != QCBOR_TYPE_INT64) + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + } + else if (item.label.int64 == EAT_CBOR_ARM_LABEL_SW_COMPONENTS) + { + if (item.uDataType != QCBOR_TYPE_ARRAY) + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + + sw_component_present = 1; + status = QCBORDecode_GetNext(decode_context, &item); + if (status != PAL_ATTEST_SUCCESS) + continue; + + count = item.val.uCount; + for (i = 0; i <= count; i++) + { + mandaroty_sw_components |= 1 << item.label.int64; + + if (item.label.int64 == EAT_CBOR_SW_COMPONENT_MEASUREMENT) + { + if (item.uDataType != QCBOR_TYPE_BYTE_STRING) + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + } + else if (item.label.int64 == EAT_CBOR_SW_COMPONENT_MEASUREMENT_DESC) + { + if (item.uDataType != QCBOR_TYPE_TEXT_STRING) + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + } + else if (item.label.int64 == EAT_CBOR_SW_COMPONENT_VERSION) + { + if (item.uDataType != QCBOR_TYPE_TEXT_STRING) + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + } + else if (item.label.int64 == EAT_CBOR_SW_COMPONENT_SIGNER_ID) + { + if (item.uDataType != QCBOR_TYPE_BYTE_STRING) + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + } + else if (item.label.int64 == EAT_CBOR_SW_COMPONENT_EPOCH) + { + if (item.uDataType != QCBOR_TYPE_INT64) + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + } + else if (item.label.int64 == EAT_CBOR_SW_COMPONENT_TYPE) + { + if (item.uDataType != QCBOR_TYPE_TEXT_STRING) + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + } + + if (i < count) + { + status = QCBORDecode_GetNext(decode_context, &item); + if (status != PAL_ATTEST_SUCCESS) + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + } + } + + } + } + else + { + /* ToDo: Add other claim types */ + } + } + + if (status == QCBOR_ERR_HIT_END) + return PAL_ATTEST_SUCCESS; + else + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; +} + +/** + @brief - This API will verify the attestation token + @param - challenge : The buffer containing the challenge + challenge_size : Size of the challenge buffer + token : The buffer containing the attestation token + token_size : Size of the token buffer + @return - error status +**/ +int32_t pal_initial_attest_verify_token(uint8_t *challenge, uint32_t challenge_size, + uint8_t *token, uint32_t token_size) +{ + int32_t status = PAL_ATTEST_SUCCESS; + bool short_circuit; + int32_t cose_algorithm_id; + QCBORItem item; + QCBORDecodeContext decode_context; + struct q_useful_buf_c completed_challenge; + struct q_useful_buf_c completed_token; + struct q_useful_buf_c payload; + struct q_useful_buf_c signature; + struct q_useful_buf_c protected_headers; + struct q_useful_buf_c kid; + struct q_useful_buf_c x_cord; + struct q_useful_buf_c y_cord; + struct q_useful_buf_c cose_key_to_hash; + struct q_useful_buf_c key_hash; + struct q_useful_buf_c token_hash; + USEFUL_BUF_MAKE_STACK_UB(buf_to_hold_x_coord, T_COSE_CRYPTO_EC_P256_COORD_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buf_to_hold_y_coord, T_COSE_CRYPTO_EC_P256_COORD_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_kid, T_COSE_CRYPTO_SHA256_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_cose_key, MAX_ENCODED_COSE_KEY_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_encoded_key, MAX_ENCODED_COSE_KEY_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_token_hash, T_COSE_CRYPTO_SHA256_SIZE); + + kid.ptr = buffer_for_encoded_key.ptr; + + memcpy(buf_to_hold_x_coord.ptr, (const void *)attest_key.pubx_key, attest_key.pubx_key_size); + memcpy(buf_to_hold_y_coord.ptr, (const void *)attest_key.puby_key, attest_key.puby_key_size); + + /* Update size */ + buf_to_hold_x_coord.len = attest_key.pubx_key_size; + buf_to_hold_y_coord.len = attest_key.puby_key_size; + + x_cord.ptr = buf_to_hold_x_coord.ptr; + x_cord.len = buf_to_hold_x_coord.len; + y_cord.ptr = buf_to_hold_y_coord.ptr; + y_cord.len = buf_to_hold_y_coord.len; + + /* Construct the token buffer for validation */ + completed_token.ptr = token; + completed_token.len = token_size; + + /* Construct the challenge buffer for validation */ + completed_challenge.ptr = challenge; + completed_challenge.len = challenge_size; + +/* + ------------------------- + | CBOR Array Type | + ------------------------- + | Protected Headers | + ------------------------- + | Unprotected Headers | + ------------------------- + | Payload | + ------------------------- + | Signature | + ------------------------- +*/ + + /* Initialize the decorder */ + QCBORDecode_Init(&decode_context, completed_token, QCBOR_DECODE_MODE_NORMAL); + + /* Get the Header */ + QCBORDecode_GetNext(&decode_context, &item); + + /* Check the CBOR Array type. Check if the count is 4. + * Only COSE_SIGN1 is supported now. + */ + if (item.uDataType != QCBOR_TYPE_ARRAY || item.val.uCount != 4 || + !QCBORDecode_IsTagged(&decode_context, &item, CBOR_TAG_COSE_SIGN1)) + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + + /* Get the next headers */ + QCBORDecode_GetNext(&decode_context, &item); + if (item.uDataType != QCBOR_TYPE_BYTE_STRING) + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + + protected_headers = item.val.string; + + /* Parse the protected headers and check the data type and value*/ + status = parse_protected_headers(protected_headers, &cose_algorithm_id); + if (status != PAL_ATTEST_SUCCESS) + return status; + + /* Parse the unprotected headers and check the data type and value */ + short_circuit = false; + status = parse_unprotected_headers(&decode_context, &kid, &short_circuit); + if (status != PAL_ATTEST_SUCCESS) + return status; + + /* Encode the given public key */ + status = pal_encode_cose_key(&cose_key_to_hash, buffer_for_cose_key, x_cord, y_cord); + if (status != PAL_ATTEST_SUCCESS) + return status; + + /* Create hash of the given public key */ + status = pal_create_sha256(cose_key_to_hash, buffer_for_kid, &key_hash); + if (status != PSA_SUCCESS) + return status; + + /* Compare the hash of the public key in token and hash of the given public key */ + if (kid.len != key_hash.len) + { + return PAL_ATTEST_HASH_LENGTH_MISMATCH; + } + + if (memcmp(kid.ptr, key_hash.ptr, kid.len) != 0) + { + return PAL_ATTEST_HASH_MISMATCH; + } + + /* Get the payload */ + QCBORDecode_GetNext(&decode_context, &item); + if (item.uDataType != QCBOR_TYPE_BYTE_STRING) + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + + payload = item.val.string; + + /* Get the digital signature */ + QCBORDecode_GetNext(&decode_context, &item); + if (item.uDataType != QCBOR_TYPE_BYTE_STRING) + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + + signature = item.val.string; + + /* Compute the hash from the token */ + status = pal_compute_hash(cose_algorithm_id, buffer_for_token_hash, &token_hash, + protected_headers, payload); + if (status != PAL_ATTEST_SUCCESS) + return status; + + /* Verify the signature */ + status = pal_crypto_pub_key_verify(cose_algorithm_id, token_hash, signature); + if (status != PAL_ATTEST_SUCCESS) + return status; + + /* Initialize the Decoder and validate the payload format */ + QCBORDecode_Init(&decode_context, payload, QCBOR_DECODE_MODE_NORMAL); + status = QCBORDecode_GetNext(&decode_context, &item); + if (status != PAL_ATTEST_SUCCESS) + return status; + + if (item.uDataType != QCBOR_TYPE_MAP) + return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + + /* Parse the payload and check the data type of each claim */ + status = parse_claims(&decode_context, item, completed_challenge); + if (status != PAL_ATTEST_SUCCESS) + return status; + + if ((mandatory_claims & MANDATORY_CLAIM_WITH_SW_COMP) == MANDATORY_CLAIM_WITH_SW_COMP) + { + if ((mandaroty_sw_components & MANDATORY_SW_COMP) != MANDATORY_SW_COMP) + return PAL_ATTEST_TOKEN_NOT_ALL_MANDATORY_CLAIMS; + } + else if ((mandatory_claims & MANDATORY_CLAIM_NO_SW_COMP) != MANDATORY_CLAIM_NO_SW_COMP) + { + return PAL_ATTEST_TOKEN_NOT_ALL_MANDATORY_CLAIMS; + } + + return PAL_ATTEST_SUCCESS; +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_eat.h b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_eat.h new file mode 100644 index 00000000..8a0c5455 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_eat.h @@ -0,0 +1,170 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "qcbor.h" +#include "pal_common.h" +#include "psa/crypto.h" + +#define PAL_ATTEST_MIN_ERROR 30 + +/* NIST P-256 also known as secp256r1 */ +#define P_256 1 + +#define COSE_HEADER_PARAM_ALG 1 +#define COSE_HEADER_PARAM_KID 4 + +#define COSE_KEY_COMMON_KTY 1 +#define COSE_KEY_TYPE_EC2 2 +#define COSE_KEY_PARAM_CRV -1 +#define COSE_KEY_PARAM_X_COORDINATE -2 +#define COSE_KEY_PARAM_Y_COORDINATE -3 +#define COSE_ALGORITHM_ES256 -7 +#define COSE_ALG_SHA256_PROPRIETARY -72000 + +/** + * The size of X and Y coordinate in 2 parameter style EC public + * key. Format is as defined in [COSE (RFC 8152)] + * (https://tools.ietf.org/html/rfc8152) and [SEC 1: Elliptic Curve + * Cryptography](http://www.secg.org/sec1-v2.pdf). + * + * This size is well-known and documented in public standards. + */ +#define T_COSE_CRYPTO_EC_P256_COORD_SIZE 32 +#define T_COSE_CRYPTO_SHA256_SIZE 32 + +#define MAX_ENCODED_COSE_KEY_SIZE \ + 1 + /* 1 byte to encode map */ \ + 2 + /* 2 bytes to encode key type */ \ + 2 + /* 2 bytes to encode curve */ \ + 2 * /* the X and Y coordinates at 32 bytes each */ \ + (T_COSE_CRYPTO_EC_P256_COORD_SIZE + 1 + 2) +#define USEFUL_BUF_MAKE_STACK_UB UsefulBuf_MAKE_STACK_UB + +#define COSE_SIG_CONTEXT_STRING_SIGNATURE1 "Signature1" + +/* Private value. Intentionally not documented for Doxygen. + * This is the size allocated for the encoded protected headers. It + * needs to be big enough for make_protected_header() to succeed. It + * currently sized for one header with an algorithm ID up to 32 bits + * long -- one byte for the wrapping map, one byte for the label, 5 + * bytes for the ID. If this is made accidentially too small, QCBOR will + * only return an error, and not overrun any buffers. + * + * 9 extra bytes are added, rounding it up to 16 total, in case some + * other protected header is to be added. + */ +#define T_COSE_SIGN1_MAX_PROT_HEADER (1+1+5+9) + +/** + * This is the size of the first part of the CBOR encoded TBS + * bytes. It is around 20 bytes. See create_tbs_hash(). + */ +#define T_COSE_SIZE_OF_TBS \ + 1 + /* For opening the array */ \ + sizeof(COSE_SIG_CONTEXT_STRING_SIGNATURE1) + /* "Signature1" */ \ + 2 + /* Overhead for encoding string */ \ + T_COSE_SIGN1_MAX_PROT_HEADER + /* entire protected headers */ \ + 3 * ( /* 3 NULL bstrs for fields not used */ \ + 1 /* size of a NULL bstr */ \ + ) + +/* + CBOR Label for proprietary header indicating short-circuit + signing was used. Just a random number in the proprietary + label space */ +#define T_COSE_SHORT_CIRCUIT_LABEL (-8675309) + +#define EAT_CBOR_ARM_RANGE_BASE (-75000) +#define EAT_CBOR_ARM_LABEL_PROFILE_DEFINITION (EAT_CBOR_ARM_RANGE_BASE - 0) +#define EAT_CBOR_ARM_LABEL_CLIENT_ID (EAT_CBOR_ARM_RANGE_BASE - 1) +#define EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE (EAT_CBOR_ARM_RANGE_BASE - 2) +#define EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID (EAT_CBOR_ARM_RANGE_BASE - 3) +#define EAT_CBOR_ARM_LABEL_BOOT_SEED (EAT_CBOR_ARM_RANGE_BASE - 4) +#define EAT_CBOR_ARM_LABEL_HW_VERSION (EAT_CBOR_ARM_RANGE_BASE - 5) +#define EAT_CBOR_ARM_LABEL_SW_COMPONENTS (EAT_CBOR_ARM_RANGE_BASE - 6) +#define EAT_CBOR_ARM_LABEL_NO_SW_COMPONENTS (EAT_CBOR_ARM_RANGE_BASE - 7) +#define EAT_CBOR_ARM_LABEL_NONCE (EAT_CBOR_ARM_RANGE_BASE - 8) +#define EAT_CBOR_ARM_LABEL_UEID (EAT_CBOR_ARM_RANGE_BASE - 9) +#define EAT_CBOR_ARM_LABEL_ORIGINATION (EAT_CBOR_ARM_RANGE_BASE - 10) + +#define CBOR_ARM_TOTAL_CLAIM_INSTANCE 10 + +#define EAT_CBOR_SW_COMPONENT_TYPE (1u) +#define EAT_CBOR_SW_COMPONENT_MEASUREMENT (2u) +#define EAT_CBOR_SW_COMPONENT_EPOCH (3u) +#define EAT_CBOR_SW_COMPONENT_VERSION (4u) +#define EAT_CBOR_SW_COMPONENT_SIGNER_ID (5u) +#define EAT_CBOR_SW_COMPONENT_MEASUREMENT_DESC (6u) + +#define MANDATORY_CLAIM_WITH_SW_COMP (1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_NONCE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_UEID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_CLIENT_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_BOOT_SEED) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_SW_COMPONENTS)) + +#define MANDATORY_CLAIM_NO_SW_COMP (1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_NONCE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_UEID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_CLIENT_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_BOOT_SEED) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_NO_SW_COMPONENTS)) + +#define MANDATORY_SW_COMP (1 << EAT_CBOR_SW_COMPONENT_MEASUREMENT | \ + 1 << EAT_CBOR_SW_COMPONENT_SIGNER_ID) + +#define NULL_USEFUL_BUF_C NULLUsefulBufC + +enum attestation_error_code { + PAL_ATTEST_SUCCESS = 0, + PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING = PAL_ATTEST_MIN_ERROR, + PAL_ATTEST_TOKEN_CHALLENGE_MISMATCH, + PAL_ATTEST_TOKEN_NOT_SUPPORTED, + PAL_ATTEST_TOKEN_NOT_ALL_MANDATORY_CLAIMS, + PAL_ATTEST_HASH_LENGTH_MISMATCH, + PAL_ATTEST_HASH_MISMATCH, + PAL_ATTEST_HASH_FAIL, + PAL_ATTEST_HASH_UNSUPPORTED, + PAL_ATTEST_HASH_BUFFER_SIZE, + PAL_ATTEST_ERR_PROTECTED_HEADERS, + PAL_ATTEST_ERR_SIGN_STRUCT, + PAL_ATTEST_ERROR, +}; + +struct items_to_get_t { + int64_t label; + QCBORItem item; +}; + +int32_t pal_initial_attest_verify_token(uint8_t *challenge, uint32_t challenge_size, + uint8_t *token, uint32_t token_size); diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_empty_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_empty_intf.c new file mode 100644 index 00000000..faf3f493 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_empty_intf.c @@ -0,0 +1,30 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include "pal_common.h" + +/** + @brief - This API will call the requested attestation function + @param - type : function code + valist : variable argument list + @return - error status +**/ +int32_t pal_attestation_function(int type, va_list valist) +{ + return PAL_STATUS_ERROR; +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_intf.c new file mode 100644 index 00000000..2d99f74d --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_intf.c @@ -0,0 +1,54 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_attestation_intf.h" + +/** + @brief - This API will call the requested attestation function + @param - type : function code + valist : variable argument list + @return - error status +**/ +int32_t pal_attestation_function(int type, va_list valist) +{ + uint8_t *challenge, *token; + uint32_t challenge_size, *token_size, verify_token_size; + + switch (type) + { + case PAL_INITIAL_ATTEST_GET_TOKEN: + challenge = va_arg(valist, uint8_t*); + challenge_size = va_arg(valist, uint32_t); + token = va_arg(valist, uint8_t*); + token_size = va_arg(valist, uint32_t*); + return psa_initial_attest_get_token(challenge, challenge_size, token, token_size); + case PAL_INITIAL_ATTEST_GET_TOKEN_SIZE: + challenge_size = va_arg(valist, uint32_t); + token_size = va_arg(valist, uint32_t*); + return psa_initial_attest_get_token_size(challenge_size, token_size); + case PAL_INITIAL_ATTEST_VERIFY_TOKEN: + challenge = va_arg(valist, uint8_t*); + challenge_size = va_arg(valist, uint32_t); + token = va_arg(valist, uint8_t*); + verify_token_size = va_arg(valist, uint32_t); + return pal_initial_attest_verify_token(challenge, challenge_size, + token, verify_token_size); + default: + return PAL_STATUS_UNSUPPORTED_FUNC; + } +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_intf.h b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_intf.h new file mode 100644 index 00000000..12f6ee94 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/initial_attestation/pal_attestation_intf.h @@ -0,0 +1,30 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_INITIAL_ATTESTATION_H_ +#define _PAL_INITIAL_ATTESTATION_H_ + +#include "pal_attestation_crypto.h" + +enum attestation_function_code { + PAL_INITIAL_ATTEST_GET_TOKEN = 0x1, + PAL_INITIAL_ATTEST_GET_TOKEN_SIZE = 0x2, + PAL_INITIAL_ATTEST_VERIFY_TOKEN = 0x3, +}; + +int32_t pal_attestation_function(int type, va_list valist); +#endif /* _PAL_INITIAL_ATTESTATION_H_ */ diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/internal_trusted_storage/pal_internal_trusted_storage_empty_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/internal_trusted_storage/pal_internal_trusted_storage_empty_intf.c new file mode 100644 index 00000000..133cfa9d --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/internal_trusted_storage/pal_internal_trusted_storage_empty_intf.c @@ -0,0 +1,30 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include "pal_common.h" + +/** + @brief - This API will call the requested internal trusted storage function + @param - type : function code + valist : variable argument list + @return - error status +**/ +uint32_t pal_its_function(int type, va_list valist) +{ + return PAL_STATUS_ERROR; +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/internal_trusted_storage/pal_internal_trusted_storage_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/internal_trusted_storage/pal_internal_trusted_storage_intf.c new file mode 100644 index 00000000..4f04ab01 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/internal_trusted_storage/pal_internal_trusted_storage_intf.c @@ -0,0 +1,60 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_internal_trusted_storage_intf.h" + +/** + @brief - This API will call the requested internal trusted storage function + @param - type : function code + valist : variable argument list + @return - error status +**/ +uint32_t pal_its_function(int type, va_list valist) +{ + psa_its_uid_t uid; + uint32_t data_length, offset; + const void *p_write_data; + void *p_read_data; + psa_its_create_flags_t its_create_flags; + struct psa_its_info_t *its_p_info; + + switch (type) + { + case PAL_ITS_SET: + uid = va_arg(valist, psa_its_uid_t); + data_length = va_arg(valist, uint32_t); + p_write_data = va_arg(valist, const void*); + its_create_flags = va_arg(valist, psa_its_create_flags_t); + return psa_its_set(uid, data_length, p_write_data, its_create_flags); + case PAL_ITS_GET: + uid = va_arg(valist, psa_its_uid_t); + offset = va_arg(valist, uint32_t); + data_length = va_arg(valist, uint32_t); + p_read_data = va_arg(valist, void*); + return psa_its_get(uid, offset, data_length, p_read_data); + case PAL_ITS_GET_INFO: + uid = va_arg(valist, psa_its_uid_t); + its_p_info = va_arg(valist, struct psa_its_info_t*); + return psa_its_get_info(uid, its_p_info); + case PAL_ITS_REMOVE: + uid = va_arg(valist, psa_its_uid_t); + return psa_its_remove(uid); + default: + return PAL_STATUS_UNSUPPORTED_FUNC; + } +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/internal_trusted_storage/pal_internal_trusted_storage_intf.h b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/internal_trusted_storage/pal_internal_trusted_storage_intf.h new file mode 100644 index 00000000..6db6aac6 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/internal_trusted_storage/pal_internal_trusted_storage_intf.h @@ -0,0 +1,31 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_INTERNAL_TRUSTED_STORAGE_INTF_H_ +#define _PAL_INTERNAL_TRUSTED_STORAGE_INTF_H_ + +#include "pal_common.h" + +enum its_function_code { + PAL_ITS_SET = 0x1, + PAL_ITS_GET = 0x2, + PAL_ITS_GET_INFO = 0x3, + PAL_ITS_REMOVE = 0x4, +}; + +uint32_t pal_its_function(int type, va_list valist); +#endif /* _PAL_INTERNAL_TRUSTED_STORAGE_INTF_H_ */ diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/protected_storage/pal_protected_storage_empty_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/protected_storage/pal_protected_storage_empty_intf.c new file mode 100644 index 00000000..ee9b13da --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/protected_storage/pal_protected_storage_empty_intf.c @@ -0,0 +1,30 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include "pal_common.h" + +/** + @brief - This API will call the requested protected storage function + @param - type : function code + valist : variable argument list + @return - error status +**/ +uint32_t pal_ps_function(int type, va_list valist) +{ + return PAL_STATUS_ERROR; +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/protected_storage/pal_protected_storage_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/protected_storage/pal_protected_storage_intf.c new file mode 100644 index 00000000..a4241533 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/protected_storage/pal_protected_storage_intf.c @@ -0,0 +1,75 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_protected_storage_intf.h" + +/** + @brief - This API will call the requested protected storage function + @param - type : function code + valist : variable argument list + @return - error status +**/ +uint32_t pal_ps_function(int type, va_list valist) +{ + psa_ps_uid_t uid; + uint32_t data_length, size, offset; + const void *p_write_data; + void *p_read_data; + psa_ps_create_flags_t ps_create_flags; + struct psa_ps_info_t *ps_p_info; + + switch (type) + { + case PAL_PS_SET: + uid = va_arg(valist, psa_ps_uid_t); + data_length = va_arg(valist, uint32_t); + p_write_data = va_arg(valist, const void*); + ps_create_flags = va_arg(valist, psa_ps_create_flags_t); + return psa_ps_set(uid, data_length, p_write_data, ps_create_flags); + case PAL_PS_GET: + uid = va_arg(valist, psa_ps_uid_t); + offset = va_arg(valist, uint32_t); + data_length = va_arg(valist, uint32_t); + p_read_data = va_arg(valist, void*); + return psa_ps_get(uid, offset, data_length, p_read_data); + case PAL_PS_GET_INFO: + uid = va_arg(valist, psa_ps_uid_t); + ps_p_info = va_arg(valist, struct psa_ps_info_t*); + return psa_ps_get_info(uid, ps_p_info); + case PAL_PS_REMOVE: + uid = va_arg(valist, psa_ps_uid_t); + return psa_ps_remove(uid); + case PAL_PS_CREATE: + uid = va_arg(valist, psa_ps_uid_t); + size = va_arg(valist, uint32_t); + ps_create_flags = va_arg(valist, psa_ps_create_flags_t); + return psa_ps_create(uid, size, ps_create_flags); + case PAL_PS_SET_EXTENDED: + uid = va_arg(valist, psa_ps_uid_t); + offset = va_arg(valist, uint32_t); + data_length = va_arg(valist, uint32_t); + p_write_data = va_arg(valist, const void*); + return psa_ps_set_extended(uid, offset, data_length, p_write_data); + case PAL_PS_GET_SUPPORT: + return psa_ps_get_support(); + default: + return PAL_STATUS_UNSUPPORTED_FUNC; + } + + return PAL_STATUS_UNSUPPORTED_FUNC; +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/protected_storage/pal_protected_storage_intf.h b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/protected_storage/pal_protected_storage_intf.h new file mode 100644 index 00000000..a338cdf7 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/nspe/protected_storage/pal_protected_storage_intf.h @@ -0,0 +1,34 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_PROTECTED_STORAGE_INTF_H_ +#define _PAL_PROTECTED_STORAGE_INTF_H_ + +#include "pal_common.h" + +enum ps_function_code { + PAL_PS_SET = 0x1, + PAL_PS_GET = 0x2, + PAL_PS_GET_INFO = 0x3, + PAL_PS_REMOVE = 0x4, + PAL_PS_CREATE = 0x5, + PAL_PS_SET_EXTENDED = 0x6, + PAL_PS_GET_SUPPORT = 0x7, +}; + +uint32_t pal_ps_function(int type, va_list valist); +#endif /* _PAL_PROTECTED_STORAGE_INTF_H_ */ diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/reference_logs/attestation.txt b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/reference_logs/attestation.txt new file mode 100755 index 00000000..0c918099 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/reference_logs/attestation.txt @@ -0,0 +1,36 @@ +***** PSA Architecture Test Suite - Version 0.8 ***** + +Running.. Attestation Suite +****************************************** + +TEST: 801 | DESCRIPTION: Testing initial attestation APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_initial_attestation_get_token with Challenge 32 +[Check 2] Test psa_initial_attestation_get_token with Challenge 48 +[Check 3] Test psa_initial_attestation_get_token with Challenge 64 +[Check 4] Test psa_initial_attestation_get_token with zero challenge size +[Check 5] Test psa_initial_attestation_get_token with small challenge size +[Check 6] Test psa_initial_attestation_get_token with invalid challenge size +[Check 7] Test psa_initial_attestation_get_token with large challenge size +[Check 8] Test psa_initial_attestation_get_token with zero as token size +[Check 9] Test psa_initial_attestation_get_token with small token size +[Check 10] Test psa_initial_attestation_get_token_size with Challenge 32 +[Check 11] Test psa_initial_attestation_get_token_size with Challenge 48 +[Check 12] Test psa_initial_attestation_get_token_size with Challenge 64 +[Check 13] Test psa_initial_attestation_get_token_size with zero challenge size +[Check 14] Test psa_initial_attestation_get_token_size with small challenge size +[Check 15] Test psa_initial_attestation_get_token_size with invalid challenge size +[Check 16] Test psa_initial_attestation_get_token_size with large challenge size +TEST RESULT: PASSED + +****************************************** + +************ Attestation Suite Report ********** +TOTAL TESTS : 1 +TOTAL PASSED : 1 +TOTAL SIM ERROR : 0 +TOTAL FAILED : 0 +TOTAL SKIPPED : 0 +****************************************** + +Entering standby.. diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/reference_logs/crypto.txt b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/reference_logs/crypto.txt new file mode 100755 index 00000000..39743ad6 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/reference_logs/crypto.txt @@ -0,0 +1,403 @@ +***** PSA Architecture Test Suite - Version 0.8 ***** + +Running.. Crypto Suite +****************************************** + +TEST: 201 | DESCRIPTION: Testing psa_crypto_init API: Basic +[Info] Executing tests from non-secure +[Check 1] Test calling crypto functions before psa_crypto_init +[Check 2] Test psa_crypto_init +[Check 3] Test multiple psa_crypto_init +TEST RESULT: PASSED + +****************************************** + +TEST: 202 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_import_key 16 Byte AES +[Check 2] Test psa_import_key 24 Byte AES +[Check 3] Test psa_import_key 32 Byte AES +[Check 4] Test psa_import_key with DES 64 bit key +[Check 5] Test psa_import_key with Triple DES 2-Key +[Check 6] Test psa_import_key with Triple DES 3-Key +[Check 7] Test psa_import_key with key data greater than the algorithm size +[Check 8] Test psa_import_key with incorrect key data size +[Check 9] Test psa_import_key with incorrect key type +[Check 10] Test psa_import_key with already occupied key slot +[Check 11] Test psa_import_key with invalid key slot +[Check 12] Test psa_import_key with zero key slot +TEST RESULT: PASSED + +****************************************** + +TEST: 203 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_export_key 16 Byte AES +[Check 2] Test psa_export_key 24 Byte AES +[Check 3] Test psa_export_key 32 Byte AES +[Check 4] Test psa_export_key with DES 64 bit key +[Check 5] Test psa_export_key with Triple DES 2-Key +[Check 6] Test psa_export_key with Triple DES 3-Key +[Check 7] Test psa_export_key with key policy verify +[Check 8] Test psa_export_key with invalid key slot +[Check 9] Test psa_export_key with zero key slot +[Check 10] Test psa_export_key with less buffer size +[Check 11] Test psa_export_key with empty key slot +TEST RESULT: PASSED + +****************************************** + +TEST: 204 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_export_public_key 16 Byte AES +[Check 2] Test psa_export_public_key 24 Byte AES +[Check 3] Test psa_export_public_key 32 Byte AES +[Check 4] Test psa_export_public_key with DES 64 bit key +[Check 5] Test psa_export_public_key with Triple DES 2-Key +[Check 6] Test psa_export_public_key with Triple DES 3-Key +TEST RESULT: PASSED + +****************************************** + +TEST: 205 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_destroy_key 16 Byte AES +[Check 2] Test psa_destroy_key 24 Byte AES +[Check 3] Test psa_destroy_key 32 Byte AES +[Check 4] Test psa_destroy_key with DES 64 bit key +[Check 5] Test psa_destroy_key with Triple DES 2-Key +[Check 6] Test psa_destroy_key with Triple DES 3-Key +[Check 7] Test psa_destroy_key with invalid key slot +[Check 8] Test psa_destroy_key with zero key slot +[Check 9] Test psa_destroy_key with empty key slot +TEST RESULT: PASSED + +****************************************** + +TEST: 206 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_get_key_information 16 Byte AES +[Check 2] Test psa_get_key_information 24 Byte AES +[Check 3] Test psa_get_key_information 32 Byte AES +[Check 4] Test psa_get_key_information wit DE¦[Check 5] Test psa_get_key_information with Triple DES 2-Key +[Check 6] Test psa_get_key_information with Triple DES 3-Key +[Check 7] Test psa_get_key_information with invalid key slot +[Check 8] Test psa_get_key_information with zero key slot +[Check 9] Test psa_get_key_information with empty key slot +TEST RESULT: PASSED + +****************************************** + +TEST: 207 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_set_key_policy 16 Byte AES +[Check 2] Test psa_set_key_policy 24 Byte AES +[Check 3] Test psa_set_key_policy 32 Byte AES +[Check 4] Test psa_set_key_policy with DES 64 bit key +[Check 5] Test psa_set_key_policy with Triple DES 2-Key +[Check 6] Test psa_set_key_policy with Triple DES 3-Key +[Check 7] Test psa_set_key_policy with already occupied key slot +[Check 8] Test psa_set_key_policy with invalid key slot +[Check 9] Test psa_set_key_policy with zero key slot +[Check 10] Test psa_set_key_policy with invalid usage +TEST RESULT: PASSED + +****************************************** + +TEST: 208 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_get_key_policy 16 Byte AES +[Check 2] Test psa_get_key_policy 24 Byte AES +[Check 3] Test psa_get_key_policy 32 Byte AES +[Check 4] Test psa_get_key_policy with DES 64 bit key +[Check 5] Test psa_get_key_policy with Triple DES 2-Key +[Check 6] Test psa_get_key_policy with Triple DES 3-Key +[Check 7] Test psa_get_key_policy with invalid key slot +[Check 8] Test psa_get_key_policy with zero key slot +TEST RESULT: PASSED + +****************************************** + +TEST: 209 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_set_key_lifetime 16 Byte AES +[Check 2] Test psa_set_key_lifetime with Triple DES 2-Key +[Check 3] Test psa_set_key_lifetime with invalid key slot +[Check 4] Test psa_set_key_lifetime with zero key slot +[Check 5] Test psa_set_key_lifetime with invalid lifetime +TEST RESULT: PASSED + +****************************************** + +TEST: 210 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_get_key_lifetime 16 Byte AES +[Check 2] Test psa_get_key_lifetime with Triple DES 2-Key +[Check 3] Test psa_get_key_lifetime with invalid key slot +[Check 4] Test psa_get_key_lifetime with zero key slot +TEST RESULT: PASSED + +****************************************** + +TEST: 211 | DESCRIPTION: Testing crypto hash functions APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_hash_setup with MD2 algorithm +[Check 2] Test psa_hash_setup with MD4 algorithm +[Check 3] Test psa_hash_setup with MD5 algorithm +[Check 4] Test psa_hash_setup with RIPEMD160 algorithm +[Check 5] Test psa_hash_setup with SHA1 algorithm +[Check 6] Test psa_hash_setup with SHA224 algorithm +[Check 7] Test psa_hash_setup with SHA256 algorithm +[Check 8] Test psa_hash_setup with SHA384 algorithm +[Check 9] Test psa_hash_setup with SHA512 algorithm +[Check 10] Test psa_hash_setup with Invalid algorithm +TEST RESULT: PASSED + +****************************************** + +TEST: 212 | DESCRIPTION: Testing crypto hash functions APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_hash_update with MD2 algorithm +[Check 2] Test psa_hash_update with MD4 algorithm +[Check 3] Test psa_hash_update with MD5 algorithm +[Check 4] Test psa_hash_update with RIPEMD160 algorithm +[Check 5] Test psa_hash_update with SHA1 algorithm +[Check 6] Test psa_hash_update with SHA224 algorithm +[Check 7] Test psa_hash_update with SHA256 algorithm +[Check 8] Test psa_hash_update with SHA384 algorithm +[Check 9] Test psa_hash_update with SHA512 algorithm +[Check 10] Test psa_hash_update without hash setup +[Check 11] Test psa_hash_update with completed operation handle +TEST RESULT: PASSED + +****************************************** + +TEST: 213 | DESCRIPTION: Testing crypto hash functions APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_hash_verify with MD2 algorithm +[Check 2] Test psa_hash_verify with MD4 algorithm +[Check 3] Test psa_hash_verify with MD5 algorithm +[Check 4] Test psa_hash_verify with RIPEMD160 algorithm +[Check 5] Test psa_hash_verify with SHA1 algorithm +[Check 6] Test psa_hash_verify with SHA224 algorithm +[Check 7] Test psa_hash_verify with SHA256 algorithm +[Check 8] Test psa_hash_verify with SHA384 algorithm +[Check 9] Test psa_hash_verify with SHA512 algorithm +[Check 10] Test psa_hash_verify with incorrect expected hash +[Check 11] Test psa_hash_verify with incorrect hash length +[Check 12] test psa_hash_verify with inactive & invalid operation handle +TEST RESULT: PASSED + +****************************************** + +TEST: 214 | DESCRIPTION: Testing crypto hash functions APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_hash_finish with MD2 algorithm +[Check 2] Test psa_hash_finish with MD4 algorithm +[Check 3] Test psa_hash_finish with MD5 algorithm +[Check 4] Test psa_hash_finish with RIPEMD160 algorithm +[Check 5] Test psa_hash_finish with SHA1 algorithm +[Check 6] Test psa_hash_finish with SHA224 algorithm +[Check 7] Test psa_hash_finish with SHA256 algorithm +[Check 8] Test psa_hash_finish with SHA384 algorithm +[Check 9] Test psa_hash_finish with SHA512 algorithm +[Check 10] test psa_hash_finish with inactive operation handle +[Check 11] test psa_hash_finish with invalid hash buffer size +TEST RESULT: PASSED + +****************************************** + +TEST: 215 | DESCRIPTION: Testing crypto hash functions APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_hash_abort with MD2 algorithm +[Check 2] Test psa_hash_abort with MD4 algorithm +[Check 3] Test psa_hash_abort with MD5 algorithm +[Check 4] Test psa_hash_abort with RIPEMD160 algorithm +[Check 5] Test psa_hash_abort with SHA1 algorithm +[Check 6] Test psa_hash_abort with SHA224 algorithm +[Check 7] Test psa_hash_abort with SHA256 algorithm +[Check 8] Test psa_hash_abort with SHA384 algorithm +[Check 9] Test psa_hash_abort with SHA512 algorithm +[Check 10] Test psa_hash_finish after calling psa_hash_abort +TEST RESULT: PASSED + +****************************************** + +TEST: 223 | DESCRIPTION: Testing crypto key management APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_key_policy_get_usage with usage as encrypt +[Check 2] Test psa_key_policy_get_usage with usage as decrypt +[Check 3] Test psa_key_policy_get_usage with usage as derive +[Check 4] Test psa_key_policy_get_usage with usage as export +[Check 5] Test psa_key_policy_get_usage with usage as sign +[Check 6] Test psa_key_policy_get_usage with usage as verify +TEST RESULT: PASSED + +****************************************** + +TEST: 224 | DESCRIPTION: Testing crypto AEAD APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_aead_encrypt - CCM - 16B AES - 13B nounce & 8B addi data +[Check 2] Test psa_aead_encrypt - AES-CCM +[Check 3] Test psa_aead_encrypt - GCM - 16B AES - 12B Nounce & 12B addi data +[Check 4] Test psa_aead_encrypt - DES Key +[Check 5] Test psa_aead_encrypt - Empty key slot +[Check 6] Test psa_aead_encrypt - Zero key slot +[Check 7] Test psa_aead_encrypt - Invalid key slot +[Check 8] Test psa_aead_encrypt - Unsupported Algorithm +[Check 9] Test psa_aead_encrypt - Invalid key usage +[Check 10] Test psa_aead_encrypt - Small output buffer size +TEST RESULT: PASSED + +****************************************** + +TEST: 225 | DESCRIPTION: Testing crypto AEAD APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_aead_decrypt - CCM - 16B AES - 13B nounce & 8B addi data +[Check 2] Test psa_aead_decrypt - AES-CCM +[Check 3] Test psa_aead_decrypt - GCM - 16B AES - 12B Nounce & 12B addi data +[Check 4] Test psa_aead_decrypt - DES Key +[Check 5] Test psa_aead_decrypt - Empty key slot +[Check 6] Test psa_aead_decrypt - Zero key slot +[Check 7] Test psa_aead_decrypt - Invalid key slot +[Check 8] Test psa_aead_decrypt - Unsupported Algorithm +[Check 9] Test psa_aead_decrypt - Invalid key usage +[Check 10] Test psa_aead_decrypt - Small output buffer size +[Check 11] Test psa_aead_decrypt - Invalid cipher text +[Check 12] Test psa_aead_decrypt - Invalid cipher text size +TEST RESULT: PASSED + +****************************************** + +TEST: 232 | DESCRIPTION: Testing crypto symmetric cipher APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_cipher_encrypt_setup 16 Byte AES +[Check 2] Test psa_cipher_encrypt_setup 24 Byte AES +[Check 3] Test psa_cipher_encrypt_setup 32 Byte AES +[Check 4] Test psa_cipher_encrypt_setup DES 64 bit key +[Check 5] Test psa_cipher_encrypt_setup Triple DES 2-Key +[Check 6] Test psa_cipher_encrypt_setup Triple DES 3-Key +[Check 7] Test psa_cipher_encrypt_setup 16 Byte raw data +[Check 8] Test psa_cipher_encrypt_setup - not a cipher algorithm +[Check 9] Test psa_cipher_encrypt_setup - unknown cipher algorithm +[Check 10] Test psa_cipher_encrypt_setup - incompatible key ARC4 +[Check 11] Test psa_cipher_encrypt_setup - invalid key slot +[Check 12] Test psa_cipher_encrypt_setup - empty key slot +[Check 13] Test psa_cipher_encrypt_setup - zero as key slot +[Check 14] Test psa_cipher_encrypt_setup - incorrect usage +TEST RESULT: PASSED + +****************************************** + +TEST: 233 | DESCRIPTION: Testing crypto symmetric cipher APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_cipher_decrypt_setup 16 Byte AES +[Check 2] Test psa_cipher_decrypt_setup 24 Byte AES +[Check 3] Test psa_cipher_decrypt_setup 32 Byte AES +[Check 4] Test psa_cipher_decrypt_setup DES 64 bit key +[Check 5] Test psa_cipher_decrypt_setup Triple DES 2-Key +[Check 6] Test psa_cipher_decrypt_setup Triple DES 3-Key +[Check 7] Test psa_cipher_decrypt_setup 16 Byte raw data +[Check 8] Test psa_cipher_decrypt_setup - not a cipher algorithm +[Check 9] Test psa_cipher_decrypt_setup - unknown cipher algorithm +[Check 10] Test psa_cipher_decrypt_setup - incompatible key ARC4 +[Check 11] Test psa_cipher_decrypt_setup - invalid key slot +[Check 12] Test psa_cipher_decrypt_setup - empty key slot +[Check 13] Test psa_cipher_decrypt_setup - zero as key slot +[Check 14] Test psa_cipher_decrypt_setup - incorrect usage +TEST RESULT: PASSED + +****************************************** + +TEST: 235 | DESCRIPTION: Testing crypto symmetric cipher APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_cipher_set_iv 16 Byte AES +[Check 2] Test psa_cipher_set_iv 24 Byte AES +[Check 3] Test psa_cipher_set_iv 32 Byte AES +[Check 4] Test psa_cipher_set_iv DES 64 bit key +[Check 5] Test psa_cipher_set_iv Triple DES 2-Key +[Check 6] Test psa_cipher_set_iv Triple DES 3-Key +[Check 7] Test psa_cipher_set_iv AES - small iv buffer +[Check 8] Test psa_cipher_set_iv DES - small iv buffer +[Check 9] Test psa_cipher_set_iv AES - large iv buffer +[Check 10] Test psa_cipher_set_iv DES - large iv buffer +TEST RESULT: PASSED + +****************************************** + +TEST: 236 | DESCRIPTION: Testing crypto symmetric cipher APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_cipher_update - Encrypt - AES CBC_NO_PADDING +[Check 2] Test psa_cipher_update - Encrypt - AES CBC_NO_PADDING (Short input) +[Check 3] Test psa_cipher_update - Encrypt - AES CBC_PKCS7 +[Check 4] Test psa_cipher_update - Encrypt - AES CBC_PKCS7 (Short input) +[Check 5] Test psa_cipher_update - Encrypt - AES CTR +[Check 6] Test psa_cipher_update - Encrypt - DES CBC (nopad) +[Check 7] Test psa_cipher_update - Encrypt - 2-key 3DE -CBC (nopad) +[Check 8] Test psa_cipher_update - Encrypt - 3-key 3DE -CBC (nopad) +[Check 9] Test psa_cipher_update - small output buffer size +[Check 10] Test psa_cipher_update - Decrypt - AES CBC_NO_PADDING +[Check 11] Test psa_cipher_update - Decrypt - AES CBC_NO_PADDING (Short input) +[Check 12] Test psa_cipher_update - Decrypt - AES CBC_PKCS7 +[Check 13] Test psa_cipher_update - Decrypt - AES CBC_PKCS7 (Short input) +[Check 14] Test psa_cipher_update - Decrypt - AES CTR +[Check 15] Test psa_cipher_update - Decrypt - DES CBC (nopad) +[Check 16] Test psa_cipher_update - Decrypt - 2-key 3DE -CBC (nopad) +[Check 17] Test psa_cipher_update - Decrypt - 3-key 3DE -CBC (nopad) +TEST RESULT: PASSED + +****************************************** + +TEST: 237 | DESCRIPTION: Testing crypto symmetric cipher APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_cipher_finish - Encrypt - AES CBC_NO_PADDING +[Check 2] Test psa_cipher_finish - Encrypt - AES CBC_NO_PADDING (Short input) +[Check 3] Test psa_cipher_finish - Encrypt - AES CBC_PKCS7 +[Check 4] Test psa_cipher_finish - Encrypt - AES CBC_PKCS7 (Short input) +[Check 5] Test psa_cipher_finish - Encrypt - AES CTR +[Check 6] Test psa_cipher_finish - Encrypt - AES CTR (short input) +[Check 7] Test psa_cipher_finish - Encrypt - DES CBC (nopad) +[Check 8] Test psa_cipher_finish - Encrypt - 2-key 3DE -CBC (nopad) +[Check 9] Test psa_cipher_finish - Encrypt - 3-key 3DE -CBC (nopad) +[Check 10] Test psa_cipher_finish - small output buffer size +[Check 11] Test psa_cipher_finish - Decrypt - AES CBC_NO_PADDING +[Check 12] Test psa_cipher_finish - Decrypt - AES CBC_NO_PADDING (Short input) +[Check 13] Test psa_cipher_finish - Decrypt - AES CBC_PKCS7 +[Check 14] Test psa_cipher_finish - Decrypt - AES CBC_PKCS7 (Short input) +[Check 15] Test psa_cipher_finish - Decrypt - AES CTR +[Check 16] Test psa_cipher_finish - Decrypt - AES CTR (short input) +[Check 17] Test psa_cipher_finish - Decrypt - DES CBC (nopad) +[Check 18] Test psa_cipher_finish - Decrypt - 2-key 3DE -CBC (nopad) +[Check 19] Test psa_cipher_finish - 3-key 3DE -CBC (nopad) +TEST RESULT: PASSED + +****************************************** + +TEST: 238 | DESCRIPTION: Testing crypto symmetric cipher APIs +[Info] Executing tests from non-secure +[Check 1] Test psa_cipher_abort - Encrypt - AES CBC_NO_PADDING +[Check 2] Test psa_cipher_abort - Encrypt - AES CBC_PKCS7 +[Check 3] Test psa_cipher_abort - Encrypt - AES CTR +[Check 4] Test psa_cipher_abort - Encrypt - DES CBC (nopad) +[Check 5] Test psa_cipher_abort - Encrypt - 2-key 3DE -CBC (nopad) +[Check 6] Test psa_cipher_abort - Encrypt - 3-key 3DE -CBC (nopad) +[Check 7] Test psa_cipher_abort - Decrypt - AES CBC_NO_PADDING +[Check 8] Test psa_cipher_abort - Decrypt - AES CBC_PKCS7 +[Check 9] Test psa_cipher_abort - Decrypt - AES CTR +[Check 10] Test psa_cipher_abort - Decrypt - DES CBC (nopad) +[Check 11] Test psa_cipher_abort - Decrypt - 2-key 3DE -CBC (nopad) +[Check 12] Test psa_cipher_abort - Decrypt - 3-key 3DE -CBC (nopad) +[Check 1] Test psa_cipher_update after psa_cipher_abort should fail +TEST RESULT: PASSED + +****************************************** + +************ Crypto Suite Report ********** +TOTAL TESTS : 24 +TOTAL PASSED : 24 +TOTAL SIM ERROR : 0 +TOTAL FAILED : 0 +TOTAL SKIPPED : 0 +****************************************** + +Entering standby.. diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/reference_logs/protected_storage.txt b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/reference_logs/protected_storage.txt new file mode 100755 index 00000000..b7f12d55 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/reference_logs/protected_storage.txt @@ -0,0 +1,154 @@ +***** PSA Architecture Test Suite - Version 0.8 ***** + +Running.. Protected Storage Suite +****************************************** + +TEST: 401 | DESCRIPTION: UID not found check +[Info] Executing tests from non-secure +[Check 1] Call get API for UID 6 which is not set +[Check 2] Call get_info API for UID 6 which is not set +[Check 3] Call remove API for UID 6 which is not set +[Check 4] Call get API for UID 6 which is removed +[Check 5] Call get_info API for UID 6 which is removed +[Check 6] Call remove API for UID 6 which is removed +Set storage for UID 6 +[Check 7] Call get API for different UID 6 +[Check 8] Call get_info API for different UID 6 +[Check 9] Call remove API for different UID 6 +TEST RESULT: PASSED + +****************************************** + +TEST: 402 | DESCRIPTION: Write once error check +[Info] Executing tests from non-secure +[Check 1] Update the flag of UID 1 with WRITE_ONCE flag +[Check 2] Try to remove the UID 1 having WRITE_ONCE flag +[Check 3] Create a new UID 2 with WRITE_ONCE flag +[Check 4] Try to remove the UID 2 having WRITE_ONCE flag +[Check 5] Try to change the length of write_once UID 2 +[Check 6] Check UID removal still fails +[Check 7] Try to change the WRITE_ONCE flag to None for UID 2 +[Check 8] Check UID removal still fails +TEST RESULT: PASSED + +****************************************** + +TEST: 403 | DESCRIPTION: Insufficient space check +[Info] Executing tests from non-secure +[Check 1] Overload storage space +Remove all registered UIDs +[Check 2] Overload storage again to verify all previous UID removed +Remove all registered UIDs +TEST RESULT: PASSED + +****************************************** + +TEST: 404 | DESCRIPTION: Data Consistency check +[Info] Executing tests from non-secure +[Check 1] Call get API with incorrect length +[Check 2] Old buffer invalid after length change +TEST RESULT: PASSED + +****************************************** + +TEST: 405 | DESCRIPTION: Success scenarios check +[Info] Executing tests from non-secure +[Check 1] Set UID with data length zero and call storage APIs +[Check 2] Resetting the length check +TEST RESULT: PASSED + +****************************************** + +TEST: 406 | DESCRIPTION: Flags not supported check +[Info] Executing tests from non-secure +[Check 1] Call set API with valid flag values +TEST RESULT: PASSED + +****************************************** + +TEST: 407 | DESCRIPTION: Incorrect Size check +[Info] Executing tests from non-secure +Create a valid Storage +Increase the length of storage +[Check 1] Call get API with old length +Decrease the length of storage +[Check 2] Call get API with old length +[Check 3] Call get API with valid length +TEST RESULT: PASSED + +****************************************** + +TEST: 408 | DESCRIPTION: Invalid offset check +[Info] Executing tests from non-secure +[Check 1] Try to access data with varying valid offset +[Check 2] Try to access data with varying invalid offset +TEST RESULT: PASSED + +****************************************** + +TEST: 409 | DESCRIPTION: Invalid Arguments check +[Info] Executing tests from non-secure +[Check 1] Call set API with NULL pointer and data length 0 +[Check 2] Create UID with zero data length +[Check 3] Try to set NULL buffer for existing UID +[Check 4] Call get API with NULL read buffer and data length 0 +[Check 5] Increase the length +TEST RESULT: PASSED + +****************************************** + +TEST: 410 | DESCRIPTION: UID value zero check +[Info] Executing tests from non-secure +[Check 1] Creating storage with UID 0 should fail +TEST RESULT: PASSED + +****************************************** + +TEST: 411 | DESCRIPTION: Optional APIs: UID not found check +[Info] Executing tests from non-secure +Test Case skipped as Optional PS APIs are not supported. +TEST RESULT: SKIPPED (Skip Code=0x2B) + +****************************************** + +TEST: 412 | DESCRIPTION: Optional APIs: Invalid arguments and offset invalid +[Info] Executing tests from non-secure +Test Case skipped as Optional PS APIs are not supported. +TEST RESULT: SKIPPED (Skip Code=0x2B) + +****************************************** + +TEST: 413 | DESCRIPTION: Set_Extended and Create api : Success +[Info] Executing tests from non-secure +Test Case skipped as Optional PS APIs are not supported. +TEST RESULT: SKIPPED (Skip Code=0x2B) + +****************************************** + +TEST: 414 | DESCRIPTION: Optional APIs not supported check +[Info] Executing tests from non-secure +Optional PS APIs are not supported. +[Check 1] Call to create API should fail as API not supported +[Check 2] Create valid storage with set API +[Check 3] Call to set_extended API call should fail +[Check 4] Verify data is unchanged +TEST RESULT: PASSED + +****************************************** + +TEST: 415 | DESCRIPTION: Create API write_once flag value check +[Info] Executing tests from non-secure +Test Case skipped as Optional PS APIs are not supported. +TEST RESULT: SKIPPED (Skip Code=0x2B) + +****************************************** + +************ Protected Storage Suite Report ********** +TOTAL TESTS : 15 +TOTAL PASSED : 11 +TOTAL SIM ERROR : 0 +TOTAL FAILED : 0 +TOTAL SKIPPED : 4 +****************************************** + +Entering standby.. diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/spe/pal_driver_intf.c b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/spe/pal_driver_intf.c new file mode 100644 index 00000000..4d522071 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/spe/pal_driver_intf.c @@ -0,0 +1,131 @@ + /** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_driver_intf.h" + +/** + @brief - This function initializes the UART + @param - uart base addr + @return - void +**/ +void pal_uart_init(uint32_t uart_base_addr) +{ + pal_uart_pl011_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, int32_t data) +{ + pal_uart_pl011_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)); +} + +/** + @brief - Trigger interrupt for irq signal assigned to driver partition + before return to caller. + @param - void + @return - void +**/ +void pal_generate_interrupt(void) +{ + pal_uart_pl011_generate_irq(); +} + +/** + @brief - Disable interrupt that was generated using pal_generate_interrupt API. + @param - void + @return - void +**/ +void pal_disable_interrupt(void) +{ + pal_uart_pl011_disable_irq(); +} diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/spe/pal_driver_intf.h b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/spe/pal_driver_intf.h new file mode 100644 index 00000000..cef34ca8 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/spe/pal_driver_intf.h @@ -0,0 +1,35 @@ + /** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_DRIVER_INTF_H_ +#define _PAL_DRIVER_INTF_H_ + +#include "pal_uart.h" +#include "pal_nvmem.h" +#include "pal_wd_cmsdk.h" + +void pal_uart_init(uint32_t uart_base_addr); +void pal_print(char *str, int32_t data); +int pal_nvmem_write(addr_t base, uint32_t offset, void *buffer, int size); +int pal_nvmem_read(addr_t base, uint32_t offset, void *buffer, int size); +int pal_wd_timer_init(addr_t base_addr, uint32_t time_us, uint32_t timer_tick_us); +int pal_wd_timer_enable(addr_t base_addr); +int pal_wd_timer_disable(addr_t base_addr); +int pal_wd_timer_is_enabled(addr_t base_addr); +void pal_generate_interrupt(void); +void pal_disable_interrupt(void); +#endif /* _PAL_DRIVER_INTF_H_ */ diff --git a/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/target.cfg b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/target.cfg new file mode 100644 index 00000000..629efb53 --- /dev/null +++ b/api-tests/platform/targets/tgt_dev_apis_tfm_musca_b1/target.cfg @@ -0,0 +1,56 @@ +///** @file +// * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +// * SPDX-License-Identifier : Apache-2.0 +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT 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 = 0x40106000; // UART1_NS +uart.0.size = 0xFFF; +uart.0.intr_id = 0xFF; +uart.0.permission = TYPE_READ_WRITE; + +// Watchdog device info +watchdog.num = 1; +watchdog.0.base = 0x40081000; +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 +watchdog.0.timeout_in_micro_sec_crypto = 0x1312D00; //18.0 sec : 18 * 1000 * 1000 + +// Range of 1KB Non-volatile memory to preserve data over reset. Ex, NVRAM and FLASH +nvmem.num =1; +nvmem.0.start = 0x20060000; +nvmem.0.end = 0x200603FF; +nvmem.0.permission = TYPE_READ_WRITE; + +// Miscellaneous - Test scatter info +dut.num = 1; + +// Start address of 12KB NS memory for test ELF +dut.0.ns_test_addr = 0x28110000; + +// 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 = 0x28120000; + +// Is combine_test_binary available in RAM? +dut.0.combine_test_binary_in_ram = AVAILABLE; + +dut.0.implemented_psa_firmware_isolation_level = LEVEL1; diff --git a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/Makefile b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/Makefile index 58167536..894cec4d 100644 --- a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/Makefile +++ b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/Makefile @@ -83,6 +83,7 @@ endif ifeq (${PSA_INITIAL_ATTESTATION_IMPLEMENTED},1) SRC_C_NSPE += pal_attestation_intf.c SRC_C_NSPE += pal_attestation_eat.c +SRC_C_NSPE += pal_attestation_crypto.c else SRC_C_NSPE += pal_attestation_empty_intf.c endif @@ -98,7 +99,7 @@ INCLUDE= -I$(SOURCE)/platform/targets/$(TARGET)/nspe \ -I$(SOURCE)/platform/targets/$(TARGET)/nspe/initial_attestation \ -I$(SOURCE)/platform/targets/$(TARGET)/nspe/initial_attestation/ext/inc \ -I$(SOURCE)/platform/targets/$(TARGET)/nspe/internal_trusted_storage \ - -I$(SOURCE)/platform/targets/$(TARGET)/nspe/protected_storage + -I$(SOURCE)/platform/targets/$(TARGET)/nspe/protected_storage \ VPATH=$(SOURCE)/platform/targets/$(TARGET)/: \ $(SOURCE)/platform/targets/$(TARGET)/spe: \ diff --git a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/manifests/common/driver_partition_psa.json b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/manifests/common/driver_partition_psa.json index 5d57571c..3e2ec674 100644 --- a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/manifests/common/driver_partition_psa.json +++ b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/manifests/common/driver_partition_psa.json @@ -3,11 +3,9 @@ "name": "DRIVER_PARTITION", "type": "PSA-ROT", "priority": "NORMAL", - "id": "0x00000003", "description": "Implements device services such print, flash read/write,. etc.", "entry_point": "driver_main", "stack_size": "0x400", - "heap_size": "0x100", "services": [{ "name": "DRIVER_UART_SID", "sid": "0x0000FC01", @@ -33,9 +31,9 @@ "minor_policy": "RELAXED" }, { - "name": "TEST_INTR_SID", + "name": "DRIVER_TEST_SID", "sid": "0x0000FC04", - "signal": "TEST_INTR_SIG", + "signal": "DRIVER_TEST_SIG", "non_secure_clients": true, "minor_version": 1, "minor_policy": "RELAXED" @@ -69,13 +67,9 @@ ], "irqs": [ { + "description": "Using UART TX interrupt to test psa_wait and psa_eoi for irq_signal", "signal": "DRIVER_UART_INTR_SIG", "line_num": 17 } - ], - "linker_pattern": { - "object_list": [ - "driver_partition.a" - ] - } + ] } diff --git a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/manifests/ipc/client_partition_psa.json b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/manifests/ipc/client_partition_psa.json index 081dc95d..b93377bd 100644 --- a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/manifests/ipc/client_partition_psa.json +++ b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/manifests/ipc/client_partition_psa.json @@ -3,11 +3,9 @@ "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": "0x100", "services": [{ "name": "CLIENT_TEST_DISPATCHER_SID", "sid": "0x0000FA01", @@ -20,7 +18,7 @@ "dependencies": [ "DRIVER_UART_SID", "DRIVER_NVMEM_SID", - "TEST_INTR_SID", + "DRIVER_TEST_SID", "SERVER_TEST_DISPATCHER_SID", "SERVER_UNSPECIFED_MINOR_V_SID", "SERVER_STRICT_MINOR_VERSION_SID", @@ -35,10 +33,5 @@ "size": "0x20", "permission": "READ-WRITE" } - ], - "linker_pattern": { - "object_list": [ - "client_partition.a" - ] - } + ] } diff --git a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/manifests/ipc/server_partition_psa.json b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/manifests/ipc/server_partition_psa.json index 3541387c..146b8fbc 100644 --- a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/manifests/ipc/server_partition_psa.json +++ b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/manifests/ipc/server_partition_psa.json @@ -3,7 +3,6 @@ "name": "SERVER_PARTITION", "type": "APPLICATION-ROT", "priority": "NORMAL", - "id": "0x00000002", "description": "Server partition executing server test func", "entry_point": "server_main", "stack_size": "0x400", @@ -66,10 +65,5 @@ "dependencies": [ "DRIVER_UART_SID", "DRIVER_NVMEM_SID" - ], - "linker_pattern": { - "object_list": [ - "server_partition.a" - ] - } + ] } diff --git a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/common/pal_config.h b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/common/pal_config.h index daa0ec5f..e3f70ad7 100644 --- a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/common/pal_config.h +++ b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/common/pal_config.h @@ -50,7 +50,7 @@ #endif #if !defined(VERBOSE) -#define VERBOSE 4 /* Print verbosity = ERROR */ +#define VERBOSE 3 /* Print verbosity = TEST */ #endif #if (!defined(VAL_NSPE_BUILD) && !defined(SPE_BUILD)) @@ -62,7 +62,15 @@ #endif #if !defined(TEST_COMBINE_ARCHIVE) -//#define TEST_COMBINE_ARCHIVE /* Test dispatcher code selection */ +#define TEST_COMBINE_ARCHIVE 0 /* Combine test archive or binary? */ +#endif + +#if !defined(WATCHDOG_AVAILABLE) +#define WATCHDOG_AVAILABLE 0 /* If zero, skip watchdog programming */ +#endif + +#if !defined(SP_HEAP_MEM_SUPP) +#define SP_HEAP_MEM_SUPP 0 /* Are Dynamic funcs available to secure partition? */ #endif /* @@ -79,6 +87,13 @@ * of this file. */ #include "psa_manifest/sid.h" + +/* + * psa_manifest/pid.h: Secure Partition IDs + * Macro definitions that map from Secure Partition names to Secure Partition IDs. + * Partition manifest parse build tool must provide the implementation of this file. +*/ +#include "psa_manifest/pid.h" #endif #if PSA_CRYPTO_IMPLEMENTED diff --git a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/common/pal_driver_ipc_intf.c b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/common/pal_driver_ipc_intf.c index 32aef9c5..f8f773fb 100644 --- a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/common/pal_driver_ipc_intf.c +++ b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/common/pal_driver_ipc_intf.c @@ -56,7 +56,7 @@ int pal_uart_init_ns(uint32_t uart_base_addr) @return - SUCCESS/FAILURE **/ -int pal_print_ns(char *str, uint32_t data) +int pal_print_ns(char *str, int32_t data) { int string_len = 0; char *p = str; diff --git a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/common/pal_driver_ns_intf.c b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/common/pal_driver_ns_intf.c index dd6b14c7..2af6fcc7 100644 --- a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/common/pal_driver_ns_intf.c +++ b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/common/pal_driver_ns_intf.c @@ -38,7 +38,7 @@ int pal_uart_init_ns(uint32_t uart_base_addr) @return - SUCCESS/FAILURE **/ -int pal_print_ns(char *str, uint32_t data) +int pal_print_ns(char *str, int32_t data) { pal_cmsdk_print(str, data); return PAL_STATUS_SUCCESS; diff --git a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/crypto/pal_crypto_intf.c b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/crypto/pal_crypto_intf.c index ad838f91..3df6aa8d 100644 --- a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/crypto/pal_crypto_intf.c +++ b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/crypto/pal_crypto_intf.c @@ -36,7 +36,7 @@ int32_t pal_crypto_function(int type, va_list valist) uint32_t status; const void *extra; size_t extra_size, capacity, *gen_cap, nonce_length, additional_data_length; - psa_key_handle_t handle, *key_handle; + psa_key_handle_t handle, *key_handle, target_handle; psa_key_type_t key_type, *key_type_out; psa_key_policy_t *policy; psa_key_usage_t usage, *usage_out; @@ -325,6 +325,11 @@ int32_t pal_crypto_function(int type, va_list valist) case PAL_CRYPTO_ALLOCATE_KEY: key_handle = (psa_key_handle_t *)va_arg(valist, int*); return psa_allocate_key(key_handle); + case PAL_CRYPTO_COPY_KEY: + handle = (psa_key_handle_t)va_arg(valist, int); + target_handle = (psa_key_handle_t)va_arg(valist, int); + policy = va_arg(valist, psa_key_policy_t*); + return psa_copy_key(handle, target_handle, policy); case PAL_CRYPTO_FREE: for (i = 0; i < PAL_KEY_SLOT_COUNT; i++) psa_destroy_key(i); diff --git a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/crypto/pal_crypto_intf.h b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/crypto/pal_crypto_intf.h index dfabee18..d1dabfa4 100644 --- a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/crypto/pal_crypto_intf.h +++ b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/crypto/pal_crypto_intf.h @@ -68,6 +68,7 @@ enum crypto_function_code { PAL_CRYPTO_ASYMMTERIC_VERIFY = 0x31, PAL_CRYPTO_KEY_AGREEMENT = 0x32, PAL_CRYPTO_ALLOCATE_KEY = 0x33, + PAL_CRYPTO_COPY_KEY = 0x34, PAL_CRYPTO_FREE = 0xFE, }; diff --git a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_crypto.c b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_crypto.c new file mode 100644 index 00000000..ae2bdba4 --- /dev/null +++ b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_crypto.c @@ -0,0 +1,346 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_attestation_crypto.h" + +static uint32_t public_key_registered = 0; + +static inline struct q_useful_buf_c useful_buf_head(struct q_useful_buf_c buf, + size_t amount) +{ + return UsefulBuf_Head(buf, amount); +} + +static uint32_t check_hash_sizes(void) +{ + if (T_COSE_CRYPTO_SHA256_SIZE != PSA_HASH_SIZE(PSA_ALG_SHA_256)) + { + return PAL_ATTEST_HASH_FAIL; + } + + return PAL_ATTEST_SUCCESS; +} + +static psa_ecc_curve_t attest_map_elliptic_curve_type(int32_t cose_curve) +{ + psa_ecc_curve_t psa_curve; + + /*FixMe: Mapping is not complete, missing ones: P384, P521, ED25519, ED448 */ + switch (cose_curve) + { + case P_256: + psa_curve = PSA_ECC_CURVE_SECP256R1; + break; + default: + psa_curve = USHRT_MAX; + } + + return psa_curve; +} + +static psa_algorithm_t cose_hash_alg_id_to_psa(int32_t cose_hash_alg_id) +{ + psa_algorithm_t status; + + switch (cose_hash_alg_id) + { + case COSE_ALG_SHA256_PROPRIETARY: + status = PSA_ALG_SHA_256; + break; + default: + status = PSA_ALG_MD4; + break; + } + + return status; +} + +static int32_t hash_alg_id_from_sig_alg_id(int32_t cose_sig_alg_id) +{ + switch (cose_sig_alg_id) + { + case COSE_ALGORITHM_ES256: + return COSE_ALG_SHA256_PROPRIETARY; + default: + return INT32_MAX; + } +} + +int32_t pal_cose_crypto_hash_start(struct pal_cose_crypto_hash *hash_ctx, int32_t cose_hash_alg_id) +{ + int32_t cose_ret = PAL_ATTEST_SUCCESS; + psa_status_t psa_ret; + struct pal_cose_psa_crypto_hash *psa_hash_ctx; + + cose_ret = check_hash_sizes(); + if (cose_ret) + { + goto error; + } + + if (sizeof(struct pal_cose_crypto_hash) < sizeof(struct pal_cose_psa_crypto_hash)) + { + cose_ret = PAL_ATTEST_HASH_FAIL; + goto error; + } + + psa_hash_ctx = (struct pal_cose_psa_crypto_hash *)hash_ctx; + psa_ret = psa_hash_setup(&psa_hash_ctx->operation, cose_hash_alg_id_to_psa(cose_hash_alg_id)); + + if (psa_ret == PAL_ATTEST_SUCCESS) + { + psa_hash_ctx->status = PAL_ATTEST_SUCCESS; + cose_ret = PAL_ATTEST_SUCCESS; + } + else if (psa_ret == PSA_ERROR_NOT_SUPPORTED) + { + cose_ret = PAL_ATTEST_HASH_UNSUPPORTED; + } + else + { + cose_ret = PAL_ATTEST_HASH_FAIL; + } + +error: + return cose_ret; +} + +void pal_cose_crypto_hash_update(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf_c data_to_hash) +{ + struct pal_cose_psa_crypto_hash *psa_hash_ctx; + + if (sizeof(struct pal_cose_crypto_hash) < sizeof(struct pal_cose_psa_crypto_hash)) + { + return; + } + + psa_hash_ctx = (struct pal_cose_psa_crypto_hash *)hash_ctx; + + if (psa_hash_ctx->status == PAL_ATTEST_SUCCESS) + { + if (data_to_hash.ptr != NULL) + { + psa_hash_ctx->status = psa_hash_update(&psa_hash_ctx->operation, + data_to_hash.ptr, + data_to_hash.len); + } + else + { + /* Intentionally do nothing, just computing the size of the token */ + } + } +} + +int32_t pal_cose_crypto_hash_finish(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf buffer_to_hold_result, + struct q_useful_buf_c *hash_result) +{ + uint32_t cose_ret = PAL_ATTEST_SUCCESS; + psa_status_t psa_ret; + struct pal_cose_psa_crypto_hash *psa_hash_ctx; + + if (sizeof(struct pal_cose_crypto_hash) < sizeof(struct pal_cose_psa_crypto_hash)) + { + cose_ret = PAL_ATTEST_HASH_FAIL; + goto error; + } + + psa_hash_ctx = (struct pal_cose_psa_crypto_hash *)hash_ctx; + + if (psa_hash_ctx->status == PAL_ATTEST_SUCCESS) + { + psa_ret = psa_hash_finish(&psa_hash_ctx->operation, + buffer_to_hold_result.ptr, + buffer_to_hold_result.len, + &(hash_result->len)); + + if (psa_ret == PAL_ATTEST_SUCCESS) + { + hash_result->ptr = buffer_to_hold_result.ptr; + cose_ret = 0; + } + else if (psa_ret == PSA_ERROR_BUFFER_TOO_SMALL) + { + cose_ret = PAL_ATTEST_HASH_BUFFER_SIZE; + } + else + { + cose_ret = PAL_ATTEST_HASH_FAIL; + } + } + else + { + cose_ret = PAL_ATTEST_HASH_FAIL; + } + +error: + return cose_ret; +} + +int pal_create_sha256(struct q_useful_buf_c bytes_to_hash, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash) +{ + uint32_t status = PAL_ATTEST_SUCCESS; + struct pal_cose_crypto_hash hash_ctx; + + status = pal_cose_crypto_hash_start(&hash_ctx, COSE_ALG_SHA256_PROPRIETARY); + if (status) + return status; + + pal_cose_crypto_hash_update(&hash_ctx, bytes_to_hash); + status = pal_cose_crypto_hash_finish(&hash_ctx, buffer_for_hash, hash); + + return status; +} + +uint32_t pal_compute_hash(int32_t cose_alg_id, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash, struct q_useful_buf_c protected_headers, + struct q_useful_buf_c payload) +{ + uint32_t status; + QCBOREncodeContext cbor_encode_ctx; + struct q_useful_buf_c tbs_first_part; + QCBORError qcbor_result; + struct pal_cose_crypto_hash hash_ctx = {{0}}; + int32_t hash_alg_id; + UsefulBuf_MAKE_STACK_UB (buffer_for_TBS_first_part, T_COSE_SIZE_OF_TBS); + + /* This builds the CBOR-format to-be-signed bytes */ + QCBOREncode_Init(&cbor_encode_ctx, buffer_for_TBS_first_part); + QCBOREncode_OpenArray(&cbor_encode_ctx); + /* context */ + QCBOREncode_AddSZString(&cbor_encode_ctx, + COSE_SIG_CONTEXT_STRING_SIGNATURE1); + /* body_protected */ + QCBOREncode_AddBytes(&cbor_encode_ctx, + protected_headers); + /* sign_protected */ + QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_USEFUL_BUF_C); + /* external_aad */ + QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_USEFUL_BUF_C); + /* fake payload */ + QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_USEFUL_BUF_C); + QCBOREncode_CloseArray(&cbor_encode_ctx); + + /* Get the result and convert it to struct q_useful_buf_c representation */ + qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &tbs_first_part); + if (qcbor_result) + { + /* Mainly means that the protected_headers were too big + (which should never happen) */ + status = PAL_ATTEST_ERR_SIGN_STRUCT; + goto Done; + } + + /* Start the hashing */ + hash_alg_id = hash_alg_id_from_sig_alg_id(cose_alg_id); + + /* Don't check hash_alg_id for failure. pal_cose_crypto_hash_start() + * will handle it properly + */ + status = pal_cose_crypto_hash_start(&hash_ctx, hash_alg_id); + if (status) + goto Done; + + /* Hash the first part of the TBS. Take all but the last two + * bytes. The last two bytes are the fake payload from above. It + * is replaced by the real payload which is hashed next. The fake + * payload is needed so the array count is right. This is one of + * the main things that make it possible to implement with one + * buffer for the whole cose sign1. + */ + pal_cose_crypto_hash_update(&hash_ctx, useful_buf_head(tbs_first_part, + tbs_first_part.len - 2)); + + /* Hash the payload */ + pal_cose_crypto_hash_update(&hash_ctx, payload); + + /* Finish the hash and set up to return it */ + status = pal_cose_crypto_hash_finish(&hash_ctx, + buffer_for_hash, + hash); + +Done: + return status; +} + +uint32_t pal_import_attest_key(int32_t alg) +{ + psa_key_type_t attest_key_type; + size_t public_key_size; + psa_status_t status = PSA_SUCCESS; + psa_key_policy_t policy; + psa_ecc_curve_t psa_curve; + psa_key_handle_t public_key_handle; + + /* Mapping of COSE curve type to PSA curve types */ + psa_curve = attest_map_elliptic_curve_type(P_256); + if (psa_curve == USHRT_MAX) + return PAL_ATTEST_ERROR; + + /* Setup the key policy for public key */ + policy = psa_key_policy_init(); + psa_key_policy_set_usage(&policy, PSA_KEY_USAGE_VERIFY, alg); + + status = psa_allocate_key(&public_key_handle); + if (status != PSA_SUCCESS) + return status; + + status = psa_set_key_policy(public_key_handle, &policy); + if (status != PSA_SUCCESS) + return status; + + attest_key_type = PSA_KEY_TYPE_ECC_PUBLIC_KEY(psa_curve); + + /* Register public key to crypto service */ + public_key_size = attest_key.pubx_key_size + attest_key.puby_key_size; + + status = psa_import_key(public_key_handle, + attest_key_type, + (const uint8_t *)&attest_public_key, + public_key_size + 1); + + return status; +} + + +uint32_t pal_crypto_pub_key_verify(int32_t cose_algorithm_id, + struct q_useful_buf_c token_hash, + struct q_useful_buf_c signature) +{ + uint32_t status = PAL_ATTEST_SUCCESS; + + if (!public_key_registered) + { + status = pal_import_attest_key(cose_algorithm_id); + if (status != PAL_ATTEST_SUCCESS) + return status; + + public_key_registered = 1; + } + +/* + * Enable the verify function when Trusted Firmare - M Supports + + * Verify the signature a hash or short message using a public key. + status = psa_asymmetric_verify(public_key_handle, + cose_algorithm_id, token_hash.ptr, token_hash.len, + signature.ptr, signature.len); +*/ + return status; +} diff --git a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_crypto.h b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_crypto.h new file mode 100644 index 00000000..2d63ad13 --- /dev/null +++ b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_crypto.h @@ -0,0 +1,102 @@ +/** @file + * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. + * SPDX-License-Identifier : Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_attestation_eat.h" + +#define ATTEST_PUBLIC_KEY_SLOT 4 + +typedef struct{ + uint8_t *pubx_key; + uint32_t pubx_key_size; + uint8_t *puby_key; + uint32_t puby_key_size; +} ecc_key_t; + +struct ecc_public_key_t { + const uint8_t a; + uint8_t public_key[]; /* X-coordinate || Y-coordinate */ +}; + +static const struct ecc_public_key_t attest_public_key = { + /* Constant byte */ + 0x04, + /* X-coordinate */ + {0x79, 0xEB, 0xA9, 0x0E, 0x8B, 0xF4, 0x50, 0xA6, + 0x75, 0x15, 0x76, 0xAD, 0x45, 0x99, 0xB0, 0x7A, + 0xDF, 0x93, 0x8D, 0xA3, 0xBB, 0x0B, 0xD1, 0x7D, + 0x00, 0x36, 0xED, 0x49, 0xA2, 0xD0, 0xFC, 0x3F, + /* Y-coordinate */ + 0xBF, 0xCD, 0xFA, 0x89, 0x56, 0xB5, 0x68, 0xBF, + 0xDB, 0x86, 0x73, 0xE6, 0x48, 0xD8, 0xB5, 0x8D, + 0x92, 0x99, 0x55, 0xB1, 0x4A, 0x26, 0xC3, 0x08, + 0x0F, 0x34, 0x11, 0x7D, 0x97, 0x1D, 0x68, 0x64}, +}; + +struct pal_cose_crypto_hash { + /* Can't put the actual size here without creating dependecy on + * actual hash implementation, so this is a fairly large and + * accommodating size. + */ + uint8_t bytes[128]; +}; + +struct pal_cose_psa_crypto_hash { + psa_status_t status; + psa_hash_operation_t operation; +}; + +static const uint8_t initial_attestation_public_x_key[] = +{ + 0x79, 0xEB, 0xA9, 0x0E, 0x8B, 0xF4, 0x50, 0xA6, + 0x75, 0x15, 0x76, 0xAD, 0x45, 0x99, 0xB0, 0x7A, + 0xDF, 0x93, 0x8D, 0xA3, 0xBB, 0x0B, 0xD1, 0x7D, + 0x00, 0x36, 0xED, 0x49, 0xA2, 0xD0, 0xFC, 0x3F +}; + +static const uint8_t initial_attestation_public_y_key[] = +{ + 0xBF, 0xCD, 0xFA, 0x89, 0x56, 0xB5, 0x68, 0xBF, + 0xDB, 0x86, 0x73, 0xE6, 0x48, 0xD8, 0xB5, 0x8D, + 0x92, 0x99, 0x55, 0xB1, 0x4A, 0x26, 0xC3, 0x08, + 0x0F, 0x34, 0x11, 0x7D, 0x97, 0x1D, 0x68, 0x64 +}; + +/* Initialize the structure with given public key */ +static const ecc_key_t attest_key = { + (uint8_t *)initial_attestation_public_x_key, + sizeof(initial_attestation_public_x_key), + (uint8_t *)initial_attestation_public_y_key, + sizeof(initial_attestation_public_y_key) +}; + +int32_t pal_cose_crypto_hash_start(struct pal_cose_crypto_hash *hash_ctx, int32_t cose_hash_alg_id); +void pal_cose_crypto_hash_update(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf_c data_to_hash); +int32_t pal_cose_crypto_hash_finish(struct pal_cose_crypto_hash *hash_ctx, + struct q_useful_buf buffer_to_hold_result, + struct q_useful_buf_c *hash_result); +int pal_create_sha256(struct q_useful_buf_c bytes_to_hash, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash); +uint32_t pal_compute_hash(int32_t cose_alg_id, struct q_useful_buf buffer_for_hash, + struct q_useful_buf_c *hash, struct q_useful_buf_c protected_headers, + struct q_useful_buf_c payload); +uint32_t pal_import_attest_key(int32_t alg); +uint32_t pal_crypto_pub_key_verify(int32_t cose_algorithm_id, struct q_useful_buf_c token_hash, + struct q_useful_buf_c signature); + + diff --git a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_eat.c b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_eat.c index 262dc5dd..178fdc9c 100644 --- a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_eat.c +++ b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_eat.c @@ -15,10 +15,56 @@ * limitations under the License. **/ -#include "pal_attestation_eat.h" +#include "pal_attestation_crypto.h" + +uint32_t mandatory_claims = 0; +uint32_t mandaroty_sw_components = 0; +bool_t sw_component_present = 0; + +static int pal_encode_cose_key(struct q_useful_buf_c *cose_key, + struct q_useful_buf buffer_for_cose_key, + struct q_useful_buf_c x_cord, struct q_useful_buf_c y_cord) +{ + uint32_t return_value; + QCBORError qcbor_result; + QCBOREncodeContext cbor_encode_ctx; + int32_t cose_curve_id = P_256; + struct q_useful_buf_c encoded_key_id; + + /* Get the public key x and y */ + /* Encode it into a COSE_Key structure */ + QCBOREncode_Init(&cbor_encode_ctx, buffer_for_cose_key); + QCBOREncode_OpenMap(&cbor_encode_ctx); + QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx, + COSE_KEY_COMMON_KTY, + COSE_KEY_TYPE_EC2); + QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx, + COSE_KEY_PARAM_CRV, + cose_curve_id); + QCBOREncode_AddBytesToMapN(&cbor_encode_ctx, + COSE_KEY_PARAM_X_COORDINATE, + x_cord); + QCBOREncode_AddBytesToMapN(&cbor_encode_ctx, + COSE_KEY_PARAM_Y_COORDINATE, + y_cord); + QCBOREncode_CloseMap(&cbor_encode_ctx); + + qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &encoded_key_id); + if (qcbor_result != QCBOR_SUCCESS) + { + /* Mainly means that the COSE_Key was too big for buffer_for_cose_key */ + return_value = PAL_ATTEST_ERR_PROTECTED_HEADERS; + goto Done; + } + + /* Finish up and return */ + *cose_key = encoded_key_id; + return_value = PAL_ATTEST_SUCCESS; + +Done: + return return_value; +} -uint32_t mandatory_claims = 0, mandaroty_sw_components = 0; -bool_t sw_component_present = 0; static int get_items_in_map(QCBORDecodeContext *decode_context, struct items_to_get_t *item_list) @@ -90,7 +136,7 @@ static int get_item_in_map(QCBORDecodeContext *decode_context, } static int parse_unprotected_headers(QCBORDecodeContext *decode_context, - struct useful_buf_c *child, + struct q_useful_buf_c *child, bool *loop_back) { struct items_to_get_t item_list[3]; @@ -120,7 +166,7 @@ static int parse_unprotected_headers(QCBORDecodeContext *decode_context, return PAL_ATTEST_SUCCESS; } -static int parse_protected_headers(struct useful_buf_c protected_headers, +static int parse_protected_headers(struct q_useful_buf_c protected_headers, int32_t *alg_id) { QCBORDecodeContext decode_context; @@ -156,7 +202,7 @@ static int parse_protected_headers(struct useful_buf_c protected_headers, @return - error status **/ static int parse_claims(QCBORDecodeContext *decode_context, QCBORItem item, - struct useful_buf_c completed_challenge) + struct q_useful_buf_c completed_challenge) { int i, count = 0; int status = PAL_ATTEST_SUCCESS; @@ -281,16 +327,42 @@ static int parse_claims(QCBORDecodeContext *decode_context, QCBORItem item, int32_t pal_initial_attest_verify_token(uint8_t *challenge, uint32_t challenge_size, uint8_t *token, uint32_t token_size) { - int status = PAL_ATTEST_SUCCESS; + int32_t status = PAL_ATTEST_SUCCESS; bool short_circuit; int32_t cose_algorithm_id; QCBORItem item; QCBORDecodeContext decode_context; - struct useful_buf_c completed_challenge; - struct useful_buf_c completed_token; - struct useful_buf_c payload; - struct useful_buf_c protected_headers; - struct useful_buf_c kid; + struct q_useful_buf_c completed_challenge; + struct q_useful_buf_c completed_token; + struct q_useful_buf_c payload; + struct q_useful_buf_c signature; + struct q_useful_buf_c protected_headers; + struct q_useful_buf_c kid; + struct q_useful_buf_c x_cord; + struct q_useful_buf_c y_cord; + struct q_useful_buf_c cose_key_to_hash; + struct q_useful_buf_c key_hash; + struct q_useful_buf_c token_hash; + USEFUL_BUF_MAKE_STACK_UB(buf_to_hold_x_coord, T_COSE_CRYPTO_EC_P256_COORD_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buf_to_hold_y_coord, T_COSE_CRYPTO_EC_P256_COORD_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_kid, T_COSE_CRYPTO_SHA256_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_cose_key, MAX_ENCODED_COSE_KEY_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_encoded_key, MAX_ENCODED_COSE_KEY_SIZE); + USEFUL_BUF_MAKE_STACK_UB(buffer_for_token_hash, T_COSE_CRYPTO_SHA256_SIZE); + + kid.ptr = buffer_for_encoded_key.ptr; + + memcpy(buf_to_hold_x_coord.ptr, (const void *)attest_key.pubx_key, attest_key.pubx_key_size); + memcpy(buf_to_hold_y_coord.ptr, (const void *)attest_key.puby_key, attest_key.puby_key_size); + + /* Update size */ + buf_to_hold_x_coord.len = attest_key.pubx_key_size; + buf_to_hold_y_coord.len = attest_key.puby_key_size; + + x_cord.ptr = buf_to_hold_x_coord.ptr; + x_cord.len = buf_to_hold_x_coord.len; + y_cord.ptr = buf_to_hold_y_coord.ptr; + y_cord.len = buf_to_hold_y_coord.len; /* Construct the token buffer for validation */ completed_token.ptr = token; @@ -345,6 +417,27 @@ int32_t pal_initial_attest_verify_token(uint8_t *challenge, uint32_t challenge_s if (status != PAL_ATTEST_SUCCESS) return status; + /* Encode the given public key */ + status = pal_encode_cose_key(&cose_key_to_hash, buffer_for_cose_key, x_cord, y_cord); + if (status != PAL_ATTEST_SUCCESS) + return status; + + /* Create hash of the given public key */ + status = pal_create_sha256(cose_key_to_hash, buffer_for_kid, &key_hash); + if (status != PSA_SUCCESS) + return status; + + /* Compare the hash of the public key in token and hash of the given public key */ + if (kid.len != key_hash.len) + { + return PAL_ATTEST_HASH_LENGTH_MISMATCH; + } + + if (memcmp(kid.ptr, key_hash.ptr, kid.len) != 0) + { + return PAL_ATTEST_HASH_MISMATCH; + } + /* Get the payload */ QCBORDecode_GetNext(&decode_context, &item); if (item.uDataType != QCBOR_TYPE_BYTE_STRING) @@ -357,6 +450,19 @@ int32_t pal_initial_attest_verify_token(uint8_t *challenge, uint32_t challenge_s if (item.uDataType != QCBOR_TYPE_BYTE_STRING) return PAL_ATTEST_TOKEN_ERR_CBOR_FORMATTING; + signature = item.val.string; + + /* Compute the hash from the token */ + status = pal_compute_hash(cose_algorithm_id, buffer_for_token_hash, &token_hash, + protected_headers, payload); + if (status != PAL_ATTEST_SUCCESS) + return status; + + /* Verify the signature */ + status = pal_crypto_pub_key_verify(cose_algorithm_id, token_hash, signature); + if (status != PAL_ATTEST_SUCCESS) + return status; + /* Initialize the Decoder and validate the payload format */ QCBORDecode_Init(&decode_context, payload, QCBOR_DECODE_MODE_NORMAL); status = QCBORDecode_GetNext(&decode_context, &item); diff --git a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_eat.h b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_eat.h index 9f435fb3..8a0c5455 100644 --- a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_eat.h +++ b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_eat.h @@ -17,16 +17,70 @@ #include "qcbor.h" #include "pal_common.h" +#include "psa/crypto.h" #define PAL_ATTEST_MIN_ERROR 30 +/* NIST P-256 also known as secp256r1 */ +#define P_256 1 + #define COSE_HEADER_PARAM_ALG 1 #define COSE_HEADER_PARAM_KID 4 -#define MANDATORY_CLAIM_WITH_SW_COMP 862 -#define MANDATORY_CLAIM_NO_SW_COMP 926 -#define MANDATORY_SW_COMP 36 -#define CBOR_ARM_TOTAL_CLAIM_INSTANCE 10 +#define COSE_KEY_COMMON_KTY 1 +#define COSE_KEY_TYPE_EC2 2 +#define COSE_KEY_PARAM_CRV -1 +#define COSE_KEY_PARAM_X_COORDINATE -2 +#define COSE_KEY_PARAM_Y_COORDINATE -3 +#define COSE_ALGORITHM_ES256 -7 +#define COSE_ALG_SHA256_PROPRIETARY -72000 + +/** + * The size of X and Y coordinate in 2 parameter style EC public + * key. Format is as defined in [COSE (RFC 8152)] + * (https://tools.ietf.org/html/rfc8152) and [SEC 1: Elliptic Curve + * Cryptography](http://www.secg.org/sec1-v2.pdf). + * + * This size is well-known and documented in public standards. + */ +#define T_COSE_CRYPTO_EC_P256_COORD_SIZE 32 +#define T_COSE_CRYPTO_SHA256_SIZE 32 + +#define MAX_ENCODED_COSE_KEY_SIZE \ + 1 + /* 1 byte to encode map */ \ + 2 + /* 2 bytes to encode key type */ \ + 2 + /* 2 bytes to encode curve */ \ + 2 * /* the X and Y coordinates at 32 bytes each */ \ + (T_COSE_CRYPTO_EC_P256_COORD_SIZE + 1 + 2) +#define USEFUL_BUF_MAKE_STACK_UB UsefulBuf_MAKE_STACK_UB + +#define COSE_SIG_CONTEXT_STRING_SIGNATURE1 "Signature1" + +/* Private value. Intentionally not documented for Doxygen. + * This is the size allocated for the encoded protected headers. It + * needs to be big enough for make_protected_header() to succeed. It + * currently sized for one header with an algorithm ID up to 32 bits + * long -- one byte for the wrapping map, one byte for the label, 5 + * bytes for the ID. If this is made accidentially too small, QCBOR will + * only return an error, and not overrun any buffers. + * + * 9 extra bytes are added, rounding it up to 16 total, in case some + * other protected header is to be added. + */ +#define T_COSE_SIGN1_MAX_PROT_HEADER (1+1+5+9) + +/** + * This is the size of the first part of the CBOR encoded TBS + * bytes. It is around 20 bytes. See create_tbs_hash(). + */ +#define T_COSE_SIZE_OF_TBS \ + 1 + /* For opening the array */ \ + sizeof(COSE_SIG_CONTEXT_STRING_SIGNATURE1) + /* "Signature1" */ \ + 2 + /* Overhead for encoding string */ \ + T_COSE_SIGN1_MAX_PROT_HEADER + /* entire protected headers */ \ + 3 * ( /* 3 NULL bstrs for fields not used */ \ + 1 /* size of a NULL bstr */ \ + ) /* CBOR Label for proprietary header indicating short-circuit @@ -47,6 +101,8 @@ #define EAT_CBOR_ARM_LABEL_UEID (EAT_CBOR_ARM_RANGE_BASE - 9) #define EAT_CBOR_ARM_LABEL_ORIGINATION (EAT_CBOR_ARM_RANGE_BASE - 10) +#define CBOR_ARM_TOTAL_CLAIM_INSTANCE 10 + #define EAT_CBOR_SW_COMPONENT_TYPE (1u) #define EAT_CBOR_SW_COMPONENT_MEASUREMENT (2u) #define EAT_CBOR_SW_COMPONENT_EPOCH (3u) @@ -54,6 +110,40 @@ #define EAT_CBOR_SW_COMPONENT_SIGNER_ID (5u) #define EAT_CBOR_SW_COMPONENT_MEASUREMENT_DESC (6u) +#define MANDATORY_CLAIM_WITH_SW_COMP (1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_NONCE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_UEID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_CLIENT_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_BOOT_SEED) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_SW_COMPONENTS)) + +#define MANDATORY_CLAIM_NO_SW_COMP (1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_NONCE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_UEID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_CLIENT_ID) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_BOOT_SEED) | \ + 1 << (EAT_CBOR_ARM_RANGE_BASE \ + - EAT_CBOR_ARM_LABEL_NO_SW_COMPONENTS)) + +#define MANDATORY_SW_COMP (1 << EAT_CBOR_SW_COMPONENT_MEASUREMENT | \ + 1 << EAT_CBOR_SW_COMPONENT_SIGNER_ID) + +#define NULL_USEFUL_BUF_C NULLUsefulBufC enum attestation_error_code { PAL_ATTEST_SUCCESS = 0, @@ -61,6 +151,13 @@ enum attestation_error_code { PAL_ATTEST_TOKEN_CHALLENGE_MISMATCH, PAL_ATTEST_TOKEN_NOT_SUPPORTED, PAL_ATTEST_TOKEN_NOT_ALL_MANDATORY_CLAIMS, + PAL_ATTEST_HASH_LENGTH_MISMATCH, + PAL_ATTEST_HASH_MISMATCH, + PAL_ATTEST_HASH_FAIL, + PAL_ATTEST_HASH_UNSUPPORTED, + PAL_ATTEST_HASH_BUFFER_SIZE, + PAL_ATTEST_ERR_PROTECTED_HEADERS, + PAL_ATTEST_ERR_SIGN_STRUCT, PAL_ATTEST_ERROR, }; diff --git a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_intf.h b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_intf.h index ef132b5b..12f6ee94 100644 --- a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_intf.h +++ b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/nspe/initial_attestation/pal_attestation_intf.h @@ -18,7 +18,7 @@ #ifndef _PAL_INITIAL_ATTESTATION_H_ #define _PAL_INITIAL_ATTESTATION_H_ -#include "pal_attestation_eat.h" +#include "pal_attestation_crypto.h" enum attestation_function_code { PAL_INITIAL_ATTEST_GET_TOKEN = 0x1, diff --git a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/spe/pal_driver_intf.c b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/spe/pal_driver_intf.c index cc8b5373..fd307839 100644 --- a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/spe/pal_driver_intf.c +++ b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/spe/pal_driver_intf.c @@ -33,7 +33,7 @@ void pal_uart_init(uint32_t uart_base_addr) - data : Value for format specifier **/ -void pal_print(char *str, uint32_t data) +void pal_print(char *str, int32_t data) { pal_cmsdk_print(str,data); @@ -110,3 +110,23 @@ int pal_wd_timer_is_enabled(addr_t base_addr) return (pal_wd_cmsdk_is_enabled(base_addr)); } +/** + @brief - Trigger interrupt for irq signal assigned to driver partition + before return to caller. + @param - void + @return - void +**/ +void pal_generate_interrupt(void) +{ + pal_uart_cmsdk_generate_irq(); +} + +/** + @brief - Disable interrupt that was generated using pal_generate_interrupt API. + @param - void + @return - void +**/ +void pal_disable_interrupt(void) +{ + pal_uart_cmsdk_disable_irq(); +} diff --git a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/spe/pal_driver_intf.h b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/spe/pal_driver_intf.h index da85a63e..cef34ca8 100644 --- a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/spe/pal_driver_intf.h +++ b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/spe/pal_driver_intf.h @@ -23,11 +23,13 @@ #include "pal_wd_cmsdk.h" void pal_uart_init(uint32_t uart_base_addr); -void pal_print(char *str, uint32_t data); +void pal_print(char *str, int32_t data); int pal_nvmem_write(addr_t base, uint32_t offset, void *buffer, int size); int pal_nvmem_read(addr_t base, uint32_t offset, void *buffer, int size); int pal_wd_timer_init(addr_t base_addr, uint32_t time_us, uint32_t timer_tick_us); int pal_wd_timer_enable(addr_t base_addr); int pal_wd_timer_disable(addr_t base_addr); int pal_wd_timer_is_enabled(addr_t base_addr); +void pal_generate_interrupt(void); +void pal_disable_interrupt(void); #endif /* _PAL_DRIVER_INTF_H_ */ diff --git a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/target.cfg b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/target.cfg index 37b2628b..9a407b80 100644 --- a/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/target.cfg +++ b/api-tests/platform/targets/tgt_ff_mbedos_fvp_mps2_m4/target.cfg @@ -62,18 +62,21 @@ dut.0.combine_test_binary_in_ram = AVAILABLE; // Level of Isolation dut.0.implemented_psa_firmware_isolation_level = LEVEL1; +// Are dynamic memory allocation functions available to secure partition? +dut.0.sp_heap_mem_supp = AVAILABLE; + // Assign free memory range for isolation testing. Choose the addresses // for these memory regions such that it follows below condition: -// nspe_mmio.0.start < client_partition_mmio.0.start < driver_partition_mmio.0.start. +// nspe_mmio.0.start < server_partition_mmio.0.start < driver_partition_mmio.0.start. nspe_mmio.num=1; nspe_mmio.0.start = 0x200AF000; nspe_mmio.0.end = 0x200AF01F; nspe_mmio.0.permission = TYPE_READ_WRITE; -client_partition_mmio.num=1; -client_partition_mmio.0.start = 0x200AF020; -client_partition_mmio.0.end = 0x200AF03F; -client_partition_mmio.0.permission = TYPE_READ_WRITE; +server_partition_mmio.num=1; +server_partition_mmio.0.start = 0x200AF020; +server_partition_mmio.0.end = 0x200AF03F; +server_partition_mmio.0.permission = TYPE_READ_WRITE; driver_partition_mmio.num=1; driver_partition_mmio.0.start = 0x200AF040; diff --git a/api-tests/tools/makefiles/Makefile b/api-tests/tools/makefiles/Makefile index 6891f43a..1aa790b1 100644 --- a/api-tests/tools/makefiles/Makefile +++ b/api-tests/tools/makefiles/Makefile @@ -30,17 +30,32 @@ all: clean target_cfg gen_linker process_testsuite.db build #Generate target files from User provided data base target_cfg: + @echo "" + @echo "Processing target configuration..." 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} + python $(SOURCE)/tools/scripts/targetConfigGen.py ${TARGET} $(SOURCE)/val/common/val_target.h \ + $(SOURCE)/platform/targets/${TARGET}/target.cfg $(BUILD)/platform/${TARGET}/targetConfigGen.c \ + $(BUILD)/platform/${TARGET}/target_database.h target_database "" + gcc -D__addr_t_defined -DTARGET_CFG_BUILD $(BUILD)/platform/${TARGET}/targetConfigGen.c -o $(BUILD)/platform/${TARGET}/targetConfigGen \ + -I$(SOURCE)/val/nspe -I$(SOURCE)/val/common -I$(SOURCE)/platform/targets/${TARGET}/nspe/common + ./$(BUILD)/platform/${TARGET}/targetConfigGen #Read target.cfg and update the addresses in linker script gen_linker: + @echo "" + @echo "Updating linker files..." mkdir -p $(SUITE_OUT)/ $(BUILD)/val/ $(BUILD)/partition/ ; perl $(SOURCE)/tools/scripts/process_test_linker_file.pl $(SOURCE) $(SUITE_OUT) ${TARGET} $(TOOLCHAIN) process_testsuite.db: - $(eval TEST_LIST := $(shell grep "^test" $(SUITE_IN)/testsuite.db > $(SUITE_OUT)/.testlist.txt ; dos2unix $(SUITE_OUT)/.testlist.txt ; cat $(SUITE_OUT)/.testlist.txt)) + @echo "" + @echo "Creating testlist..." +ifeq (${INCLUDE_PANIC_TESTS}, 1) + $(eval TEST_LIST := $(shell grep -o "^test....." $(SUITE_IN)/testsuite.db > $(SUITE_OUT)/.testlist.txt ; dos2unix $(SUITE_OUT)/.testlist.txt ; cat $(SUITE_OUT)/.testlist.txt)) +else + $(eval TEST_LIST := $(shell grep -v "^test....., panic_test" $(SUITE_IN)/testsuite.db | grep "^test" > $(SUITE_OUT)/.testlist.txt ; dos2unix $(SUITE_OUT)/.testlist.txt ; cat $(SUITE_OUT)/.testlist.txt)) +endif perl $(SOURCE)/tools/scripts/gen_tests_list.pl $(BUILD) $(SUITE_OUT)/.testlist.txt $(SUITE) @@ -60,9 +75,17 @@ val_nspe.a: @echo "----------val build complete-------------" test_combine.elf: test.elf + @echo "" +ifeq (${TEST_COMBINE_ARCHIVE}, 1) + @echo "----------Combine NS test objects into archive start-------------" + $(AR) $(AR_OPTIONS) $(SUITE_OUT)/test_combine.a $(SUITE_OUT)/test*/test_*_nspe.o + @echo "----------Combine NS test objects into archive complete-------------" +else + @echo "----------Combine NS test elfs into binary start-------------" perl $(SOURCE)/tools/scripts/test_elf_combine.pl $(SUITE_OUT)/.testlist.txt hexdump -v -e ' 1/4 "%08X" "\n"' $(SUITE_OUT)/test_elf_combine.bin > $(SUITE_OUT)/test_elf_combine.hex - $(AR) $(AR_OPTIONS) $(SUITE_OUT)/test_combine.a $(SUITE_OUT)/test*/test_*_nspe.o + @echo "----------Combine NS test elfs into binary complete-------------" +endif test.elf: @echo "" @@ -80,7 +103,7 @@ endif output_list: @echo "" - @echo "Below are the list of output binaries/libraries. Integrate these bins/libs" + @echo "Below are the list of output binaries/libraries. Integrate these" @echo "to your software stack to execute test suite." @echo "" @echo "a) NSPE files:" @@ -103,5 +126,6 @@ endif @echo "" clean: - @echo ">>>> Cleaning the build directory..." + @echo "" + @echo "Cleaning the build directory..." rm -rf $(BUILD)/* diff --git a/api-tests/tools/makefiles/linker/test.linker b/api-tests/tools/makefiles/linker/test.linker index ee746997..69fc4d15 100644 --- a/api-tests/tools/makefiles/linker/test.linker +++ b/api-tests/tools/makefiles/linker/test.linker @@ -21,8 +21,8 @@ TEST_START = 0x2004F000; MEMORY { TEST_INFO (R) : ORIGIN = TEST_START, LENGTH = 0x100 - TEST_TEXT (RX) : ORIGIN = TEST_START +0x100, LENGTH = 0x1800 - TEST_DATA (RW) : ORIGIN = TEST_START +0x1900, LENGTH = 0x1800 + TEST_TEXT (RX) : ORIGIN = TEST_START +0x100, LENGTH = 0x1A00 + TEST_DATA (RW) : ORIGIN = TEST_START +0x1B00, LENGTH = 0x1800 } SECTIONS diff --git a/api-tests/tools/makefiles/linker/test.sct b/api-tests/tools/makefiles/linker/test.sct index ef86c3f2..4d5a7a91 100644 --- a/api-tests/tools/makefiles/linker/test.sct +++ b/api-tests/tools/makefiles/linker/test.sct @@ -16,7 +16,7 @@ #define TEST_CODE_START 0x0 #define TEST_INFO_SIZE 0x100 -#define TEST_TEXT_SIZE 0x1800 +#define TEST_TEXT_SIZE 0x1A00 #define TEST_DATA_SIZE 0x1800 LR_CODE TEST_CODE_START diff --git a/api-tests/tools/makefiles/spbuild.mk b/api-tests/tools/makefiles/spbuild.mk index 21fbc965..1479e744 100644 --- a/api-tests/tools/makefiles/spbuild.mk +++ b/api-tests/tools/makefiles/spbuild.mk @@ -59,7 +59,13 @@ $(BUILD)/partition/%.o : %.s $(AS) -o $@ $< client_partition.a: +ifeq ($(wildcard $(SUITE_OUT)/test_i*/.*),) + $(AR) $(AR_OPTIONS) $(BUILD)/partition/client_partition.a $(BUILD)/partition/client_partition.o $(SUITE_OUT)/test*/test_l*_spe.o +else ifeq ($(wildcard $(SUITE_OUT)/test_l*/.*),) $(AR) $(AR_OPTIONS) $(BUILD)/partition/client_partition.a $(BUILD)/partition/client_partition.o $(SUITE_OUT)/test*/test_i*_spe.o +else + $(AR) $(AR_OPTIONS) $(BUILD)/partition/client_partition.a $(BUILD)/partition/client_partition.o $(SUITE_OUT)/test*/test_i*_spe.o $(SUITE_OUT)/test*/test_l*_spe.o +endif server_partition.a: $(AR) $(AR_OPTIONS) $(BUILD)/partition/server_partition.a $(BUILD)/partition/server_partition.o $(SUITE_OUT)/test*/test_supp_*_spe.o diff --git a/api-tests/tools/makefiles/toolchain.mk b/api-tests/tools/makefiles/toolchain.mk index 2a3c9983..699e6bbb 100644 --- a/api-tests/tools/makefiles/toolchain.mk +++ b/api-tests/tools/makefiles/toolchain.mk @@ -82,27 +82,35 @@ endif COMPILER_OPTIONS += -DVERBOSE=$(VERBOSE) ifeq (${TEST_COMBINE_ARCHIVE}, 1) -COMPILER_OPTIONS += -DTEST_COMBINE_ARCHIVE +COMPILER_OPTIONS += -DTEST_COMBINE_ARCHIVE=1 +endif + +ifeq (${WATCHDOG_AVAILABLE}, 1) +COMPILER_OPTIONS += -DWATCHDOG_AVAILABLE=1 +endif + +ifeq (${SP_HEAP_MEM_SUPP}, 1) +COMPILER_OPTIONS += -DSP_HEAP_MEM_SUPP=1 endif ifeq (${PSA_IPC_IMPLEMENTED}, 1) -COMPILER_OPTIONS += -DPSA_IPC_IMPLEMENTED +COMPILER_OPTIONS += -DPSA_IPC_IMPLEMENTED=1 endif ifeq (${PSA_CRYPTO_IMPLEMENTED}, 1) -COMPILER_OPTIONS += -DPSA_CRYPTO_IMPLEMENTED +COMPILER_OPTIONS += -DPSA_CRYPTO_IMPLEMENTED=1 endif ifeq (${PSA_PROTECTED_STORAGE_IMPLEMENTED}, 1) -COMPILER_OPTIONS += -DPSA_PROTECTED_STORAGE_IMPLEMENTED +COMPILER_OPTIONS += -DPSA_PROTECTED_STORAGE_IMPLEMENTED=1 endif ifeq (${PSA_INTERNAL_TRUSTED_STORAGE_IMPLEMENTED}, 1) -COMPILER_OPTIONS += -DPSA_INTERNAL_TRUSTED_STORAGE_IMPLEMENTED +COMPILER_OPTIONS += -DPSA_INTERNAL_TRUSTED_STORAGE_IMPLEMENTED=1 endif ifeq (${PSA_INITIAL_ATTESTATION_IMPLEMENTED}, 1) -COMPILER_OPTIONS += -DPSA_INITIAL_ATTESTATION_IMPLEMENTED +COMPILER_OPTIONS += -DPSA_INITIAL_ATTESTATION_IMPLEMENTED=1 endif CC= $(COMPILER) $(COMPILER_OPTIONS) $(CC_OPTIONS) $(USER_INCLUDE) $(INCLUDE) diff --git a/api-tests/tools/scripts/gen_tests_list.pl b/api-tests/tools/scripts/gen_tests_list.pl index 8b27b50b..f98bb562 100644 --- a/api-tests/tools/scripts/gen_tests_list.pl +++ b/api-tests/tools/scripts/gen_tests_list.pl @@ -33,8 +33,6 @@ sub gen_test_entry_info my $test_entry_list = "$build/val/test_entry_list.inc"; my $test_entry_fn_declare_list = "$build/val/test_entry_fn_declare_list.inc"; - print "\n>>>> Generating tests list by referring testsuite.db \n"; - if ($suite eq "crypto") { $suite_base = 1; @@ -73,7 +71,7 @@ sub gen_test_entry_info close IN; close OUT1; close OUT2; - print "Output files are: + print "Non-secure test entry symbol list: $test_entry_list, $test_entry_fn_declare_list\n"; } @@ -87,8 +85,6 @@ sub gen_secure_tests_list my $server_tests_list_declare = "$build/partition/server_tests_list_declare.inc"; my $server_tests_list = "$build/partition/server_tests_list.inc"; - print "\n>>>> Generating secure tests list files by referring testsuite.db \n"; - open(IN, $tests_list) or die "Unable to open $tests_list $!"; open(OUT1, '>', $client_tests_list_declare) or die "Unable to open: $!"; open(OUT2, '>', $client_tests_list) or die "Unable to open: $!"; @@ -123,7 +119,7 @@ sub gen_secure_tests_list close OUT3; close OUT4; - print "Output files are: + print "Secure test entry symbol list: $client_tests_list_declare, $client_tests_list, $server_tests_list_declare, diff --git a/api-tests/tools/scripts/process_test_linker_file.pl b/api-tests/tools/scripts/process_test_linker_file.pl index 3a31b3b5..4b0bb276 100755 --- a/api-tests/tools/scripts/process_test_linker_file.pl +++ b/api-tests/tools/scripts/process_test_linker_file.pl @@ -16,8 +16,6 @@ # * limitations under the License. #**/ -print "\n>>>> Updating linker file... \n"; - #inputs $source=$ARGV[0]; $suite_out=$ARGV[1]; diff --git a/api-tests/tools/scripts/setup.sh b/api-tests/tools/scripts/setup.sh index 46058a75..7d6b36cf 100755 --- a/api-tests/tools/scripts/setup.sh +++ b/api-tests/tools/scripts/setup.sh @@ -21,16 +21,21 @@ echo "" declare -a INCLUDE_PATHS export SUITE=" " export TEST_COMBINE_ARCHIVE=0 +export INCLUDE_PANIC_TESTS=0 +export WATCHDOG_AVAILABLE=0 +export SP_HEAP_MEM_SUPP=0 export CLIENT_FILE_FOUND=0 export SERVICE_FILE_FOUND=0 +export MANIFEST_OUT_FILE_FOUND=0 export CRYPTO_FILE_FOUND=0 export PROTECTED_STORAGE_FILE_FOUND=0 export INTERNAL_TRUSTED_STORAGE_FILE_FOUND=0 export INITIAL_ATTESTATION_FILE_FOUND=0 +export LIFECYCLE_FILE_FOUND=0 IPC_HEADER_FILE_REQ="If PSA IPC implemented in your platform, include path must point to path -where \"psa/client.h\", \"psa/service.h\" and test partition manifest output files -(\"psa_manifest/sid.h\" and \"psa_manifest/.h\") are located. +where \"psa/client.h\", \"psa/service.h\", \"psa/lifecycle.h\" and test partition manifest output files +(\"psa_manifest/sid.h\", \"psa_manifest/pid.h\" and \"psa_manifest/.h\") are located. " CRYPTO_HEADER_FILE_REQ="If PSA CRYPTO APIs are implemented into your platform then you must provide \"psa/crypto.h\" file to setup.sh script using --include option to compile tests and framework. @@ -42,7 +47,7 @@ ITS_HEADER_FILE_REQ="If PSA INTERNAL_TRUSTED_STORAGE APIs are implemented into y \"psa/internal_trusted_storage.h\" file to setup.sh script using --include option to compile tests and framework. " ATTESTATION_HEADER_FILE_REQ="If PSA INITIAL_ATTESTATION APIs are implemented into your platform then you must provide -\"psa/initial_attestation.h\" file to setup.sh script using --include option to compile tests and framework. +\"psa/initial_attestation.h\" and \"psa/crypto.h\" file to setup.sh script using --include option to compile tests and framework. " HELP=" @@ -50,6 +55,7 @@ HELP=" Usage: setup.sh [--source SOURCE_DIR] [--build BUILD_DIR] [--target TARGET] [--suite SUITE] [--toolchain TOOLCHAIN] [--cpu_arch CPU_ARCH] [--verbose PRINT_LEVEL] [--include INCLUDE_PATH] [--help|-h] +Toplevel script to build tests and framework sources for given test suite. Arguments Info: --source : SOURCE_DIR pointing to architecture test suite directory structure. @@ -59,7 +65,8 @@ Arguments Info: target.cfg file corresponding to input string must be avaiable at platform/targets// --suite : Compile tests for given suite. Support values are: - ipc, crypto, internal_trusted_storage, protected_storage and initial_attestation. + ipc, crypto, internal_trusted_storage, protected_storage, + and initial_attestation. --toolchain : Build using the given TOOLCHAIN. Supported values are GNUARM (GNU Arm Embedded) and ARMCLANG (ARM Compiler 6.x). --cpu_arch : Provide cpu arch string as argument. @@ -68,9 +75,13 @@ Arguments Info: Supported print levels are: 1 - INFO & above. 2 - DEBUG & above. - 3 - TEST & above. - 4 - WARN & ERROR.(Default) + 3 - TEST & above.(Default) + 4 - WARN & ERROR. 5 - ERROR. + --archive_tests : Create combine test archive(.a) file by combining available test objects files. + Absence of this option would create combine test binary(.bin) by combining available test elfs + --include_panic_tests : Consider panic tests (mentioned in testsuite.db of respective suite) along with functional tests + for building the final executables. Absence of this option would consider only non-panic (ie, functional) tests --include : Additional directory to be included into compiler search path. Provide --include where path pointing to location of PSA defined header files. You can specify multiple source locations using --include option. @@ -118,6 +129,9 @@ while [ $# -gt 0 ]; do --archive_tests ) export TEST_COMBINE_ARCHIVE=1 ;; + --include_panic_tests ) + export INCLUDE_PANIC_TESTS=1 + ;; --include ) shift export INCLUDE="$INCLUDE -I $1/" INCLUDE_PATHS=("${INCLUDE_PATHS[@]}" $1) @@ -134,7 +148,7 @@ while [ $# -gt 0 ]; do shift done -echo ">>>> Processing inputs..." +echo "----------Process input arguments- start-------------" if [ -z "$SOURCE" ] then export SOURCE=./ @@ -179,6 +193,8 @@ PSA_CRYPTO_IMPLEMENTED=`grep -c "^ *PSA_CRYPTO_IMPLEMENTED\s*:=\s*1" $PLATFORM_M PSA_PROTECTED_STORAGE_IMPLEMENTED=`grep -c "^ *PSA_PROTECTED_STORAGE_IMPLEMENTED\s*:=\s*1" $PLATFORM_MAKEFILE` PSA_INTERNAL_TRUSTED_STORAGE_IMPLEMENTED=`grep -c "^ *PSA_INTERNAL_TRUSTED_STORAGE_IMPLEMENTED\s*:=\s*1" $PLATFORM_MAKEFILE` PSA_INITIAL_ATTESTATION_IMPLEMENTED=`grep -c "^ *PSA_INITIAL_ATTESTATION_IMPLEMENTED\s*:=\s*1" $PLATFORM_MAKEFILE` +WATCHDOG_AVAILABLE=`grep -c "^ *watchdog.num\s*=\s*1\s*;" $SOURCE/platform/targets/$TARGET/target.cfg` +SP_HEAP_MEM_SUPP=`grep -c "^ *dut.0.sp_heap_mem_supp\s*=\s*AVAILABLE\s*;" $SOURCE/platform/targets/$TARGET/target.cfg` # Check PSA_IPC_IMPLEMENTED validity if [ $SUITE == "ipc" ] && [ $PSA_IPC_IMPLEMENTED == "0" ] @@ -238,6 +254,14 @@ then then export SERVICE_FILE_FOUND=1 fi + if [ -f "$path/psa_manifest/sid.h" ] && [ -f "$path/psa_manifest/pid.h" ] + then + export MANIFEST_OUT_FILE_FOUND=1 + fi + if [ -f "$path/psa/lifecycle.h" ] + then + export LIFECYCLE_FILE_FOUND=1 + fi done if [ $CLIENT_FILE_FOUND == "0" ] then @@ -251,6 +275,18 @@ then echo "$IPC_HEADER_FILE_REQ" exit 1 fi + if [ $MANIFEST_OUT_FILE_FOUND == "0" ] + then + echo "Couldn't find psa_manifest/sid.h or psa_manifest/pid.h file in paths: ${INCLUDE_PATHS[@]}" + echo "$IPC_HEADER_FILE_REQ" + exit 1 + fi + if [ $LIFECYCLE_FILE_FOUND == "0" ] + then + echo "Couldn't find psa/lifecycle.h file in paths: ${INCLUDE_PATHS[@]}" + echo "$IPC_HEADER_FILE_REQ" + exit 1 + fi fi fi @@ -344,7 +380,12 @@ then then export INITIAL_ATTESTATION_FILE_FOUND=1 fi + if [ -f "$path/psa/crypto.h" ] + then + export CRYPTO_FILE_FOUND=1 + fi done + if [ $INITIAL_ATTESTATION_FILE_FOUND == "0" ] then echo "Couldn't find psa/initial_attestation.h file in paths: ${INCLUDE_PATHS[@]}" @@ -352,10 +393,16 @@ then exit 1 elif [ $INITIAL_ATTESTATION_FILE_FOUND == "1" ] then + if [ $CRYPTO_FILE_FOUND == "0" ] + then + echo "Couldn't find psa/crypto.h file in paths: ${INCLUDE_PATHS[@]}" + echo "$CRYPTO_HEADER_FILE_REQ" + exit 1 + fi if [ ! -d "$SOURCE/platform/targets/$TARGET/nspe/initial_attestation/ext" ] then git clone https://github.com/laurencelundblade/QCBOR.git $SOURCE/platform/targets/$TARGET/nspe/initial_attestation/ext - cd $SOURCE/platform/targets/$TARGET/nspe/initial_attestation/ext; git checkout 01168ef3f20e81d5db1ebd0cfa9a70055ee5b155 ; cd - + cd $SOURCE/platform/targets/$TARGET/nspe/initial_attestation/ext; git checkout da53227db1488dde0952bdff66c3d904dce270b3 ; cd - else echo "QCBOR library already cloned" fi @@ -363,7 +410,6 @@ then fi fi - if [ -z "$TOOLCHAIN" ] then export TOOLCHAIN=GNUARM @@ -411,9 +457,23 @@ then exit 1 fi else - export VERBOSE=4 + export VERBOSE=3 fi +if [ $INCLUDE_PANIC_TESTS == "1" ] && [ $WATCHDOG_AVAILABLE == "0" ] +then + echo " +Warning: You have set watchdog.num to 0 in $SOURCE/platform/targets/$TARGET/target.cfg +Note - To test PSA APIs panic conditions, test harness may require to access watchdog timer +to recover from panic and to be able to continue with next test. +Ignore this warning if system under test has capability to reset the system +when it encounters panic condition. +" +fi + +echo "----------Process input arguments- complete-------------" +echo "" + MAKE_OPTIONS=" SOURCE=$SOURCE " MAKE_OPTIONS+=" BUILD=$BUILD " MAKE_OPTIONS+=" TARGET=$TARGET " @@ -422,6 +482,9 @@ MAKE_OPTIONS+=" TOOLCHAIN=$TOOLCHAIN " MAKE_OPTIONS+=" CPU_ARCH=$CPU_ARCH " MAKE_OPTIONS+=" VERBOSE=$VERBOSE " MAKE_OPTIONS+=" TEST_COMBINE_ARCHIVE=$TEST_COMBINE_ARCHIVE " +MAKE_OPTIONS+=" INCLUDE_PANIC_TESTS=$INCLUDE_PANIC_TESTS " +MAKE_OPTIONS+=" WATCHDOG_AVAILABLE=$WATCHDOG_AVAILABLE " +MAKE_OPTIONS+=" SP_HEAP_MEM_SUPP=$SP_HEAP_MEM_SUPP " MAKE_OPTIONS+=" PSA_IPC_IMPLEMENTED=$PSA_IPC_IMPLEMENTED " MAKE_OPTIONS+=" PSA_CRYPTO_IMPLEMENTED=$PSA_CRYPTO_IMPLEMENTED " MAKE_OPTIONS+=" PSA_PROTECTED_STORAGE_IMPLEMENTED=$PSA_PROTECTED_STORAGE_IMPLEMENTED " diff --git a/api-tests/tools/scripts/targetConfigGen.pl b/api-tests/tools/scripts/targetConfigGen.pl deleted file mode 100755 index 602de991..00000000 --- a/api-tests/tools/scripts/targetConfigGen.pl +++ /dev/null @@ -1,238 +0,0 @@ -#!/usr/bin/env perl -#/** @file -# * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. -# * SPDX-License-Identifier : Apache-2.0 -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT 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 -#--------------------------------------------------------------------- - -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/target_database"; -$output_c = "$build/platform/$target/targetConfigGen.c"; -$input_h = "$source/val/common/val_target.h"; - -$final_output_file = undef; -if($final_output =~ /([0-9a-zA-Z_]+)$/) { - $final_output_file = $1; -} - -@unique_devices = undef; - -sub uniq { - my %seen; - grep !$seen{$_}++, @_; -} - -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_]+)(,*)/) { - $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\, \"static target_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 target_database.h file -print "gcc -D__addr_t_defined -DTARGET_CFG_BUILD $output_c -o $build/platform/$target/targetConfigGen -I$source/val/nspe -I$source/val/common -I$source/platform/targets/$target/nspe/common\n"; -system("gcc -D__addr_t_defined -DTARGET_CFG_BUILD $output_c -o $build/platform/$target/targetConfigGen -I$source/val/nspe -I$source/val/common -I$source/platform/targets/$target/nspe/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/api-tests/tools/scripts/targetConfigGen.py b/api-tests/tools/scripts/targetConfigGen.py new file mode 100644 index 00000000..6442f74d --- /dev/null +++ b/api-tests/tools/scripts/targetConfigGen.py @@ -0,0 +1,183 @@ +#!/usr/bin/python +#/** @file +# * Copyright (c) 2019, Arm Limited or its affiliates. All rights reserved. +# * SPDX-License-Identifier : Apache-2.0 +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +#**/ + +import sys + +if (len(sys.argv) != 8): + print("\nPlease provide following inputs") + print("\narg1 : target name") + print("\narg2 : val_target.h") + print("\narg3 : tbsa_tgt.cfg") + print("\narg4 : intermediate source file") + print("\narg5 : output database file\n") + print("\narg6 : name for table\n") + print("\narg7 : section name for database table\n") + sys.exit(1) + +target = sys.argv[1] +in_val_tgt = sys.argv[2] +in_tbsa_tgt = sys.argv[3] +out_source = sys.argv[4] +out_database = sys.argv[5] +table_name = sys.argv[6] +section_name = sys.argv[7] + +minor_major_map = {} +unique_major_groups = [] +unique_minor_components = [] + +def get_minor_major_map(): + """ This method populates the dictionary which maps between every available minor component to their major group """ + try: + with open(in_val_tgt, mode="r") as f: + for line in f: + if "GROUP_" in line: + temp_list = line.split() + if "=" in temp_list[1] and "GROUP_" in temp_list[2]: + minor_major_map.update({temp_list[0]:temp_list[2][6:temp_list[2].find(',')]}) + if minor_major_map[temp_list[0]] not in unique_major_groups: + unique_major_groups.append(minor_major_map[temp_list[0]]) + except: + print("Cannot open %s" %in_val_tgt) + +def generate_source(): + """" This method generates the source file which gets compiled on host machine. + The compiled output then generates the database file for a given target """ + try: + with open(out_source, mode="a") as o_f: + o_f.write("#include \n") + o_f.write("#include \"val_target.h\"\n\n") + o_f.write("int main (void)\n") + o_f.write("{\n") + try: + with open(in_tbsa_tgt, mode="r") as i_f: + num = '' + for line in i_f: + if "/" in line[0:1]: + """ Ignoring the commented lines """ + pass + elif (".num" in line) and (line[line.find('.')+1:line.find('.')+4] == "num"): + """ Pick the lines which tells the total instances of a component """ + minor_comp = line[:line.find('.')] + num = "".join(line[line.find('=')+1:line.find(';')].replace(" ","")) + if (int(num)) != 0: + o_f.write("\t%s_desc_t %s[%s] = {0};\n" %(minor_major_map[minor_comp.upper()].lower(), minor_comp, num)) + o_f.write("\tint %s_num_instances = %s;\n" %(minor_comp, num)) + num = int(num) + for instance in range(num): + o_f.write("\t%s[%d].cfg_type.cfg_id = ((GROUP_%s << 24) + (%s_%s << 16) + %d);\n" \ + %(minor_comp, instance, minor_major_map[minor_comp.upper()],\ + minor_major_map[minor_comp.upper()], minor_comp.upper(), instance)) + o_f.write("\t%s[%d].cfg_type.size = sizeof(%s)/%s_num_instances;\n" %(minor_comp, instance, minor_comp, minor_comp)) + o_f.write("\t%s[%d].cfg_type.size |= (%s_num_instances << 24);\n" %(minor_comp, instance, minor_comp)) + unique_minor_components.append(minor_comp.upper()) + elif ("=" in line) and (";" in line) and (int(num) != 0): + """ Pick the lines which tells the component details """ + period_1 = line.find('.') + period_2 = period_1 + line[period_1+1:].find('.') + 1 + o_f.write("\t%s[%s].%s;\n" %(line[:period_1], line[period_1+1:period_2], line[period_2+1:line.find(';')])) + else: + o_f.write("\n") + o_f.write("\n") + except: + print("Cannot open input file %s" %in_tbsa_tgt) + + """ Populate major structure details """ + for group in unique_major_groups: + o_f.write("\t%s_hdr_t group_%s = {0};\n" %(group.lower(), group.lower())) + o_f.write("\tgroup_%s.cfg_type.cfg_id = (GROUP_%s << 24);\n" %(group.lower(), group)) + o_f.write("\tgroup_%s.cfg_type.size += sizeof(group_%s);\n" %(group.lower(), group.lower())) + for minor in unique_minor_components: + if group == minor_major_map[minor]: + o_f.write("\tgroup_%s.cfg_type.size += sizeof(%s);\n" %(group.lower(), minor.lower())) + o_f.write("\tgroup_%s.num += %s_num_instances;\n" %(group.lower(), minor.lower())) + o_f.write("\n") + + """ Start pushing fprintf into the source resposible for generating the database table """ + o_f.write("\tuint32_t *word_ptr;\n") + o_f.write("\tint byte_no = 0;\n") + o_f.write("\tFILE *fp;\n\n") + o_f.write("\tfp = fopen(\"%s\", \"w\");\n\n" %(out_database)) + temp_out_file = out_database + while '/' in temp_out_file: + temp_out_file = "".join(temp_out_file[temp_out_file.find('/')+1:]) + o_f.write("\tfprintf(fp, \"#ifndef _%s_H_\\n\");\n" %(temp_out_file[:temp_out_file.find('.')].upper())) + o_f.write("\tfprintf(fp, \"#define _%s_H_\\n\");\n\n" %(temp_out_file[:temp_out_file.find('.')].upper())) + if section_name: + o_f.write("\tfprintf(fp, \"__attribute__((section(\\\"%s\\\")))\\n\");\n" %(section_name)) + o_f.write("\tfprintf(fp, \"const uint32_t static %s[] = {\\n\");\n" %(table_name)) + o_f.write("\tfprintf(fp, \"0x%x\", \'T\');\n") + o_f.write("\tfprintf(fp, \"%x\", \'B\');\n") + o_f.write("\tfprintf(fp, \"%x\", \'S\');\n") + o_f.write("\tfprintf(fp, \"%x,\\n\", \'A\');\n") + o_f.write("\tfprintf(fp, \"0x%x\", \'_\');\n") + o_f.write("\tfprintf(fp, \"%x\", \'C\');\n") + o_f.write("\tfprintf(fp, \"%x\", \'F\');\n") + o_f.write("\tfprintf(fp, \"%x,\\n\", \'G\');\n") + required_target_name_len = 4 + new_target = target + if (len(target) < required_target_name_len): + while(required_target_name_len - len(target)): + new_target += " " + required_target_name_len -= 1 + o_f.write("\tfprintf(fp, \"0x%x\", ") + o_f.write("\'%c\');\n" %(new_target[0:1])) + o_f.write("\tfprintf(fp, \"%x\", ") + o_f.write("\'%c\');\n" %(new_target[1:2])) + o_f.write("\tfprintf(fp, \"%x\", ") + o_f.write("\'%c\');\n" %(new_target[2:3])) + o_f.write("\tfprintf(fp, \"%x,\\n\", ") + o_f.write("\'%c\');\n" %(new_target[3:4])) + o_f.write("\tfprintf(fp, \"0x%x\", \'_\');\n") + o_f.write("\tfprintf(fp, \"%x\", \'C\');\n") + o_f.write("\tfprintf(fp, \"%x\", \'F\');\n") + o_f.write("\tfprintf(fp, \"%x,\\n\", \'G\');\n") + o_f.write("\tuint32_t version = 1;\n") + o_f.write("\tfprintf(fp, \"0x%08x,\\n\", version);\n") + o_f.write("\tuint32_t total_size = 0;\n") + for group in unique_major_groups: + o_f.write("\ttotal_size += group_%s.cfg_type.size;\n" %(group.lower())) + o_f.write("\ttotal_size += (8 + 8 + 4 + 4);\n") + o_f.write("\tfprintf(fp, \"0x%08x,\\n\", total_size);\n") + + """ Start writing component values to database file """ + for group in unique_major_groups: + o_f.write("\t/* Writing major group details to the file */\n") + o_f.write("\tword_ptr = (uint32_t *)&group_%s;\n" %(group.lower())) + o_f.write("\tfor(byte_no=0; byte_no>>> Combining test ELFs... \n"; - #Inputs $test_list_file = $ARGV[0]; diff --git a/api-tests/val/common/val.h b/api-tests/val/common/val.h index b3dbcd55..2af2858e 100644 --- a/api-tests/val/common/val.h +++ b/api-tests/val/common/val.h @@ -178,12 +178,18 @@ typedef enum { } 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, + /* VAL uses this boot flag to mark first time boot of the system */ + BOOT_UNKNOWN = 0x1, + /* VAL/Test uses this boot flag to catch any unwanted system reboot - SIM ERROR Cases*/ + BOOT_NOT_EXPECTED = 0x2, + /* Test performs panic check for non-secure test run and expect reboot */ + BOOT_EXPECTED_NS = 0x3, + /* Test performs panic check for secure test run and expect reboot */ + BOOT_EXPECTED_S = 0x4, + /* Test expected reboot but it didn't happen */ + BOOT_EXPECTED_BUT_FAILED = 0x5, + /* Test expect reboot for secure/non-secure test run. If reboot happens, re-enter same test */ + BOOT_EXPECTED_REENTER_TEST = 0x6, } boot_state_t; typedef enum { @@ -224,6 +230,7 @@ typedef enum { VAL_STATUS_INIT_ALREADY_DONE = 0x29, VAL_STATUS_HEAP_NOT_AVAILABLE = 0x2A, VAL_STATUS_UNSUPPORTED = 0x2B, + VAL_STATUS_DRIVER_FN_FAILED = 0x2C, VAL_STATUS_ERROR_MAX = INT_MAX, } val_status_t; @@ -237,13 +244,21 @@ typedef enum { PRINT_ALWAYS = 9 } print_verbosity_t; -/* Interrupt test function id enums */ +/* Driver test function id enums */ typedef enum { TEST_PSA_EOI_WITH_NON_INTR_SIGNAL = 1, TEST_PSA_EOI_WITH_MULTIPLE_SIGNALS = 2, TEST_PSA_EOI_WITH_UNASSERTED_SIGNAL = 3, TEST_INTR_SERVICE = 4, -} test_intr_fn_id_t; + TEST_ISOLATION_PSA_ROT_DATA_RD = 5, + TEST_ISOLATION_PSA_ROT_DATA_WR = 6, + TEST_ISOLATION_PSA_ROT_STACK_RD = 7, + TEST_ISOLATION_PSA_ROT_STACK_WR = 8, + TEST_ISOLATION_PSA_ROT_HEAP_RD = 9, + TEST_ISOLATION_PSA_ROT_HEAP_WR = 10, + TEST_ISOLATION_PSA_ROT_MMIO_RD = 11, + TEST_ISOLATION_PSA_ROT_MMIO_WR = 12, +} driver_test_fn_id_t; /* typedef's */ typedef struct { diff --git a/api-tests/val/common/val_target.c b/api-tests/val/common/val_target.c index ae3c2f4f..3564c7d9 100644 --- a/api-tests/val/common/val_target.c +++ b/api-tests/val/common/val_target.c @@ -18,7 +18,8 @@ #include "val_target.h" #include "target_database.h" -#ifdef USE_RAW_PRINT_FOR_DRIVER_PARTITION +/* Use raw print for driver partition */ +#ifdef DRIVER_PARTITION_INCLUDE #define val_print(x, y, z) \ do { \ if (x >= VERBOSE) \ @@ -26,7 +27,7 @@ } while(0) #else __UNUSED STATIC_DECLARE val_status_t val_print - (print_verbosity_t verbosity, char *string, uint32_t data); + (print_verbosity_t verbosity, char *string, int32_t data); #endif /** @@ -52,7 +53,7 @@ STATIC_DECLARE val_status_t val_target_cfg_get_next(void **blob) /* Sanity check signature and version here */ if ((hdr->version != 1) || (hdr->size == 0)) { - val_print(PRINT_ERROR, "Target config database Error. \n", 0); + val_print(PRINT_ERROR, "\tTarget config database Error. \n", 0); return VAL_STATUS_ERROR; } hdr++; @@ -82,7 +83,7 @@ STATIC_DECLARE val_status_t val_target_get_cfg_blob(cfg_id_t cfg_id, uint8_t **d val_status_t status; void *config_blob = NULL; - val_print(PRINT_INFO, "Input id is %x \n", cfg_id); + val_print(PRINT_INFO, "\tInput id is %x \n", cfg_id); do { @@ -138,7 +139,7 @@ STATIC_DECLARE val_status_t val_target_get_config(cfg_id_t cfg_id, uint8_t **dat 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); + val_print(PRINT_ERROR, "\tInvalid Target data config ID = %x \n", cfg_id); return VAL_STATUS_INSUFFICIENT_SIZE; } @@ -146,8 +147,8 @@ STATIC_DECLARE val_status_t val_target_get_config(cfg_id_t cfg_id, uint8_t **dat 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); + val_print(PRINT_ERROR, "\tGet Config failed with status = %x", status); + val_print(PRINT_ERROR, " for cfg_id = %x\n", cfg_id); return status; } return VAL_STATUS_SUCCESS; diff --git a/api-tests/val/common/val_target.h b/api-tests/val/common/val_target.h index c32f0a57..c8b14661 100644 --- a/api-tests/val/common/val_target.h +++ b/api-tests/val/common/val_target.h @@ -64,7 +64,7 @@ typedef enum _SOC_PERIPHERAL_CONFIG_ID_ { typedef enum _MEMORY_CONFIG_ID_ { MEMORY_NVMEM = 0x2, MEMORY_NSPE_MMIO = 0x3, - MEMORY_CLIENT_PARTITION_MMIO = 0x4, + MEMORY_SERVER_PARTITION_MMIO = 0x4, MEMORY_DRIVER_PARTITION_MMIO = 0x5, } memory_cfg_id_t; @@ -82,7 +82,7 @@ typedef enum _COMPONENT_GROUPING_{ WATCHDOG = GROUP_SOC_PERIPHERAL, NVMEM = GROUP_MEMORY, NSPE_MMIO = GROUP_MEMORY, - CLIENT_PARTITION_MMIO = GROUP_MEMORY, + SERVER_PARTITION_MMIO = GROUP_MEMORY, DRIVER_PARTITION_MMIO = GROUP_MEMORY, BOOT = GROUP_MISCELLANEOUS, DUT = GROUP_MISCELLANEOUS, @@ -195,11 +195,15 @@ typedef struct _MISCELLANEOUS_INFO_DESC_ { addr_t ns_start_addr_of_combine_test_binary; is_available_t combine_test_binary_in_ram; addr_t ns_test_addr; + is_available_t sp_heap_mem_supp; } miscellaneous_desc_t; /*val target config read apis */ +#ifdef VAL_NSPE_BUILD STATIC_DECLARE val_status_t val_target_get_config(cfg_id_t cfg_id, uint8_t **data, uint32_t *size); STATIC_DECLARE val_status_t val_target_cfg_get_next(void **blob); -STATIC_DECLARE val_status_t val_target_get_cfg_blob(cfg_id_t cfg_id, uint8_t **data, uint32_t *size); +STATIC_DECLARE val_status_t val_target_get_cfg_blob(cfg_id_t cfg_id, uint8_t **data, + uint32_t *size); STATIC_DECLARE val_status_t val_target_get_config(cfg_id_t cfg_id, uint8_t **data, uint32_t *size); #endif +#endif diff --git a/api-tests/val/nspe/pal_interfaces_ns.h b/api-tests/val/nspe/pal_interfaces_ns.h index 186a5fc7..0b506121 100644 --- a/api-tests/val/nspe/pal_interfaces_ns.h +++ b/api-tests/val/nspe/pal_interfaces_ns.h @@ -100,7 +100,7 @@ int pal_uart_init_ns(uint32_t uart_base_addr); * @return - SUCCESS/FAILURE **/ -int pal_print_ns(char *str, uint32_t data); +int pal_print_ns(char *str, int32_t data); /** * @brief - Initializes an hardware watchdog timer diff --git a/api-tests/val/nspe/val_attestation.h b/api-tests/val/nspe/val_attestation.h index d860739b..58ea59c1 100644 --- a/api-tests/val/nspe/val_attestation.h +++ b/api-tests/val/nspe/val_attestation.h @@ -19,7 +19,6 @@ #define _VAL_INITIAL_ATTESTATION_H_ #include "val.h" - #define MAX_CHALLENGE_SIZE PSA_INITIAL_ATTEST_CHALLENGE_SIZE_64 enum attestation_function_code { diff --git a/api-tests/val/nspe/val_crypto.c b/api-tests/val/nspe/val_crypto.c index 764b44a9..da1aced4 100644 --- a/api-tests/val/nspe/val_crypto.c +++ b/api-tests/val/nspe/val_crypto.c @@ -30,7 +30,7 @@ int32_t val_crypto_function(int type, ...) { va_list valist; - val_status_t status; + int32_t status; va_start(valist, type); status = pal_crypto_function(type, valist); diff --git a/api-tests/val/nspe/val_crypto.h b/api-tests/val/nspe/val_crypto.h index 6cb1b2f0..6fc3f562 100644 --- a/api-tests/val/nspe/val_crypto.h +++ b/api-tests/val/nspe/val_crypto.h @@ -48,6 +48,7 @@ #define PSA_KEY_LIFETIME_INVALID 0xFFFFFFFF #define PSA_KEY_USAGE_INVALID 0xFFFFFFFF +#define PSA_HASH_ALG_INVALID 0x01FFFFFF #define PSA_ALG_INVALID 0xFFFFFFFF enum crypto_function_code { @@ -98,6 +99,7 @@ enum crypto_function_code { VAL_CRYPTO_ASYMMTERIC_VERIFY = 0x31, VAL_CRYPTO_KEY_AGREEMENT = 0x32, VAL_CRYPTO_ALLOCATE_KEY = 0x33, + VAL_CRYPTO_COPY_KEY = 0x34, VAL_CRYPTO_FREE = 0xFE, }; diff --git a/api-tests/val/nspe/val_dispatcher.c b/api-tests/val/nspe/val_dispatcher.c index 29c72dc2..86755187 100644 --- a/api-tests/val/nspe/val_dispatcher.c +++ b/api-tests/val/nspe/val_dispatcher.c @@ -126,7 +126,7 @@ int val_copy_elf(uint32_t saddr, uint32_t *info_addr) **/ val_status_t val_test_load(test_id_t *test_id, test_id_t test_id_prev) { -#ifndef TEST_COMBINE_ARCHIVE +#if (TEST_COMBINE_ARCHIVE == 0) test_header_t test_header; addr_t flash_addr = combine_test_binary_addr; @@ -260,7 +260,7 @@ val_status_t val_test_load(test_id_t *test_id, test_id_t test_id_prev) **/ val_status_t val_get_test_entry_addr(addr_t *paddr) { -#ifndef TEST_COMBINE_ARCHIVE +#if (TEST_COMBINE_ARCHIVE == 0) *paddr = (addr_t)(((val_test_info_t *)g_test_info_addr)->entry_addr); #else *paddr = g_test_info_addr; diff --git a/api-tests/val/nspe/val_entry.h b/api-tests/val/nspe/val_entry.h index 2b885e0c..2236016e 100644 --- a/api-tests/val/nspe/val_entry.h +++ b/api-tests/val/nspe/val_entry.h @@ -21,7 +21,7 @@ #include "val_framework.h" #define PSA_ACS_MAJOR_VER 0 -#define PSA_ACS_MINOR_VER 8 +#define PSA_ACS_MINOR_VER 9 /** @brief - PSA Test Suite C main function, does VAL init and calls test dispatcher diff --git a/api-tests/val/nspe/val_framework.c b/api-tests/val/nspe/val_framework.c index 1bc3b128..4f6163c1 100644 --- a/api-tests/val/nspe/val_framework.c +++ b/api-tests/val/nspe/val_framework.c @@ -111,11 +111,31 @@ val_status_t val_execute_non_secure_tests(uint32_t test_num, client_test_t *test return status; } - if (boot.state == BOOT_NOT_EXPECTED || boot.state == BOOT_EXPECTED_CRYPTO) + if (boot.state == BOOT_NOT_EXPECTED || boot.state == BOOT_EXPECTED_REENTER_TEST) { - val_print(PRINT_TEST,"[Info] Executing tests from non-secure\n", 0); while (tests_list[i] != NULL) { + /* + * Reboot have been expected by test in previous ns run, + * consider previous run pass and jump to second test function + * of the same test if available. + */ + if ((boot.state == BOOT_EXPECTED_REENTER_TEST) && (i == 1)) + { + val_print(PRINT_DEBUG, "[Check1] PASSED\n", 0); + i++; + continue; + } + + status = val_set_boot_flag(BOOT_NOT_EXPECTED); + if (VAL_ERROR(status)) + { + return status; + } + + if (i == 1) + val_print(PRINT_TEST,"[Info] Executing tests from non-secure\n", 0); + if (server_hs == TRUE) { /* Handshake with server tests */ @@ -204,6 +224,17 @@ val_status_t val_switch_to_secure_client(uint32_t test_num) if (boot.state != BOOT_EXPECTED_S) { + /* + * Reboot have been expected by test in previous s run, + * consider previous run pass and jump to second test function + * of the same test if available. + */ + if (boot.state == BOOT_EXPECTED_REENTER_TEST) + { + test_info.block_num++; + val_print(PRINT_DEBUG, "[Check1] PASSED\n", 0); + } + status = val_set_boot_flag(BOOT_NOT_EXPECTED); if (VAL_ERROR(status)) { @@ -399,7 +430,7 @@ val_status_t val_err_check_set(uint32_t checkpoint, val_status_t status) } else { - status = val_get_status(); + status = (val_get_status() & TEST_STATUS_MASK); if (VAL_ERROR(status)) { val_print(PRINT_ERROR, "\tCheckpoint %d : ", checkpoint); @@ -451,15 +482,16 @@ void val_test_init(uint32_t test_num, char8_t *desc, uint32_t test_bitfield) 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); + val_print(PRINT_ALWAYS, "\tSkipping test. Required isolation level is not supported\n", 0); return; } +#if (WATCHDOG_AVAILABLE == 1) /* 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); + val_print(PRINT_ERROR, "\tval_wd_timer_init failed Error=0x%x\n", status); return; } @@ -467,9 +499,10 @@ void val_test_init(uint32_t test_num, char8_t *desc, uint32_t test_bitfield) status = val_wd_timer_enable(); if (VAL_ERROR(status)) { - val_print(PRINT_ERROR, "val_wd_timer_enable failed Error=0x%x\n", status); + val_print(PRINT_ERROR, "\tval_wd_timer_enable failed Error=0x%x\n", status); return; } +#endif val_set_status(RESULT_START(VAL_STATUS_SUCCESS)); return; @@ -483,10 +516,20 @@ void val_test_init(uint32_t test_num, char8_t *desc, uint32_t test_bitfield) void val_test_exit(void) { - val_status_t status; + val_status_t status = VAL_STATUS_SUCCESS; + +#if (WATCHDOG_AVAILABLE == 1) + status = val_wd_timer_disable(); + if (VAL_ERROR(status)) + { + val_print(PRINT_ERROR, "\tval_wd_timer_disable failed Error=0x%x\n", status); + val_set_status(RESULT_FAIL(status)); + return; + } +#endif - val_wd_timer_disable(); status = val_get_status(); + /* return if test skipped or failed */ if (IS_TEST_FAIL(status) || IS_TEST_SKIP(status)) { @@ -509,9 +552,12 @@ val_status_t val_get_last_run_test_id(test_id_t *test_id) 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}; + boot_state_t boot_state[] = {BOOT_NOT_EXPECTED, + BOOT_EXPECTED_NS, + BOOT_EXPECTED_S, + BOOT_EXPECTED_BUT_FAILED, + BOOT_EXPECTED_REENTER_TEST + }; status = val_get_boot_flag(&boot.state); if (VAL_ERROR(status)) @@ -519,6 +565,8 @@ val_status_t val_get_last_run_test_id(test_id_t *test_id) return status; } + val_print(PRINT_INFO, "\n\tboot.state=0x%x", boot.state); + for (i = 0; i < (sizeof(boot_state)/sizeof(boot_state[0])); i++) { if (boot.state == boot_state[i]) @@ -585,7 +633,7 @@ val_status_t val_set_boot_flag(boot_state_t 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); + val_print(PRINT_ERROR, "\tval_nvmem_write failed. Error=0x%x\n", status); return status; } return status; @@ -604,7 +652,7 @@ val_status_t val_get_boot_flag(boot_state_t *state) 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); + val_print(PRINT_ERROR, "\tval_nvmem_read failed. Error=0x%x\n", status); return status; } *state = boot.state; diff --git a/api-tests/val/nspe/val_interfaces.h b/api-tests/val/nspe/val_interfaces.h index b41aab1b..8e9c56bd 100644 --- a/api-tests/val/nspe/val_interfaces.h +++ b/api-tests/val/nspe/val_interfaces.h @@ -25,7 +25,7 @@ /* typedef's */ typedef struct { val_status_t (*print) (print_verbosity_t verbosity, - char *string, uint32_t data); + char *string, int32_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, diff --git a/api-tests/val/nspe/val_peripherals.c b/api-tests/val/nspe/val_peripherals.c index 9f153e00..dccc4e40 100644 --- a/api-tests/val/nspe/val_peripherals.c +++ b/api-tests/val/nspe/val_peripherals.c @@ -56,7 +56,7 @@ val_status_t val_uart_init(void) - data : Value for format specifier @return - val_status_t **/ -val_status_t val_print(print_verbosity_t verbosity, char *string, uint32_t data) +val_status_t val_print(print_verbosity_t verbosity, char *string, int32_t data) { if ((is_uart_init_done == 0) || (verbosity < VERBOSE)) { diff --git a/api-tests/val/nspe/val_peripherals.h b/api-tests/val/nspe/val_peripherals.h index dfea1431..d1b75389 100644 --- a/api-tests/val/nspe/val_peripherals.h +++ b/api-tests/val/nspe/val_peripherals.h @@ -21,7 +21,7 @@ #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_print(print_verbosity_t verbosity, char *string, int32_t data); val_status_t val_spi_read(addr_t addr, uint8_t *data, uint32_t len); 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); diff --git a/api-tests/val/spe/pal_interfaces_s.h b/api-tests/val/spe/pal_interfaces_s.h index a9edd70a..2ca9e28b 100644 --- a/api-tests/val/spe/pal_interfaces_s.h +++ b/api-tests/val/spe/pal_interfaces_s.h @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,7 +19,7 @@ #define _VAL_PAL_INTERFACE_APIS_H_ -#include "val/common/val.h" +#include "val.h" /* Peripherals APIs */ @@ -37,7 +37,7 @@ void pal_uart_init(addr_t uart_base_addr); - data : Value for Format specifier @return - void */ -void pal_print(char *str, uint32_t data); +void pal_print(char *str, int32_t data); /** @brief - Initializes an hardware watchdog timer @@ -90,4 +90,19 @@ int pal_nvmem_write(addr_t base, uint32_t offset, void *buffer, int size); @return - error status 0:SUCCESS, 1:FAIL */ int pal_nvmem_read(addr_t base, uint32_t offset, void *buffer, int size); + +/** + @brief - Trigger interrupt for irq signal assigned to driver partition + before return to caller. + @param - void + @return - void +**/ +void pal_generate_interrupt(void); + +/** + @brief - Disable interrupt that was generated using pal_generate_interrupt API. + @param - void + @return - void +**/ +void pal_disable_interrupt(void); #endif diff --git a/api-tests/val/spe/val_driver_service_apis.c b/api-tests/val/spe/val_driver_service_apis.c index 46a31622..05b14fde 100644 --- a/api-tests/val/spe/val_driver_service_apis.c +++ b/api-tests/val/spe/val_driver_service_apis.c @@ -17,7 +17,7 @@ #include "val_driver_service_apis.h" -#include "val/common/val_target.c" +#include "val_target.c" print_verbosity_t g_print_level = PRINT_INFO; static int is_uart_init_done = 0; @@ -39,10 +39,10 @@ val_status_t val_uart_init_sf(addr_t uart_base_addr) @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 + - data : Value for Format specifier @return - error status */ -val_status_t val_print_sf(char *string, uint32_t data) +val_status_t val_print_sf(char *string, int32_t data) { if (is_uart_init_done == 1) { @@ -199,3 +199,47 @@ val_status_t val_init_driver_memory(void) return VAL_STATUS_SUCCESS; } + +/** + @brief - This function returns the driver reserved mmio region base + @param - base pointer + @return - val_status_t +**/ +val_status_t val_get_driver_mmio_addr(addr_t *base_addr) +{ + val_status_t status; + memory_desc_t *memory_desc; + + status = val_target_get_config(TARGET_CONFIG_CREATE_ID(GROUP_MEMORY, + MEMORY_DRIVER_PARTITION_MMIO, 0), + (uint8_t **)&memory_desc, + (uint32_t *)sizeof(memory_desc_t)); + if (VAL_ERROR(status)) + { + return status; + } + + *base_addr = memory_desc->start; + return VAL_STATUS_SUCCESS; +} + +/** + @brief - Trigger interrupt for irq signal assigned to driver partition + before return to caller. + @param - void + @return - void +**/ +void val_generate_interrupt(void) +{ + pal_generate_interrupt(); +} + +/** + @brief - Disable interrupt that was generated using val_generate_interrupt API. + @param - void + @return - void +**/ +void val_disable_interrupt(void) +{ + pal_disable_interrupt(); +} diff --git a/api-tests/val/spe/val_driver_service_apis.h b/api-tests/val/spe/val_driver_service_apis.h index 9614e352..251a0655 100644 --- a/api-tests/val/spe/val_driver_service_apis.h +++ b/api-tests/val/spe/val_driver_service_apis.h @@ -20,20 +20,13 @@ #include "val.h" #include "val_client_defs.h" -#include "val_service_defs.h" #include "pal_interfaces_s.h" -/* "psa_manifest/.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 "psa_manifest/driver_partition_psa.h" - -#define USE_RAW_PRINT_FOR_DRIVER_PARTITION 1 +#define DRIVER_PARTITION_INCLUDE +#include "val_service_defs.h" val_status_t val_uart_init_sf(addr_t uart_base_addr); -val_status_t val_print_sf(char *string, uint32_t data); +val_status_t val_print_sf(char *string, int32_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); @@ -42,4 +35,7 @@ val_status_t val_nvmem_read_sf(addr_t base, uint32_t offset, void *buffer, int s val_status_t val_nvmem_write_sf(addr_t base, uint32_t offset, void *buffer, int size); val_status_t val_driver_private_set_boot_flag_fn(boot_state_t state); val_status_t val_init_driver_memory(void); +val_status_t val_get_driver_mmio_addr(addr_t *base_addr); +void val_generate_interrupt(void); +void val_disable_interrupt(void); #endif diff --git a/api-tests/val/spe/val_partition_common.h b/api-tests/val/spe/val_partition_common.h index df36402d..76b5c8c6 100644 --- a/api-tests/val/spe/val_partition_common.h +++ b/api-tests/val/spe/val_partition_common.h @@ -30,16 +30,8 @@ #include "val_target.c" #include "val_service_defs.h" -/* "psa_manifest/.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 "psa_manifest/client_partition_psa.h" -#include "psa_manifest/server_partition_psa.h" - __UNUSED STATIC_DECLARE val_status_t val_print - (print_verbosity_t verbosity, char *string, uint32_t data); + (print_verbosity_t verbosity, char *string, int32_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 @@ -52,7 +44,7 @@ __UNUSED STATIC_DECLARE val_status_t val_process_call_request(psa_signal_t sig, __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); + (test_info_t test_info, 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); @@ -70,6 +62,9 @@ __UNUSED static val_api_t val_api = { .ipc_close = val_ipc_close, .set_boot_flag = val_set_boot_flag, .target_get_config = val_target_get_config, + .process_connect_request = val_process_connect_request, + .process_call_request = val_process_call_request, + .process_disconnect_request= val_process_disconnect_request, }; __UNUSED static psa_api_t psa_api = { @@ -78,6 +73,17 @@ __UNUSED static psa_api_t psa_api = { .connect = psa_connect, .call = psa_call, .close = psa_close, + .wait = psa_wait, + .set_rhandle = psa_set_rhandle, + .get = psa_get, + .read = psa_read, + .skip = psa_skip, + .write = psa_write, + .reply = psa_reply, + .notify = psa_notify, + .clear = psa_clear, + .eoi = psa_eoi, + .rot_lifecycle_state = psa_rot_lifecycle_state, }; /** @@ -88,7 +94,7 @@ __UNUSED static psa_api_t psa_api = { - 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) +STATIC_DECLARE val_status_t val_print(print_verbosity_t verbosity, char *string, int32_t data) { int string_len = 0; char *p = string; @@ -108,7 +114,7 @@ STATIC_DECLARE val_status_t val_print(print_verbosity_t verbosity, char *string, p++; } - psa_invec data1[3] = {{&uart_fn, sizeof(uart_fn)}, {string, string_len+1}, {&data, 4}}; + psa_invec data1[3] = {{&uart_fn, sizeof(uart_fn)}, {string, string_len+1}, {&data, sizeof(data)}}; print_handle = psa_connect(DRIVER_UART_SID, 0); if (print_handle < 0) @@ -301,23 +307,21 @@ STATIC_DECLARE val_status_t val_process_disconnect_request(psa_signal_t sig, psa /** @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 - test_info_t : test_num and block_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) +STATIC_DECLARE val_status_t val_execute_secure_tests(test_info_t test_info, 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 from secure\n", 0); + int i = test_info.block_num; while (tests_list[i] != NULL) { + if (i == 1) + val_print(PRINT_TEST, "[Info] Executing tests from secure\n", 0); /* Handshake with server tests */ test_info.block_num = i; @@ -513,7 +517,7 @@ STATIC_DECLARE val_status_t val_set_boot_flag(boot_state_t 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); + val_print(PRINT_ERROR, "\tval_nvmem_write failed Error=0x%x\n", status); return status; } return status; diff --git a/api-tests/val/spe/val_service_defs.h b/api-tests/val/spe/val_service_defs.h index 51b6d5cd..b499b5d3 100644 --- a/api-tests/val/spe/val_service_defs.h +++ b/api-tests/val/spe/val_service_defs.h @@ -1,5 +1,5 @@ /** @file - * Copyright (c) 2018, Arm Limited or its affiliates. All rights reserved. + * Copyright (c) 2018-2019, Arm Limited or its affiliates. All rights reserved. * SPDX-License-Identifier : Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,16 +20,25 @@ /***************** 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. - */ - /* psa/service.h: Secure Partition API elements. Only accessible to Secure Partition */ #include "psa/service.h" +/* psa/lifecycle.h: Contains the PSA Lifecycle API elements */ +#include "psa/lifecycle.h" + +/* "psa_manifest/.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 "psa_manifest/driver_partition_psa.h" +#ifndef DRIVER_PARTITION_INCLUDE +#include "psa_manifest/client_partition_psa.h" +#include "psa_manifest/server_partition_psa.h" +#endif + +#include "val_target.h" + /* struct of function pointers to uniqify nspe and spe client interface for test */ typedef struct { uint32_t (*framework_version) (void); @@ -42,21 +51,38 @@ typedef struct { size_t out_len ); void (*close) (psa_handle_t handle); + psa_signal_t (*wait) (psa_signal_t signal_mask, uint32_t timeout); + void (*set_rhandle) (psa_handle_t msg_handle, void *rhandle); + psa_status_t (*get) (psa_signal_t signal, psa_msg_t *msg); + size_t (*read) (psa_handle_t msg_handle, uint32_t invec_idx, + void *buffer, size_t num_bytes); + size_t (*skip) (psa_handle_t msg_handle, uint32_t invec_idx, + size_t num_bytes); + void (*write) (psa_handle_t msg_handle, uint32_t outvec_idx, + const void *buffer, size_t num_bytes); + void (*reply) (psa_handle_t msg_handle, psa_status_t status); + void (*notify) (int32_t partition_id); + void (*clear) (void); + void (*eoi) (psa_signal_t irq_signal); + uint32_t (*rot_lifecycle_state) (void); } 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_status_t (*target_get_config) (cfg_id_t cfg_id, uint8_t **data, uint32_t *size); + val_status_t (*print) (print_verbosity_t verbosity, + char *string, int32_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_status_t (*target_get_config) (cfg_id_t cfg_id, uint8_t **data, uint32_t *size); + val_status_t (*process_connect_request) (psa_signal_t sig, psa_msg_t *msg); + val_status_t (*process_call_request) (psa_signal_t sig, psa_msg_t *msg); + val_status_t (*process_disconnect_request) (psa_signal_t sig, psa_msg_t *msg); } val_api_t; #endif

!O$ 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 literal 587853 zcma%iV_;-m)^5y>Z5tKawry2x+fK)JI#zdVr_(VywmP=0n|I!sJM&}i_uccO&Z&d7 z)_&I7&$G|oRTN5M5)90YoCp+ucBW<#;8;00h?$5TOso<3_yBTN_CRB2D{r6~F*87p zn4OE0i=7pqNX*K?4G<^hW@csP0w@r(vH{foy2i}L0+1rsC1z*iAm(D?)E5vy0NR`V zWs3Mew@)9O-2e6=1~hdr11cFi+qryt6m_t5v$J<0W(O!a1I?^VT^*c>nOOt`0OIzh z&R&kLpRPGSpH%@!SlPM)odFWI#;(A>O#r0DbooBR;AD{y7UksNU}oYFV-;tX;A9o% z6p>(M6JZzUWE0?HVq#)rVHe>R73N@NVPO;FV&P(AWs%@wX5|v);E-Sv&<9A{n*lv_ z|3ZSD>#uL-4xet>|KU~+Xm8L*^rY%HIkcsT+A zS}H(ufTk`pF)K0iCn7Gc&Ol>31USnL6B9EdWFsRJBcmpW#IKq{l_^a z6?|fDE*8K);o)Rv`K%Idb|!!-F&78Zznh(q3M0cK@tZ!tRAc%5ox=(M(N*h&*PH5( zCgcBZ04n~tm-oxaPn68MSicNhia$H5?Lhu#y~=92(nDBiWX)J)Gnpg0Lh@K%3QakY#%VK8)Jw=L9K3*3fO} zDM132Z$$z&R4UR9oUhZ;>C~cxUg9Xrl~F6m*qZeGc9fp%rmR*doZ>e0_fxwjSL+?bV^lTM81?$_B3Iz8@QILiJl0_nt<5X6dcTNKa4b$c3<5FqC zk8P7txu5VO5xqBoPYq8A@kt2@1NdC%o0sRYTjs zLary4h-(fF)F%;Ut8iW8YYY8^+2enln~@4jvJDRer3!U5pj#0({76K94O2A4RZv^l zS%U^a3&2`;`aMw(hEbJz%)pFXXr(M_-@0{#oHHaYI`)0?>0M!shJH7sTe8VJYV)Ub-{X>UqW%8lh%hp%U9o-on*aw zWZc`#C1FD+$QMqcAtY02caOhquv)LwGhG_S)I}f^`H61)oJ!kPT^ex}0vxXB=D*ND z4L_@>qn!H&;t>cc8s1K+M5Z(Gh455S5Sz zX#C&a!ptG?x&5nCsk$25n;AQs{i`oAvv2_vssETs)`pFnSLG|lV3;$P@{VPmdK5@c zY!VFKgFvuh(a`{uh#8ofSUCVH#B86Q?Iiwo|M}_suS0d}d^zW!1<=489JN-BhU+Fi zJ?b6>0|mEeVKWc~jgMzg7#e^EG26d+`)^A7mlzn^TTrjK(E#LuuEu7@uExa7%ztyS zg0UUYg_xC#=^y;3=;mtssT+Tt`1`Vwu?0~6lj8yEF2H|X1pIaK^A?W3xJBh}sucaS zY3pF|pYH!dCq!(4Kr;~sPu;(~uyeCBvT(A0KF!I^!pOqK%Fe9+sU7yNpD2G?<^0>K zsI!CP|M-NYvxA%CU#g=D5CghfnF3WLMF6S*HD_ac7e`}fpuMTr-=Y2E#?Ktde`=EP z|7h}4$UbBEY#7@=8m0ntad2}s1^%UN5})z?wPX53=I;O{K4pRHFTGXyw5{aqV5$mq z)dhSiGC&RJ=?ajx`vgh!Q#C#X#`RO@h?)M5<}d9210~MC5cy}4{);S1SUJ16ev0zH z3W|f7`%|A}wE6g_-j|$2K8A?!t z%b_szL(O5-E$Zp+WynD1niv1LDVw1iy%%OJBCLw;80S?yXg3KjjmIYT*0I%3L!TlF zZbk*7&@4r`Y}*@XUtV6kLY`BtZYCEdjDd(mA5aCJ7HyYf0ba!+#olzFJy4m|4^Ok& zIJ8}lQf8NVSW9S@v`GsY0 zI3Bad^59M$tKpIk?CiLO^xfr0WrDj{ZGo!|{T?TM$ zXIFhKS)<6VGdtil-$;btrC>>as~DF?C{X-yQo(aLSuw68@&De(jja@EIzr9uAf2dF zbPGj=#)9cW0Z;lZne43hq%gZb$kV1__OW|Si-|10heOqex5(bKR* z11sY-2mMa|m z@Vagfb7*sEVFB#9z2~~_Zn-AE!G{YvTQ-C?AJ9ig6#yubd0}(~wf*=l;nPd47^W8( zxa{F{dE2OV2dpB+8@n_KXL>srV)Yyri)qq><5p!~&YL2d?3+k&N%b)Dc||J>Qhq-- zRv}(h*CaH`W){q3JFUNR3~LYw&~ZomeqkD#x+B~V364)Pawyk-Gk7Nw1lTE`2R^4w z2F~f{_X@Z^m|Rm?%csNjuEN4O&)m#i)j&<;M_xUFK=l^Hhs7VN_BX~IZv%1^oc#Ag zy0sV5O7Fymt@Xd{WRGG{9%UieX=ffa5umUwJ9YnFQ}V`cFH~uPLoHC>t{gAcIvc5P=;j`?y4VAj&(y<(?rrP{OPzVnR{3A^!1Di<2d4kX zL)QGIwY%V;#tYo}JjWo%q{^mdP#8XcWGc-0;q$dYCBKSO>)gCnIsW`zTxv(c@9jsO zI=DgFNIoL817DxOFj$a-j)e59n62gJ&NQW$6t4CAXI?^LVf8EJNZpC#86TTAO_-a# zuslCSQWuzppK;W==|3yXa3JQ?vNy5~JhWHN0*ia8^&LjmSYyc%F1hGk{K@9Rwxj=w`F{+v{|k-( zv-UqVm;K+x-vFPn^|Ldwn;#S~#}s--aV1A&7(-h?&o!M84N%`Oh96QRqCUX zjcl2I-1xyq)&j<>m`feSYz>85vB1=U+y_<@D%TTdP_{4_6;bM?WMV%OV_lrkPhRs@ z!$~;3mR=)&Wl4$TER8PCa2vjytO0J~KvI*!M!9$`!YilX;}=?)ipydBdwQXfN_M}v z*e4xeQ3%cK=$r)8=Lq_Nshh^CH zqE0)P3KZL}psJK{Ih-QE=_OB7>-Bgpyx52Au`6|;xadW-rqG#S7R$)6!`3%k^ZD+Z z>KSreJPU0sRb?Itu!qFITjT=C+(PMFi)36;xxsWY;(T9$E_N~0q%O8Aob44D9X+i346gIEIBCo%R-)T$qC5w4$q&R29&R@%P3|^swQi!c0y9uLq8|l zJ2mXoa`l>-=9-dc$e0#kLY~nXAY%YXmkgtFNtSF`t}DOv_Tx(y6heXtC6!)x@MLFZ zEZ+#YRPz@hf4FaxrKPila#4n2%%D=rhC+~TtOBPGMxz2Br?`fF??0a;+XS5P2N(L@ z3Mng+oysC=^k@!O4p2C1H-4Rh8fubncET`%?A;%Er8!PRHY#${Xf&IWPtZ&PMjL&wn2)TqO(lt*!`5S`irB z*jB)rQ^Is=ps&y&$Sp{|)#NmpKw9B9EK4hWcUdi}#{4mv8`cEdal-q33bkwT_5_$! zfV;IApwF)zF-Ko{@e1B5iM`p|b?6zWdyU+Dx?$ZLxukW2l>yX|{`N(jFd5f!TomSh)1V>2 z2pRHF=r+p&u3n5{qro1=2fw?!=uHGo47E2o8f?`>8bjA|Bw3Fl)MGaQ|=|9g9tKqfV1$72wfA=VO^nBpYTWd zNHhlun<_ebe$I#dQ?(teS9#G+UUE3frC~$!fV{a#WvqVVWz0LX=$lv+BXrMnqpMGF z(-x(h1M6t8pmzt(YY!1zgko`&to)^s>1%batc7VY`XRTGCwrDr#h3p|Z+{Jl|KFq3 zf6^Pv=iA4>=18h6fcMd{(b07n<&CL@9_JE0uF7v(I;#KG`d`yC|EqO2RyOW`y>>Bw z&QCD2a{t?1C$obylvdW}B};gjq`C}sHxAh!a%3nm?3~fa^lcj>K{92SbCqSH8e#K1 z@?Q%@xw~l8@14k7L)|M+B^ku+(=``)-C!JTrq`1QSe_lJBHO0Phi^> zAPkAK*Q$P*bS=?aT6_usDugX4{%(fY9%<^1gvrW>^7|T%{@ip8hGmE?=b#Qe3<%owL zoqww`|IRV-)W3N%^V6Xq?B{xGvWoz*|H_>QA6W#x$Tx4n426Nm4n7IM3}pKlBq$#E zCG12hd#l~x{)xl#2u0YSMZLrE4llFI6jahk@BMDr^x5DV#^nMqA((G?_B&4@D&6Zs zU_4Cz2IkO0!b+^p1hZP63xxXsvY{<7>21NWh!Jl`*;XgH8KQ*2>b#8nneXHXK@~V^ zEr04{Y}2+$;HrQ#`VH}`3q;u#f5+}fS15v^y`NDMvT+l$vy`t=+y$U^ThKwegs{j>Pvlx@fpF1MF`o$;T1+l#>MjB*X4OCkLA~=R>uO%xEs8)5=qP{manNw{ynJ)eWYlzUV; zjuA!8IRE%>;GTfbn2y&CWUt4SAe;)S@7LuN>{ZqK7rW7f84P}T34NK$Kwhh4pj4?P zx&Cg_8SdLL^&=1kb7e53!)Y|7!>hV5dvPaa3lg0-IF^HlimfLy;N844fz0G=jYJGf z7b6>4)${VwPu&flrU;DS*3I8&EPT9YX~pv^Mt=`|>n$I4nNa|k^8g7^XUkzGrp&`-2xLtFrKW@y1hqkC#y5UHCDzq9{0zgZXI&Ik2I zN94vfhIB@ED}XRQ)`gD?y09A;uRsdOpYGng+BCdOoG?9wyEGP~hBhO6iq=?dIDaSc z5d4@1BQ^yZsu_wn%e&9EcnE=x#tS0R4G*7T$t~Ne7Jj3PY8kqfR&Q|xRxkn?6hx!t z7-jdRBn*KyeO`*Sa#E?(Rp`o1iTC$^5$Dead|{vIm0-||x2 zR%tnD0L4Wb@3eW2+h+5c4m~r+QwxW$Mh?hYkv=-9Tq4ggdnPC@i$XU+n^;uM-LVL|EGHVM%%%(JUNOXMxaF}p5w{_#C<}>MkWAFQh@X3)J^MZDBx93 zp@(xxmReHSHcz+iV)_Gv>ouD88)Zv!6xDAZ6u&>JL;U&@sj`0vOTww6nrRap z!~P1qHp2pAbSBzX5|m@3^K{NJaldpQ28iE`d$72 zVL_{ZUUVt&=UN*QqLUR?KO4I|6GoxAsFF7=no_rbOyPO{gnCr{PPCa8Y$~g~byC@F zv7+bg#%nwXh|lxj4?a7P6Zr~Qsoc5@ROV3@tMYK)0g5eJu>}EQ<2+#zLZsG2?B#{i zY_M-qnHhr1r$ac?ex-RJa&myVaYwI;2~eZ6T8dkCpD*!V^R+2->2YwR?=@ z=YeCaLCA^pdGEqw85scjrV)C1kJGw*YQY%Dk_E32^xulBD%sA-qf zp-W0B?PKVX^ZYKJYX5g)%LAaaptQ{!1eS!YRtL#x5PGuARfKdO5uWp)I4+(QW^`)m znkHL+C1>^uyrX%$bleS+>(#-T+Hb^E9T~#953xfbB~roYCFolyGUS=CF!Wmbd z+?vKTwmFT=zzB2n4exvLfQ1yHDY^JUgjf7zHO^G^VUPM(WGN9v>jm8bVS(*8zt?Mg zExa&X%L{on{6BF)Mt|ApmoapxI+_k{|!>@_pPtl4}(IO#{Z4!0{lT2gi2xI}u$ z@5HLOkA7F>m6A|*8etABAL%!{%bzSy=Z6tAye_qN*H5od+9}UYE8)x7bv*3_g7CjZ zyya`$pM^^c>tRTA!GNno+%qUfvD(T>#*|C9H!Ogid{f|~WfLvb_oT79$o#HoB0@5$ z@J4=kqLCA;5|E$Nc*^jWaJX0_7TW`00Yk92QWn8C=YtQ=)~B@37ddLUcD22zphj5) z17u=?=L&O{<>nS;VGKdaVC2-^FI?RUxbK5PUfxAHGrQk(mVA!oS;#C^`jvCJK~L;d zlUTkVwUmC&fg_%^S0#vi$A{+V3+;rn#k+d}|B?mWrVMI4-y4lU~qF#f@>8vhVCw zQ0NSVB?Xp@qqp-SYIl6?Rku4tV%U6~%Ci9xN%8JH2!&{|Rf>w%Mq8eV~br0fwOr~GZr42d} zha@*JleF&iE6T+p1NQPLFx;9ianvuAvoKD`g`+rtR38Jo$A)V(oboypVIMO6(2-FX z$PJvv8Mr+mT(LXP$KV2?B#dtd?nlwuFB)J9K2GuEb0Bi`#@FY<_Ys{+%aH4gbVO~Y zx#f&>;+E%Xge+^2JvG33S0Bp6`!v>HIG~DStJIyot^inG01%H7e0|?zQXWQLg5YA` zBz}|)J*h0=rm?kXxImsMGQSpL*>qCxKby?__D9+x+s1j>MQU55$9|GqG~Hsyaosb2u`% zZ1ViGMR2XflzO5Fo7cfo*U#70Wi2;ESAUlGWx2>$R5&B;u0gZdLayPDz`#vb!mzGT zWbo#B?~WQWPwtEq%)YA+n5%_Kl(gU4RSASIfwSuL@W?pu_}LLkHWVPID}~8?rY#{; z5+s>fqxrt#qTh$FB4yG7J@VF@8eEwloZQNF1H(zATA_eNJbN{o+a)vi!lQb8e)gqhj=zUSOH%d4NMk)YxFfu+T!=8oVnxsgprY z_&})v+7bMFkU<7|rd9c{VZ<5!znva=%N z3ZU8>oSAgC3tPmgG3m9d_EB7|AjHFT+Qca z3!RUj<25KMi&YRf=a=nXno`TyZ$$ebakhRQ4TB?|U00vx^f&_#lH21}XiPX+*$%ia z?@d8Nfx0Fk!P7SPhpEeL(Fv!xnS@5d0NjjSVt5zBo<5y*R0KcA|SvX(8KFkA?l&RAGj%wUkc~~DIGT3$eLL_prUnP z=f_6XxS@c~MC}w5N3UVR@;&j4edg|fjQz^WEC(wN4z`C}TWpQG2IL(aBQ|UY46c`S z<`PGF9!YkrfepTfLmYkHSrw?mvWc~#u&S?a_lLyYnB9kTnRRCAP0p}*Te}-)u6Vop zFy|*F@R{#|q>uGz66jTuRR$(+CEad0Zm75U9WYe_fPe((6~V>XO7_a)uuLJ^aZ3MJ z$0`-@)=8}oO2_g-shE$e$;ED@QmU7CC8XdOi)*Cz7Afa(JeiL#2JS>B%DH<)Q+IIM zqAEv>tW1Rf&~@2Avu_NMGiAOhow_{r5l%`G9|t8xwiwv=Ue%_BB&-40SiRJeD5fSs zq{`bV9;HCG3f*rcQpQ~)s|eE@v)D1tR-W({r8K12{sP?PXyHrGj>^*t$zSlH&6MIw zLvID{*5p=Fvfp_e$ka0xbbgP!{A#_em27YO~WC~lDa!3EYItw`=t^7(Ek=N(4i&BmHX^RZyb$Tgx&;OKuwuQzlznqW9 zFwGzA3+$ddNKo}x-_iEUXPGM5N*G1jKB5881&^;T@!tzwldT)+{9hhRAW+tw3%v0W z_JSP4d2S`qt?uVMMXN_9;RuiCOnfoVz?wV}`*T}lPB+Y)uZ@cRDccS)GQ#zuf$kNv zCnwBj5)19NP86rAQS=`HX=c1lBztZT$;N!F3fKn6$RIBk3nq^|;5q;>G;X8)^A_hU z!c+AL1DHJwF#;N81sI~e;CMK)T9>|iU%DrFg5uqv-$@e~8j{vT()L|3rf+c7&Qz?| zEO6x8x_iqBedQOcvB@Gm`NddJ^0 z+Eu=m13g%vX8Wlvn8|P%oS-6qJY;WZ0bS<0|B4ekTFv-I)x|vp>+oY(sFYME{XBr-3A_z6g z4Ott0j)Izt{PDtJWZVW*UvpV}<^*TkdLoD>(K`>;_53dJkX=2oJT#sbR%Y76j#awu zYCxvPV=TXi7|v6+Z+rQc+x`yjz~G~;2r4fJKQbEv9%zYrh`b8}OX8+dgVtC`^J-O% zZXXIjg_8*wEU9)jg>|oY)I4{X0ocagx+&c}E3gu*Q`89$NLNxovpzE7cCY=~V&U7T zS)H1hAwEFo5Zd)PnhsaQK%wx{c0;6CAjCVnvTJzL8mjvMFO{s+@hU>V%RZc?Lg0>C zWMf_j0%;TFFW+UE)ji& zyIOnr{M(pI*M3jYOA4zW>=$!y2p>4~mS;xQh2wlg;7bE{O@4HG?Q1UqFIh;5$*SXy z&MK%7&HK%gnhb`8p>Ba}*|%{_KNK*S#g4>?Q^{N~nmP+|$W2 zX(&+*kaU&dL(S)1cI;GIcE^;dOCINXctF^P7-PyIK>tcI`ndoiIcRm)Yvq@?ZOkOQ z`&m6eoRf8~S#-wEC`Cpl&{w>e&@0|^P--8{iArCNth%ZYHwsy=f#HNpBwDKi`X|0@ zurVUU^y%20E)hc9qWl<)=3#Hovs&rmj)u34$ZLt`i`MbmTtDZ44!g+VYrWcvHXaG+2;-j0&I8DUt6tNYn6Dedlv_(oPM$gyK~v)_vJW3qsaOup zXd*MeMi=&?+JM~jl!cxJdsocN-xBSQ)@4SwD9InN`-D^}!aaL8wHcOZgvMS3%a}J| zRmTFwMVqns+DpLplXYXbS(N^)$;GxHw*E?&XkvBT{oP4kfXG+cR9Q`bEyj7>(yOGD zp4l%)*a91AZ+_VGbM~ra7{&NdqtON|8O~rN^)T(hCVmGH2v>Zq5FwKl7_N4eZ79#7qWD&WJQj4`}Tqwa%mWK)zYp_gIV>1a%jzzGM$J=L=eqA38V+45gW>C3+$;t zS^$#+IVvoGyKSP(H+ox+Qfif=pnqgA8|BBA*h2DQJ8sZF#a^DH&S?W)Oq2=8*A z6||F=#W;ID#MEA9SQ4W{syb_d2*6%@p{M{MlmsCxgmFF6@8ulL`{{BiVvgk&_ zS-+p3ds%{6XW#Pe$u|Umv08@}H-dN2$Zr>d7)||QohF2fcXr476|)UKka?(!FAzVABeH#~=*<(0I)-Vg1%r6WhUi8(UljPV4&_*y zRo39e--T%Y;64r8p4?)*53~4j-y7`D>7+YuCgZ)`*A8~();7WDPVOH8+^vP{*5&dL z72O?_I&K=NAy*ADMxn7>8rlBZxV_Zq_8#fvk|IFNvq=fU!}SC!Eu4qiTmlM3z$yNc z8?tQS6f*vWHSmgDr!nh{)r?8QiI)GlGwqfJ74#nXOf-@JRI*V@)017xH4yV(bly#Y z#zyuu(v~!ko>2^d8ua5-^LU6*Jhg8OY|+52H_i>l-nxH`kvdfoBfkKmwfydN4ugNz zgTVen6<$n3f$DJxU}LnHv80@;^T4)dPhmxll=M##PCgTarBULwM$da$z8Q4zI-heG zDVps;{7CGEX-umyY?{6&Da9*CHPTwi3{mb!GB@T{4WYYcK1E;27$$;U+j>gPC!b%h zv=t;F&khghS2t}Oc&*bc+wH7*l|aD~^y|Ho9$%43_R_ruAOZ&b<6c3@!gQ9{n2wTe7Jq2 zgKv`^GA}f)jm1JN+8MQwC0XS7hGETrowhqT{}pt=B^q2`MXXy$p2QM=klYw$I1!9? zqdk-x=`e*muE*er)Hx`-WLXLJzRNDechC#Xo0AVGG(ThN)_liFB%OUQP2@fGfY^l; zUCB@T;!o~vSJ$^um`)Z6 z-7gq*xVA5bNi(RaVI}rLEhV#VLS-`nJ0xEqBQYc~+-Y+&fZc(@{X?P<9WZqjr(23h8lmqb?TX|8sr3UVnK?H#2|QP#exOh@W@F<%`HL zdE!gn*>i}Xu0ZmL?0E6WB$r;bWCxqlpkQ`$XYIW;QzrL?3#oT~8@HF<(|piCb@tVC z-JA|FL1T2^SotJ-F&K_O&d<%`53C3>$9|)H1Z2@!!U9&1=g9N<$FC5-Qzud;mU~X1 zyv9$pI;22K65^OXHuW9YqsZEg+)~G)cL=Bwtnmqs*XJcplnIY6?1JRj0bD9^?R#%&dID>sFRH7s<38@l_GuR@eh# zQ)+($J?N)fR!=+U_gg6&>i+K~@!$_}_47N(iL=Ee%b52QBd;0Upxbta)u31a-7+U6 zsxHGF%^=i7T2p_$g-zNfflfD4r1ci3E0pmLyK+6=nyUF}$?6wL!vl9MC3T;IIfb1V z;W3=MpG@lIaIrboru8FSwta4N!yTSI#&3jUy)%crWxdAz>63C352wv32&%sZeB!sh zXl609cJh8hiwl^7zF76cS{K()OaVk(5%Js=sfg19oCyt~od+C1`S}Uld{k{7OEm)h zPi-tQqH8sqV8G;;!76E$UJx8G1|X;M5EO6momRgSs9^f*y=^Pqy`GA!ie_vONGzoF z8grU_x6%GnWy1-$$&(_^*>+}WegKagFNG+luDbvI!(JIlTJf6d(w5@;3_43uLj?00 zJN_6GIsZd~+~rmgwXyisiXHw7U|<``FopN)GI+P|RK}=5j7H+N1beZNXB@@a{11|L z6Zk#&$6G)AUXzI3Kttb}$VV8#lnZe9p<8sWLb#O(J2?B|bFzv@mK>C{7*#jqxkA0x zWF;+HwnGy@--H+0uvK!^r+JE|$@WwM`swWJJ5oMTJ~gQY9UiA%Ol8PY{eI?E}l% z*WGtgC;t+?wIr|Kes;sQ{pOm|&E48#++Lt^BsT_0*P%z9sts;pKCS=&Kp?`7>mk0i ztI^_^yn${=MQMLj5?a^hNEG2&+dXhW`#pDIz^s#*;X@#$*J6HsIS_$6JsKni;23+=a073Ln(cj`}#TcEXX|VOemSd z0{5t@|4gWC*i`|mHJ>H+P=4URZb*iFmq=HZY&^K6xgY{$Bb<`nIMn#5oFP|dLVJM> zAM>(j;_HWduv*Gb&!2$yxx9bHEQ^`g2cH+@(PJg}l2;3*02#b>5QPN3xu|7e zm*C;0^UbZ&;_^=vXG>vP>!299KhJ0XPD|bgVcYQBLzjZ|+tztTAZ5C0zPdo(nB4!C z@9flMJEhU1OZ9Lsnw?I!Qdqq@&!i&30KsgKBK$)trwV{-h0d|zCD_p^op$dhdwuq_ z9!Z{!R-@$VX+K!E`IA^#1g?IG0tFYj?;Whbw3r+|@mHNNNYc;9G@4bHj$fX4$6x`d zD`58x-}xWbf&`CLe=#Ia5Lbb@&`G?_n!GsbAy2fbXN?x>;4PZ%l=N$6-q+hYdU1-3 z*#KaG+))NpRa!xyI8e|lYhuYiRi*a%m`Gs?ppgbp5GNQyFGMvinp@9bu}3ol7rxv+ zEF2%8f+Jxo14S>*Y1U?8+jryhetO(&OEPhU;?s#t7Lb@zQdn~NR2o8*ZL`tPZv#I;W zs?R^@Jck{#RTIKE_P``E#vf|z)a+WsGN;&~Q775m6Fuh+nps1`y8=SGn|8V=9;TE` z@z`670g;JA5jE8AM<2qg0^e6}4NK66--@Nrac_2M3)z>8A9`xwo28b8d(=A=TTLm@ z-(299`i5q=!%#HSun_$=m+-s?+ZeW7a;bwnaYNwv;?()UO?iz-{)Ek*qLLqJhZ%xD}IL{al)MNOgi1&dq6-DrOSD<*OjL*0|JWB51 zQqH0eYtV%$fkevJij!eR4miT~JTa&aM+e5d#~1KF#Sh7jq(mSuSi$ih-ubtxw0K{L z(3yVxCQB7VFc9UwZQhY}PiF|BFX0o6E40zRB?kUb#K%Ya7QG%#43$lgvwx}z&wY)| z3_WwA+@6Fn(UpIVB007IRuFL3t_>HYy%&Fu7~;<(ABS#(`cwccaP z62I43@|I_ccPol^H5UBGNblPx`xd-Y=O_Un`+9=jNd1}jH|{SI4j_e^EmK3 z3>(tp0+&?PkyGLKox4ulL0;F?HXGLaOUhCxuKFwiV&9!UZF9Y;G%WJe7?IGo%>R zSccFLqft+4t-J~QD5cHl$67b1v7mmJ^DiTQJH&pEP8$G0 z38ch`lw@!)fv!Tw9m*iKZ0`>gkEs%2o2@QwSC$Tjl1VP){BeWK{tY|xpDD2@v>u*2 zEf9d;jkG*m6O&jhm}bm>Cg6+PpH<$0~Qv-13Q({518C}MAs zLC8U!SU@I?>{d#A)Z%y9AYV{>YK6%c4@r9>VI=#dts9WY%})Vl3%la0XAhnuT+rlQ z-Gi*H>eR_lw@~DB#yZL)YTgCE$MWO3^CSmJXPI;c-h#|0bfA3uhc}0t{x>B%aPJ5!$rQ%>r zH5>^!A@bBVVlb5Lx2ZO+hfr<-i2&Zv1~A2l+)-gXNeKG5Uvgx7?%OQ72wm~F`W_NP z0+)=q{ZQcoYjjfAfF~RvOi^Rt=}$6w_KIq>`t;YcUIg&nP+BEH!p0MoIM_7XBdf#t z-88>5DHEul!JbDncXET{me;=Xh^+3}#$^YAtBWADS9hV_}AgcZPeo#Cam&0CSM*)EhY)WsG>bTUinX z*}seLiR$Dn_RKWV5$+;fw2nmKb%;>lQocYMiSBxT^M(0M<1IizqFk$S7laxk#y68l z6dw?2c|rc99=wg+ZJg24p?bIBZljgnp%q1qG~x$}bR~-(_{H5dKkP9;!`ElZfuQEk z@%edXldQx#g1HLpq6)u#r*KJ8Aq2CzGiZe?s+t~Bwk!IIuQM^?uBx!aW-;xns;IHF zijwnm@t3JN=>ChiVm})4ufoaz!mUaSlek^FFq;>QFeIVwU!XGOAvPMqurHGP-|5@m zvFqhmvtUzaH-x8z5A#Q82y6+@T*26P>c2rPT;shmI}lH^nQ*Ex;ldA{EHzP+;@hi= z=4(ohiacxHq1X;uFVT%%MS*xLQ-XT?Pp6U3Gk!_$0kC!k`6;jL_Yi{zIc*EVml92H z@rVb!WkWfY(%E9+Mw9)(N`K83;tC0b7k(CsyH-}CMK))T4{4LpfUP}udg5Cw zO4fQCb+k>|osI9Vwyf zvC1J)G1R)ixQ*XqQ$F$;VZ$2iW=)M+8nrm>hTo0W7xG`dY7W$PF}8OK=NE(RUp0+& z7-;vIrO8`A7hqG~+J2W0f1UY&tPUz`o8aE$JXQ3Cql_js_a1NWC^Jzwnl>=W=NnTQ zON<`+>S3{r=n5@Y4+|znb!J6G{8XMl8MD4*MQ(Y6dWwv-|Ok+7tsfJg!Alo}>pR7M9K9Jtb~=Ud~(IUvlv>53!EnTZm=0)BBaT^4Fz0l-RO4i1E__ z1%NndBr?BM0k~&nud_kv&D58P#pj)97u6}Q9xIc|CcL#oljV;`L_v{A~ z^6?~Ju{(H{;*@}j)@TP)UqK9~zipLID!V{BcJ}zTA zmi;7{o9pN2yctf#8%&Yr0lyZYnI>oU=S9->L;K%3(SIPEY@8^fCB66?<+Ua#1 zwiX3OoQ2W(#6PQiH)Wghq76CsAE+@^nl?y<7t-*OhTCUMgMg;Yje!sQTz_-9wm;VY zO^3VixN?0DQ^xt{0NXC^TLEg-RY6IcL%mq3&t=N9&HPf~8@#6=EGf8JmShP@#-FZYGRt}>f-%5Va?63AYlAVMd=$&GdWq!vno3}vY#k9;vr zee)-_k>7;+6>NDUvTWcMykuYYn{oW-%1eXQy)_N?6u=7Rs>?Q&rP{(I!QVFV(4ndg zuA}T#v(6rzePE~#c4f^(nI{ zh-%H@Wy@XD@N~OZgsT$cSh8p~i`3FdD2|Qe@f?dT(zXa;PqO7)@0{Iw8x}5v08C2t zqS*m2jkFWPgwCONyd%}YM^^Wb=REqk@8bx;MhaVYMsB8`(#vs92a>U2zs2S@wr>i^ zOiE>KZMZKkoQW8W2qE4~>lKSOKZ~NB-}sD0Mma9rxxe&a=#X0Bp(0DATSD*@PXzo3tPV=PhM zvUc@8m9-1ZcaAsME2--P3DjA8coV)m#iIQ24&<^v&%E;ma^w8Sh*sM~L>465G=M#v2NlFeH`-zxm$w zu`qb`34BB(mWh4s!d33qVH1!YPE~ipz*HM4_v&e!kO>W|7%fH+7_mU_ai2cxL+T#p z41>+igDU@(KjX>cYNJ{S{QTIfQ|>Nh(1OEI(?c6Kpwi}a&8atr6+zyG$e^t{Kk*+r zNm_mxRwSail49(oKp=*XMRe|Vwx=zn=`s%F?F+nn;B94XU49dHwvsTd#6v%fY=Kp@ zC9f6%ZClEO3{`Oc+6}FuKW72|LNPY2+XDKH?-J?7k{;dp$T+YGXSKhB>cfc<=PZxF z0b;jwn(?EdA77uHP%; zXw)lBP;t}!>;ljPbpA7>L6|USX96tzmkd94-oIvOhkgZ0doB*ja`w+;wkF0A&Vv3n zw}Z=bd(}e4{$KeJ&ju{SFM;7@F_>gsrYJ%iVyI}rhN?||ps__L#~u(<)prwXX5N3~ z^hU5Ob=70~b3=1rVJGO*3k6P+O^cvxsY^#Pz?r~teCiu?frk&}+mt0^tNMIYXu^7? zq9GPPsF!3^e6|}+g_xisS!PRxIWF2gM$C2bMTteSNOGLF0~q-G-_QGiX*K!%o4RVE z#*j~=)enZM8RAz4@gApc2#*Y2H((QNc}qvj+k<&hR}14`l{ckI?CF0-5VD>85d>M3 zz(?VDsmQnv%h|1_h26^J%%<)tA4aUKvnr2!BYKF<9OF@ORJ3SYi58yQ`3x4q%ll+Q z=8rioUSxbjZj^&-e!{{_+6tEH8lS~2ABrANSE_0gL}Mv3de z3}y61EouTyh=o6|7n9NC1So_`_6g##0}utut08MIXl2RbQ5P8fQfj+8e#dpC?a`rP zF}m#YOp(?GJ|Ds9U;Wu68r&jY4abfS8JuH)FQ?_41&TOff~JROW|YN@M%VPa;gpZt zX0nEUY&amBoNbLNV2j#X5yv1apP8gA>!0!lfSc&327Nci0AR?&nUQwDG^NLia&!j6 zPUz}0KS$crAFSU|q;O=D1!0w zk)vv`7-<~(=m*wb+DL2Fw03FKF6mJBjPJ2yQo?sSmuhVjX+l=BeB#kR?*X0)IktEW zRRa0L*{*{}Ny4oOGmV#KkR_14*8*meFHV5|O6+o(ogkl5G^KjHTSaT-w&DLXui-?8PEDfN|_MiKEyvnQ9I+=svMYn-0AG`xt2{jbfgE!>tB&)01cmQpnQXQ#5tR=!U|EOTtXR!*X|2?u-_QJX2!Xi)Ln&{kdcxLd9+I8nSMHJ}_Ri@p8 z4`=7j&&ADqRwg%gG)>V1#BP9N5M&AC*!DPtE8V88CbRX6d-jf|xMQLx$}++SBH1{q zW*p!XoT5EE)5u`CVyjwPZHkY*xv?g>I=cq52B%OlCeAU|4}~<|q05*J!x|&lr~^a3 z5I&D#Q)~&gOpn9tW2KY&IpVfLq*+>ANL}%#_|_-mxC|2CT>L6du}45ERx;Y1y+o(K zD%j+N5oCj_?=! z^)3fbl-+K>p0MY6{}0+!=?$m!=v!y94}{9Id=nl>()*z&E&OcxORTe%!yk0zZ|?5_ zJnjgriQyJ{Q;=5cok+#jvBZ7$%rFIQZ0Yij9RDlLl@E@0j$efOQ!0>vT^ZXm6caVR zpIiy#JCm+?))Pa}Yvfk-!8oNbFi!j9hS1U@g)Nit6D*F6CHstA#(W;@Xw{)(E)pMv zQ7*Ryz3K}Bpm_Y!w|d2(DzxXllyI8@fv6_5q-)=ZS2f-`FO{V|>0Ilm9VTm~hB~3v zt+$-mvbYi+xV@A9@~88<&0(xDKKpNTgh^LoM?qI~IW*}zMm{895gqn;Rk~KU-^F=~_KV34i8g$~XWvlV(igx!D`hDv2|)|To4C>jFnCr-2Tn*9Bo4O=w$Zox z`YgS1+)flR61}j7Eb4;lWW}ek6Bhi)ap@=`T##N*fNQ7y(LM)cQ(fF(p93fCx5+}2 zj(a)@W)HkV7fVmPyGU8d7=T|iVnz2}c6+gjFW=D6y;x|DsvyU=o10|T$@C-`U)uy) zJs}O2VU&^_CY~J~yddr0EC6if%iD^bTz&e0h@br$2x3!vZHQkH_o2XGt_N`^TO$-h z9@Q?dGZ(ja(>y=I@JsVoFf(9(2MNuLZ71J2gaE?_tA*Um1>Q>)Q>QFIh89R|6tr*h zK#QU#^e@vLA}5kNZm8yj2a~Ff=la28Mi{QGu6#j87HdB61JRA?{O@xdNxMm&BmU{M zeC($if{>MWg=vDWB{4k6g7y+RS$S(PvvmF42f5fw$d0v_iG0^2F zwR;*96ESo?1>uY}{M65#NYoN4{ZCd^Z+X}Wz~6u&8wkS99LWsM+& z)xW{Mtq0O%vh>{^x(3tbtxp6ZR6%|V+YLF!5V?h1MzvP&7NVG<9Y5)-9OE+s3u0P@ zI#H0rQHg)Cfhu3>uCH_ZqWY+Dje9()T{N5 zD$u0CmTipz-7nO^7jKYynYkbGiAMs_WeC!0Q2b{wW%_qWd#6D#Ss=qBe)4uhOqf*s znjF;6cJ!J$K5&Z=tzmoZl1sDyP&7n6uqjW*3OGZ}ED1<@N#IO`V}IJv4kg(X_D{tLKdHBAlhW2Cfg)2V2Id3hN)W8YDjd zK34t>&Rxoq_`L-vjm6fY={LVH3PF`v+9IQpjS}SU!Tz_bAZKar)&v815=lTP*3-tL z>N72DuQW!lPxM60lu?Sm9Wf~jm}8CV@rx1bOz~?acA z^lAood&SCpeTO2FKye6WnoB%43a}o!@Yf^|dfV8SIhvPwMf>ZWBs~u?*9?WbxI^Df5L3f?Yc7SR@ zEvKlAUMCWY*5@s%ylewNgBukz3h~HiW3}~b{7-JzNvl1#kvgM}%`Z;7YGSgT2~-_U zoCRv6z|zDOJF%rZw|NIb}jOWC@5(laB-w6xM0 z?RiOlyVU-K&>xI?h!mzJTW_O%4)l%=pD*&f7h2Sd{W}jRfO|}LP1lRBlDnJPctw=^)HGL_-H{Z>JPz>PjdVF`GYl~SGsMxM}`QM>g*Op zuv|e+1>1g6=ss-{ijzdEP)a}nuS~bb(xJy>s8s@FQpw4YP9@QCx`tKEQJMa|4ByRI z_z$1-JgTGDWN-nbRmMDf)krc8%o0y6Q1nbW2N|A#O=Hpe+534^bJTLa&q;F`+;hn5 zEIj(7==c=o7B6>_xQqkRQsc2Q20IuheBeEf2haY9oT@D$>RN&0(iAODdp{%Gg4%j3 z7H@%Dywf!8hIcF7K$V3Pvy{porRtA!@B{CPdlMJ`@<@V$tY~e1Hy-f->FLL`VuhUu0aD%6p zKd#Wx=T2<^1xlWQ7XG=%!nh=RCnSd}!{}GsD3xOoH{CroLzVJ=Fm3X#`$GrwnYQ>% zDe1as*VV*@+>G6J@@^f+ehVD?n*dAqY_rLU9j=gl!pwS7{1z@NFS;0(<+UK_G_zBO zJ?2&x%MgdDz!)93rca{m5dcsMGEwQr<(-1Smthtg%HuRX0CON7->H2pxX6Pw>4U8) zj~jRhg9aspnOArt!$sip(E+;5gmh%0ct$gqHK+J$c{Sm9iB9?&(k-b#MuubBSSA?c z*G<hi(5E|U2)ErV3Z)c%N#`U>fMi7MpWI>fi+vk4 zMH-TnE?K)!mwM=xQixrSm@f2|(0Lo`8*Yd~8Csac+t2Rzr9($I;%7^kL(l3|JV_4% zMd6C(YwO@Gpm$X741O@F++&TSVR%jjLP zqi2DFinAK5CDuq6%qU=++_{Lpi#wHh2~A~*%51JA_%o0eMD!GDmUHc4|0Dm+ZVHcT z=&r-IemtZO5JoOB6#W8e1ds#e1OCW27d=RY&a_A|WZ6+i7nWYe`D^dw!v&675 z@t&qu(LrYSGEw9{ZN2DUi{5#W+RcWS$}9T!?JiAjIeruU6CY_TA%@$hHUf%`RkwT+ zwYkt&1p+MiWK&hXg!}76S`Kn{R<{+0LUPEg1q*$!E7tCgBBxe*kbSqx^B7;{A)&6i z4>GOWhs83~h7Ij$gqC&5V{{G`yOmm6GkrTQnR`lwNL)VUf2L}Z%@1BF2B81KY{O`_3coC+OxV_g9_#vJPkZD zl^f>RvBf4bDjlxZ((AM{KTtSFh);35q%Z508i#+0 z!((H=ficyh9&v*@&MZA?8?`B+|8b#s0=BY!wjFyhM6RmNLfsE! z5NJAYGB~70n2l;i*i-9;mp}fJCi6W`yMB=5C6v6Lk4y+r!hO)I8V)1p%?L|6DsjpE zgn7r2t9Oitqb236MC1UlHe_st_@wwyZyz=WwHiM@%-yEaDOpR+Q4&YUq5;Bt3@5yf zF$`XTn()q@K!>k1XBzW$W3cY@?H8&shNr)k$DVA!YHrb@XxmO$(cFK+#Hnl78GlTV z$-py^aR=+>|Gw@3MZ)$vCoBgs&$Sv(CAMs90b|svtru+3RP5jdQ?9k&3mQK&qDC$a zOt9s-v!{K3^HCtW-}2~O)F*?P7G(Y)JE03oEjswkMae<1+o{cO(xjPrDazy^3acI_ zX9qA;e0jF8cwT(>_r#z{e|HILkC+>gu?5_$q>-}&(Dn=hLk65weOZlM!GiB`n4Z@)1E7W5S~16hM9bzTMs856LSKL zK4<)~`RO%JRjjRhNvN_FW<&J_aA^*u6hKKUrnT-axJ!KnXgRsKOj}=Ewp|>pV2r9y zuLj-RsH`UQVCSVgD$1ICLo~56D}zmyztUw% z7gRIrTZ3dn{H+We!{5p7pwOTKwo@W-W|@3Rp*uy++C!;w7LJMfTjg2{U);KR$O3(4DfxfbsE-<7AC*pmAAx%NihO!~ErV9XR%U&<;F^RpBjSltVjGU|Q@emmeKz z-?9B5cj*cG1XeJ8TzKB;va}yZPwdOV^PmSC!Ez|+PqiLgek&;jhS2iUd9-MeNfu7t zEs>butv!+ucOUQBopDgdmKu~nLNXwY#E@g^3318_%h*o;?$mLq zuIy9?+H{iE2Cp z*XT8q{6$MULO`Ko^J(AJ`|({*E+J3+<=fQ}WGnd=5wx6zp~fG`8Um<^e$}eCC0CFe zVczq~nd&IxnO7_?9%#fROm(Va@^VMM5U8Jo^Ui^I@;J47D@ zX1fF(UxQv#@^3LK+-EvaW54y*G)Iq(;Y8pUVqzt}#$v5sxZN(xbJ2j{#u7FGhc4U^ zOqm*i-zuD56`S@U`?Y$xZt3(M5=tLp7T_MJq=P$6q@q0RT0R zDlK!K>*V*@8lV%4bY*kT`naN|)zH;gt= zwJPbATf?c)nx5ok1lWBu4~Ni58jSzK7f%=Vk?iJcS85r7)(O}*X%)zvpg*-X))>ep;I4;d=D3v)VUk^Pm-PV;X%qxCYO!QYx?=X4FzWv4bIJu5m6xV6(bGF=Gjd`rXpLvush0i6 zfi`I!-pwlGR7MCR+a^Aae|kc|Y?wclj`xbuJwAZ7qschAX~xE7zL*7BKGQ_s zDk?qwTDkRST>YpViN3AqNXy*u^fb0B79O(@|J`;V1LHGD9vM~`09?cL-_kPlEsF4CLrz2M$7ph zof27C}5u37=-&$+L zyivjm`ReY7NnKg;R~Rc>01Z!(bbC6lE0nqdZLw_yj$)*D`OF4|#CD4bHjn&ot2tZf zHrFJmGt}a&wbJ;`l{Y=vJ}x^`FE@fVhAO^_6rziVVmQUGNJm2Gx``{vlfKqR3Ro#Psq1706V>n|aFvskh_*5x%0lN)iW!FWofitN;lA81J|N?mi<|~G zr@C}_*EY}$eXx0PhVj@9bat+yQYG1i01wpS%v>|78HdqQckTj+h&}e&f62+o+~syf@=OYPL^T}7nSMxjUOC1( z1aYM|pyO5>6f--k!(x7$tSzr9lG2=;w6W+f@0EdjaIVB|r4(_m2Wwkhi7Qbuj_`RN z<%;UqX%NAD>)0$n5G3bNW(|H5VwO1Ec=z)A=DYwonM{a3`V%q@_H=Bt^w2B5^axc* zAqlQBecU&uIE0D#!;s|O-qZLcT2-$wpa?01_ri%ayK4QUQ3oBbjo?-M#~N$#r!bM2 z6r3Ov!F(qam|7N+gsnNc?W1;|rS?STkcd>DCo9_;?Han&OaF&6Vf&?n9N za2~KUzAhG{K5Ut6L}FbxPe7a?cn!-Kfw0fDLyg`?#xueBj~5;Z@t>oZ0gwq+KP`|% zw%$k}_Jk)Fv=_tLNcmwCu2do-1l*6f83(9yjifH!nF$scCJ+5$^F;bQgTN<{Er-$7 zfuX4bOWLPz$q&BwC67*@WHmtNq)%xRO+!cghg(>J^S^-JF}RUoklAS}IZqGP-vwC* zkqR9=&9%fEJ2;8tjL+C_oy}S6R`qh`>@vh#OD}I%Pzz{N@2~3uY75rMx1z?vwur%R zwk}V3C1GZg}@E`Km#a)NR0kErDn;7hw^q)X65Zrv)@(8SyIe>Mw{tkpm z1}9~(kh7sUuoqZrD*Fu1j}!~|kC}ior{rG$T;Nf&oBWL^&^iU-0-~SD(SK$t4Ao+{EHBs*ga1?rxHIOz7DQe6`G2RM2janNjFhm zmX?1y4=9#U@lfT%-tS7)MZgP2BVP__K7N7DdyR=KF?Io1ME=^-y-l)>RwOE1qZ-T3 zhL_35`3w>;8zUXqz_RXYGB`P+NM*#fvoB9So@<7SiJTb4znuP%${NQEEJ;y4KDe!v zK+KMOjf4CUUWJSt$((;rsT*HGx|;~mPD(kO6>!7Au)hk-$XtgJ&z{`o<4J*=QT zt--_;0aZBRFDhZ zsm5vwUfVF@zf^|NiLx{RW2)hAzd%7oEMrH1{t{tQOo@)mb|CfsV=v4p=l(+|3PfYtZYoVYWu?=KF z7Cyqn=rEkGi|bN(ffXuPU<*?-hZ)18;!`UMrRjxUTn7s`hW=7YX~jvRzs{vhyW@s9 z1iqvY;yrv|f9tWIyfN&s>{~Zaa!{1I~Q7qG< zp{D;e0{B(GT2$oZDQd8J3ER=JtpSS5HC<;(cIn4>C=g>lfnC1lM2^X^S4BJqRJ1Ij z0*x_4fMeU!Cmj1;v+YRsWrUf=1FwRQ+he#$pVd;g{s#GtIscejW%wk@D>kR~|CO!K zESF*&PU`O#$u+x62hq-_MiFp)lg=&jLTuCjeo&DefMm4_&agO)!-b&g4`*!WIjVjk zkaQNLhZLY4Rf1wqoVs|DC{&0vz>-|}nljk7CnDrb@YiR{Rz(8`6tX89a| z>}(XFxdZcjI;wO7y7f@A=8$=?<=g{LbL$~3h<9W9X@@wZ{|C`4KAz~^sPlXLn8RW1*T01hZz^S&}N zsiS{eQO3_YQ(r34bIS9??Sf2h$zw43&?N$QB^;?SiSlF>n}8n6$X|q^IYeM|nmuFR z3Q#+ev!pMFMX6KHFe~~@+dLRQW3zTk! z$71KXW>?1>*$}~=i++<@W<=s|F_6>&^2ksNU6!m(o)~I(Y@0C-8$Gy` zYuhot({cb>?-Es${Nl~jp^vS010z1#UP_*NTIYm;s(*ETjwaN#sa2RW7fU|=JSZLk zn{i+{1_@1#@g)Xe6Re2dofBEk=zL84kd~0f=E;+8lx4jL85#jJ*S`enKBl;8EQmC2 z9{;K9`A?>5@~|K?cN21(D9($YPYATNUh$+IlGLM>)z-Du`wJ&;u{3$$NEDFeZa`6N z8odR+v1qRIJYZWzdc!ubOp^m0@-{hp>vmCea|uw(S}(zrQ7z|zs^fYi>k$Z(Bso1W zktU2D_`WNHP{jZ7l>IbBCe95m9pr?Cn0Ps@SK>&;X%tT& zh^*mepz`8^P9e+9EDO~PL#ju%FQXJEY(X9ZLn0-7nDCHkz9TT+L+~j zgNl(?UqAlGG<~B!aFj9k2{BOquCv2y6$XdBER_ zk;eMEj}p6-jLs3-G*kQ$+3^6Y&s$PL&`r@?6j;nTU7(k^(x-Q8HBwVv@B7qTd%;)3w6lL%~niY zhX&B;>L z00t^LyQd+u{KfB_f~3>Z{P>a*>Dzx&mZWQo!EGTUX0+^P7dB3a1T}Y*B{gPyiXxU$ zQ1c&zi-VWx=dOmRca~CYZ(Tv9fOnlD4e%!eyO<7Stlkep%ZNHUjHp;Dk&TGsS>OO{ zhqAW6=3(0-+SNtCPVtBL?=mbVxE1OeR>w$t3d84`yqGy-t%#D94oKgqiSQMHG~xD{r|1k#&M` z*_Tvr$-4iW!;oY>i|gNTb>hTChOvF4mD}cO+m+(FrzG;<1k)O=2_ao_wrxIW@2&cP ztgQ2)Hg159o-9+|jGUskA*60kqI{WzkyhIuIpCHswNdG#pMh1pfm4mURfkj_O5yN@ z2qKR{(j=l=R&+E3#!2B-JSl7t@nraP%CO=94PtcEM=eUm8jIuqE=>xk{_8i+_Cvt; z=Ij=xc8{r|UP%BUI>eeO&zC$y7F77VqX_CN;Hs+BvbT~Wmn$!6mKN%Wh=$>NCW%6C z!=iZ}*JadFtNVr`NJ|`aACCqt5V#wvv_&duXcHp17sKf!i@v{)WpC5w%_)kR=B1!X zN0pYlajgAMQUM?Hq$kfh^pU=P9HA7pN8m33&|&^20Ml4^Igdx~HJ4?&0}RKI6O0%H zO05~^)GwS=p!O02Jyg6o>tkUQUTc_Lhf@Ve6xoQ3v0C}>>GNeM(qe9bqE{1mVuX`W z@zlI4234&=^7U1L8PazU$da^A>z}q|o{o6Jc%|Qa7HGmq^&%!pFe-8X5Bo#NMt5zw zG{Wy?ye8MG`|GaWiSzbL}}% zv0Nt}e?gkPdF{i0D*fHCrwz=1pT!)btkr@2(du7GcWD9Nm1p6}ArM}s`$5v}nH_|f z;C3H%KTqqC90kMTh?H9~!!pU~q!{LBX&$Qb8Vk+71pQg|E^>YaG3VJ0qv*Sk&}RWM z1ym#ZD8*@9`coW(vp{gNJPfCvq!_wJypm+R<@I0D{U@uGXGX9n9>GB?WFL#0h+7(I z>P9FCw6DSm3aBb9~Gd^wpk)7yV7QDM%{XCn%;)g zMvivPqr^0`~xATVLcWpiYuZ^Crc4yaWEbsC?3pOyYA{3!e#@W^_~-j zmLu69m(@)UId(U}!xzadR?z^LCwIsIu65IyG8`i+J0vX3E>{gU!HKo@DH*MgTFfyjWbUy_WH*dckE{G{MHS!LWhoA8!Xd?QW0gTtd6XLBRc zQjfv?&N0ZG>cGYNw;Ne|VXPl<0g$9)Ng9|w@PTlk478#=KZ$%jIC%R1T8a}TcH!f7 z4@uF}V$smJG8L?}dtvV(beQ&XW>`G*&r(I(?#s%%s$r2E)^HUBbb=vM6ePOI zZ_FLdF0g&lDk8y&DZ3Q+rir3c@gK^+=R$tX=vlcf7DGY_lbPo(DuG@~hOP$U@)a9~ zgz$r)M=2uq>Bhdcu6jz-IU-uU-^|S$Z1dYM-4Ho27-^K3bx^V-S_r? ztv_LV9z5W_7`DNz``%QgS>Hjti%i3)JE$`0e*LWKyW3k^LmZ+gxdkLSdO2y2b)wAB zW3r~HIcb1b!5a0vkcxEo`q_nGs7xl}mE|IQH>(|WNc11oPsrrE5m$Q0?v*51Z z^qg$XTId==^r41rLR4F$bVW(Ou#yllel6uRI!2RhdY(d#P6=1xYO;fjVP}{+zxDLu zM&+XbQ3D@7d%Drn3dFbm^=Jy*z(4rnH7n$GizE>mpmyYgO1FnsL4V%Ha*VHNLMw$| zGTA!t`z>CGs~n0etSKMfytzr7X)!JZ<*vD4dfi!wCE#%eSjNJo3T|d=^v$O6C&&i_~&ePQK@5!e&XfYP6YmMXRe?<06pb1~{v3rLJ?wFm-6bavOy-*`I6U z+UOp$NF5_CCH3xhfoW0_h!Zy^mJ}fsWn!m|a)9^+uu)z3WE9N%1zB&Q_tHwt4HYUZ zn}aWW_xlJXwT-sl-h&Nxo^QD4v@SV{Z38qlt((T%{5K|6Fk6D7z)&bODYiNX1j_k^ zq+7X#UkoJ1V#0wg0WpCsJBF#0U(ip^iEgTZ@SuD1B?K=YvrQus(jg`qgVhN5*6Ya; zHMV{{-Y2;72~wdo_m>P^T1Jg@Ip6@+aDOb)h>67 zzI6q2c?OqXRT~e2Gh|Yih#3clU93M5G z>BY%kHINvP8Iu8hcX|>nHqged=G9r1c@W8^h*iAWepFS=st!}STOA1u2%?}s#?{dD z)DKUDRKEcchZbS_2dxa8JLFqd$A7Y5FszeoP0h2dQdlMvEQ;`-@M`C*S)t80onA9l{;BV z(LaCSm-C2;>_-5y+bG^AyYg_6Rwb)o%#4LoF4O+Y8~xB6wt|fEQQ;*}%YIfQ7KrGR z*9L6w!e88oV|Izt?rg8yjCc4hl2%vm{f@Dtiou?Izt z8D@?vQRXUU)Q{VqCyNolf|UAx;2vkXgz7gk2weDbv%fx)_yX4@<*a zvZE+4(k|2{E`|1QWcGgxHvdNES1^OanVpWPXGO6HMIp#dE$)Ng`SojUnmS|n;bqrM z?uFdq0kuLdUBh6y$|d368xJP~GHJolyKcRAx1BfCDd%yNs z@P&)7T?N5I9>&LUED3DKu=u=pY*K3Yq5C*z!Qxc?66c-UnRezZHtI)rDRfwycGYv} zRh|mx5cT7`+Hvv#V$Y1>KrMf(of+_U7$bHB+h4k?e>ApWpK)kXWm9~K*pI=G zX=)Od4>I>Ara~S1)a#O?F_E-If232&MCcC3<^Y@i+}M}uXv)X-YPq(RUe{n|1@JYOgX)f+7%+?$fL#%-+oy>$rm{NsNsHYO)J7WT zG~-&b8RjvJP#O`-;1SidlH~9(=lG?LXcksn(+04*V|l}95rQs9E;pS$o&_$%woMfH8JpgZHyD) z(Tc%O9a^~-b*5BQ<|^K=bA2*5^*J7 zPjp(7rT7b5```hARGIFs9yw?4v01;AMpEhH&e5z7=y4)8Xo6V_jtf!Yuf6~9Mm=}^ z@9ES$IVPYPqrL$puEhCUA7KxG%Q@w<>DIeL!Y6JU%+b8IlGAM1UQETh9K*~zW(bkBn@5RX( z7YPy|ivY02Z9B#jnVuc~-0+-u3smZwlEUT_;ooxmbZQl_OFx_ty~!la@om=abs=9s z8n^FH&p=nk0x}^dSHqxvh;ea{8bWpt6mbZ?Cs-*@e%lZLv00y0mZb-l)mnEGQ`Gdz z1%%}*qpi*5jh^PwZqq*HlJdCDkThg{RFm$v`(n=obA#jLCMZ%FJNn~Y6NP~5jQjCH z0Uvap%qvE$(hOGMx!8_~-c*yvc%5tw)Er*!omTz@Rc4LoEP-{psEt||@Q3SwHCHEF ztGJG)-rwlX@eVp9j`=ET%XBeFJ@c>adwA{zH@wG@I01JyMzSO>Nmcu(M6pjC zj2E)1NT|NIW>@Mh&~(sEFMm_~4$<~7s7MUjOyY?ZFQ|Exn6 ze6mL}eW2{;_DdDU7c{Lv>04sZ=t5Z%0P*dL$ftM}$#2HQTteB5l7ZX^UrVc%C&q^u zhX2spSk!LlJ2#S0flZ1L7*_e59mMYA;_At0=+r)5)Qdw!f0P79)jW|2Y04~At`dDJ zKK5;Cg`LoQFcX(p!LGRpsDc^+P(K?{Vn{yKS5~FRDEx>tC0SOT(Oj^GXy+i(?`xIB zA!M!^(8g?VD2poKehrM~%crOmt}MNRWE0k@k;gqfOVKnF_Nd%9myK9WFjst*bSbP~ zNLg25r!5=jQYL{J58EQW@d8Y2D5=fLA2#mF<1nAbt0NP^%kvhY6n8ldH5_(obVy-6 z!Q}&ZvdNO_U%9Y;J~aZ$Ii=}XHmmbi4$%8&PR~&h zIBF=B4BiCCynOOic+V3riM{I$1{{Bl8wu!M7Da4p)>8WhfrcbOTo)U0Srdq|Emxqf z44an?N{Y8;T&m|0i#Q)poHK*rj)|?>wDoycYWF-q^N9ab`ng7_W0}b}3rL2t~FD}7SZxvTX$FjO7 zQ2fnrC9)dH2Tt`7Qo|H%DC0Y%8UBk7_AoYMz)PVpyy!m>da57oI*5j`0J9uPCr?Fc zqqLYqh>jj`aGoE`s&}eh{4!{zL48((>31RSJ>Y@x#2}Lh*%WWvU)QDIDfrBmf;WNA zpoUT^56f~!I-o+JdbP`e>m!VnXAzlWH!8d#9C6WYqPiU=p3tF=M)*PL!-YS@>z@Zq zBg!Z&x33#14A^q2Fz15VG_f!%x?cQmlq^(V~uNk zg&6zg+0%``;4XI~iaE8;u+dY?$sf6)2&EGSw0QgTU%(%qL)qfk^X+lgdPFVxWFVAm+|W%X(Sj3I$TJBRHUpJ9G`ijGaSt zCcw5vV;ddYwr$(CZQHi3j*~yO-LdVYW81giT6gdUZ}4U{sXBui)LQ3!wf84|r$g+* z_%aq=4!X-+Y#O%e_V%5qiXU|Q@cF$hPAqPYR{GZJD#{_wX3d~5vIVk0fU@&5>~rF6 z7W#3B6G6T4pj)eSjGr)i_rb_yQQ488U)DJm!*ljEs|Hd6Ah3V;UGS1bhZE?lHY&CI0K&22kS$Fd3-o26f-C;wyy(+U7p|JUkxySX{a6d59j_kl?n zol`^2>NsA2O}i`b$-cv76E?p?uz!w%@FbQ8O8cD4_FIWlp;odE<93|8l-G(m za4b_V_KK5sE(W-kC*-3FbGlj@rRlxfi|9`~NVWwlYlE6MS@_U>IXF}BID_YR8EGL9 zGaH3HhoGmpyVGGULAR9b)!4M zuYaI3=l+hxdqbiHGyFJKngxMH^qJ(U^#{mpO@*5KR_2SIz<*<46g1N*=^){PX1*vt zLsdeB=vAF{82YTbp&>GHNO{{{fuwE;%%hml8K-(ysURonY4WuWwEMZaVpgV~FD|zl zQ$#h3g>79l7Y8{LBlkR_a~3uddEITYy^i$(a`G=G#Ip%1nQrp9PsMD>N1W`T4)}3% zeWvW*fLzyFj9^`KW32l8#Kj<(#(>o;5JEERq1#yPxuWfq+w)Q8x1T&DPzk!&0ijH1 zA*2jaqdfbB7jS$>hUd$&X-vUx_z~hS>4rwyKO0-;tTztGR3sq|Z0(3?8tyoPWhC0; zG)>nL7pF0GgBzQ^nsoOue6xlq7svnP5D3x(#_L7LlzdyBngD= zR`t(=5X?f|xPxHI^Q&H_i1diiy~M%*YBAhjrjVIbib6|!!k5^Hm838hSK>VgNS5*$&q;=?j zL2;U#_y$nOw=H6&x7uv^bN*%AK_O7qs8YQcnuLiW|Z z(M+(mgVo|UW0<#o*?1;`5*B>li<~s5K)Ly=Dko2_L&`bSP#PjS_|~C(F2!XfbtI|Bf6hON?tX=jtnV?NeP*tc8F^W`n1;6J zfooBX{Y8rWPO%8=bYa21h)LK~j$6^HSMb_<_W+sVXArmCj#odw^|3Bri$M2ThL$cZ znGPVfCWo*m0X(j56Yka zd)So`e_)lfU_kQbc5t`UdFS3|OgJbQKXFO&Xm;$u0FE}gS?C3zeJGJ;Sm;~VOR zUTS(<)k)s$*eg-;lz_%bwH^KF%G&(0Y_NmQB7y=7sYd9P{5gah;5+*Y%>tbU7uxx{ znotPPVPbu*U7`8bgK0ti(OC znt=zI7u3eLQe5RCb=09SNrZ zlHIP2F79DZq~)`&oN&!`8u&%On1Q@f>$ri!kXf=;V!jMs`>`DcWZsMX1-2)eS+ALS z=Y}TS2cU3>sgCwg*x0@2W32`!b-2PxQ=g9pPlK;;zuQq5teQ%w70!tJyG#msr8WAe zQ9}wD3O6C|R%*II+`MRkMobcIy6Rrg&S@G<+G)&`xejW0#Jqj_kHgJ1Z?c+O^0PCY z>J8m9@t`$D_G**GdBh|k&dnoh2ulId8{a10Z6w0U{TJ`rxRuN+khaza0rN(WMAy*H z9w)1WdCt6FQxXo7?mqi`7_%^*T{Aa%);s_K4 zT!qE3yf6cVso$FvYx>MBt4FLf78%n;8n0@OxPs0V1hN+VWYXDy9}cX5P&STPIIsnK zr~ooWg8BH(P}r*uvMw9Pv%ugSbGSOQtz3tw$oJnDe&>3(WLl(GyDt zPD8f&Tu5-l|5$eCW0EVy4ANhRtYQ@~5cG5&9Pn`~)Pb2S6#=toPekuCRosouw|3US z^+`Qi7)56u?M6uiIAEt+7tbG6p^Z7VR2;H4!83NEF-`U>f!- z1O?_P!a<#fw9c!Bjp_GQC?|=$HgUl3*gLUI_*8kA>=lh0smoUg3A98XL$rt$_>7@- zhNO@9JggJ8dqsPmQn+nPT=XY^xgBi_@} z?QJeN=IdqGJ=5_!Sv?s7&2-KBvUYl!`nMOI4o2mpLxcmCo_w!Oe(PCKdFogVXV)XG zzZ=ExzCW8-Z|y<1kZe)&t{FY_WRWftFHzh6#v=$4W(J`|sg3Z5P&9vMSg7EhHy_Ig zskee22qV&vmOyrzcU@@+E`1T?4o9^{i&xlV=8i)s^-v6ftohAx8RG9>ix8BU#q#khb@BMndZf|^F_Sgt{fV^%rAdST zTw#2~Cz4?Do?S@XsDko`k31nODQd~X5X{~ZCH75{5idcS_86dlE$suYU_I>=T zvTUEDvr8H-EiIMI=8?U&bwN>=M_<)v!$)OmAzP^;7zny9SW<1Jxjf&9=ocx#v_a*A z(M_0D_=$Jp6~g4}u2_sPd|m5bn_6Zh@TzXkt!lwhiSLazL!ZT;#alg{M~0OoQvfR` zf)#Whev}x+$nG4O^>F_<^*({mtfd4PZW6(P*jA8xfC;E(EbCeBDE09mWf+e!5|rU& z%vxswI(nV_1VI^gqv?5~W^IB^oCFWA^Hrc^Bbg?Q{m)$56le^Ko z$x;N6#Qqy+%-Y?9@&aV-K-;;MviYzn&K4!MojvJqEt?YO$@xo4o;nnq&^X0C^l z)caZ^TpGM{-vgAnxrjnd%TXWvs`I-RV+vM&b@+R5j>5IqAe69X+7t}74{h=DpeQIL zMdUtXIwsbH9>6iNr21;^!4~GR>sO*kG9@nI!Vush`OauZ!|-~wavRLe4E9gEh$`{e zxMI#FUmXVKaxK95GsC9#E7MQMO1!Ka6e$3=Rz{sftHo^^gk~Cf%CR8eIdHIM?Jd!q z3QFPWV#x~u=dTk(va8xjp2T;Sm#7x0n9u1Q2=b@VTsB;%5Nj@a<`B0cybF+Z6W;bT z^dmtqMQWIqCzC43ZNZDRnC02E?Q;Y#<`1s|%k(^$Sm*YTHSJwYMdGb2?5E*Pkd zK^E+V&cQ-N5Q|=S^oJH{s0s~8-RW~Pcf-)#z)421_EGaxG;Y9qkU;0P;(b3j28&sa ztGNVXQ5Si0_&vBC>p*G%AwJSenV{HtCXz@zPu>BlW!!+cw&4f7gjP*i3?%quYL6P7 zU3@3hUj{5Na9WSl_8WPPbn2Hz>jI8sohkZ^$2jw)6(%#KJ<5dLr+=Rut3Hz6EEhaFGvIqLuE-bFtyK3{d0G)NA%yJwvnU z)U$gxnpPA~f9cvbTvcvXjgUgnj24wi)%F5x|s}K1%vMFHr(;VD7z2Epuoy2Mue*^khn$+zYaZgAdDQtHTVz3PAU8E5I<{d{n%} z_f}zCjW$X@B$+hvXgdqs>@^Q_jL~35`2Q@dN%rG-TyMi&;rj?p|5e*6wTOQXO{=b*eZaoR z_eMCrs)ElUu1^rQaqCVFmX-Duk{=h|-qpq6oLy!0g}JJ7Ob>rty6AYe1liu5SBV(b zq`b%pf4fU`@sw=1kIm@=KKRwYZM|8ZkLlLE;PsSR-#LW(6knz{>MDUAC{6N6lC*HbLh%Io;v5^y>9j zP>z-lEc}f;x=Tu#revPy7A`ymYK!lBu9=o33n<*TE;2qy0I^em=|_T~Uucwt7haac zc{<3fzHgjN!O<)W45*Mt#ut*>?{JoxVKAuYmo3v;@ZaAp;RoLx*;cdRR?^+Y&vFHp zMC_C7!rV?y)3cJzIYrHtWt$Mfw%o|(Bd0iks4t&MZKNw~gIa5D`A*C&2{;uH|PwV2dR{Uj{k?$DN#Dy z?#um@CBFm+4zpJvTMQ?PR-V6b37M2g1O!+`g*zkk;O~oiU0`U<>F?u?)-49k>(<#^ zM`n~SMq;EK_5z#mbLeFW%ghqaMlaB|UEi6f(5QDLeT62Po( zEQclFs)c{7t@F3-D)W_(ouD2r^zE;VEy>G62cxDK2SKQkptLnTktE=ywE z|NIoIigrpv2L@<6A1a6z<7>p@zP5>2I8(+*L!A)xS2(iUkHq9}&b6p7=Tjg0#0*19 z7`Ysi!j`Y3&@8UROM|Dlxk9jxc`?V38euTAO(T!RQiC&gMb|GjTHpRcdGx6YJB`?P zoBYdslBm5)J40(BY7}GCLL3Ylq(6p`VcMK| z-XnpGjlT@=7ARb&3RXRWHgT5;dtWEfG; zC9*i7!#A}LQrk?b*E@WU)Dfp7>ncU9IoB#B5S~QjQtj9AoH5l!1bk^B z73@aOP&831WPOCbidZs5Ni}T68YqTvemW7qwyl{{FZWc@nXkx`F{=Eu+GzeIC(NE_ zQa24+-Q{NdhuUQ*rzX%_B5uJ)v7%8a2|hpi+iq+OT0!78kt^uqr%<>am|tC|5cCIF z2905d3jIZ_sk?9b%k5vMjSlfgf2@Of+G93tC)9ditcR-!!Mfr}Gq0V2T1(LsB` zT+E0)M2>g^#tK0&Q*p`fZ0puI4N~u!fTyg{=#%$umg&HsnfF&JO~czXS^UqN?xQP} z1bIorRhM^Gg@`vbEhXo0ZG<@2UpD>i<&3r~)aVbAo(_HDhxHeZA^+f5(TH@_Nnx1? z_`GV?gRR$eZJTH+HOM-3^k66bZAPJ%z+tNnMyzO311+m5n&q*68mHJ4(sY`*-cGUt z(E=CF+TbtN(z%9D4>{jQp-R z9@5H%ri=R3_&ml2yA^4^=Kl5(c}{p^(~?|KKYNfEN2-J%qeS$kZge_?z9^Ety7`#) z-ZsmcJVl1T8uuJj;_xUH$F5YchQ9gMinAq12~KQNGs-#5IWz5PfzbFT9osb?t98~8 zlz1cz7xU;~ts)w)kUKb{LC!w0u)sgnO(I|x?{S`g#-DD5l&mzoZPOh?ex54q0~dGO zPau0jbc%*M5ZHHd`Ciujnjtp9EOw*><1${!`~PXVJw@jk!4{q0J=IE%>^|2|`R0tJ zC6<_mB{kJBRs#qVn|e3{x?tJaz0rS?P1Oz)PDAoMZs0reIJZNN6{Jc+_+vM3BnZ(AaJF*#?c>SXGrrK37~m20kf8?48q$GUhO2%0!-m zLw@-q`k6K0Gd;c^1XLT@H5s{jZQwK#VybZFm2TSPg^yKoqCZ78H>uSMLf5#^s1CW{ z@Vhb*u*~nRdpBNJa4P1lurc8;{8djffsfg5%-`EyU&|&hoIjvu4|hzvks)|%jba@e zk4vF#rQ(Ddr#B3AIee)CZmT!P){+kK4Dn#4ldVK3NxIwDppi3-d44g?q#xPeEluoE z{mDf39E~k-;Bb;#9Ve&%lfQ?~Pdd$$v2JO+Us$jsFit0QV4Xd#me|O-QJ5N<*ne~X zWbbHbG?cu|(L@U$!h@#@uDQ-Otx#egv4s)QIP#kn^lSBmq?(#k)9O1TNN##SVDiSq z1avVW%I}esbIta2bJBu#24_Vzb#lrpwbO&xAfSb=pnq^*!?u`5G>Ia+sI=1{j!wYLD%dd`^`(b9d6;mxqC|)xO}!DPG%mD{nd} zJpWOj)zHN?5-8Es)`H-^tLv+nt4JEj&nbB}rg;Uf@nTNxF9O7UWQKMPnJA&<3x2Q^ z7b=Aowca2dfy>T}rVYp&t8D+!B9(Z$bUz4g9G}2@q0-7CMnw2qlViNxo8)-;l<)AR zRNyVCj6XPn1>3`8trH$wYmQOk8@@rjE9#;*oP|v`j&{cZWW!9X@5Mqvp4p>paxSwL zm7G7lCD)uFZAea}K2k*+4+Z=(5%!`;)nQxkd_JB*a5hMUFDuVa_Mp91ooaJ54j^V0 z?C(F@1DUODxdRF?CT>#zHP%bdqw12{MOl8kgNSn@XK2{2s%slASUuirGeU^IT6Mz; z8Hp`+RA-LKmYWokq?!FT7Ncv zu9JmQb8+B?i*ib+XS%LVHB3)kJ6MaqKy#KN2IQp9MIWRO$oQIH$Oav_Pyc}zF%CU@ z3j1J}+-g&I{zM%ikd%JHp=KcM^1&XDIn~$;Zr#15w#4lEhPbENd{nBYx>KMz9g)+6 z{G(^lGSgVcekQJ#00|q{dyDWp%Ffr)K6OoyeeymO7Q%MSucDR?s92g#dm)R#HaQvB z4o3Ha5u_P6Vg$kq&_0aTur$6>#T^dgxfM^??0Nn0@Xe1a3V+R~YjkLZ`x9M5 zE%WnV`PR`D0ZT=XmGpV>=^%#$w0Soyw2Gr=rLSp5<)kbRkHEs~4uWx@m-N_YuC{g=cumf>i9CNzogH7}pJ_xh)Jm)Tc{m z@_$B5Xi*o(32fmx9>2D{lwV0wt;Bo+J<)qZHbfexj+(%m`%^p1TTQAd{`{_Ckq&nf zErG3C-Q6IGTx_z7M=ZHEGTV%kh74-!o6@#_qW!@?ti>wdmu$rk&J!L!X$xN)4e6dLz#P3lhi05(V#dk z39#ul6R_b;B7L2+HXfB)ysK$DBDs*!DbUHRy;SF;_M+;7Rb$xt_q7EOt%FhBG8saU zJoS>iYB8jtp04On>l28)9x~anddr3_-;+pC5nXkeqaBaJ)+A6%u388E27L6q)btCV z7+zccr0Dy4LZCiT^r*BuXMp2x-YnI*PnY^dV9oHrYMnCC(sWFI3RwcH&LUr0k6EAR zEeUNMYFcQKP|$kr!L<-;`^bH7zoKoJd2~FhB~WB;z*tob5bPfmpv+53UZSp8>*y7( zLlqfG%JWtj87KO5m#%ppf5Pd=PP;*Mno)U7AvtJhF&a)Q*H4l+v0kA)!U2$ln_^t=OgqRWkPbbz;u+GptA6qY51RIqJaAjA<&}TZH6T62utWM1 zP_#&HLz0``_eh$>`?&ZKGK8 zLD?CH$S!Fae7s4N+L?VO#Nw7O3gz52i8;Db69d3}vf1o~ z3Q8N3p;AV_d68cs3{9F>HI-u(KdqiwG&v4CT231LqM+e=@Olvs-T<#9eTM343lb^q zEC<{n%fRCA;`WR7pL-QvQMF(FD?yP$7_$|i1D7_pnO?rRq3>)Sp*;k?ff)OhJ1%>twMHEX#(jWT~vRYypVDcslEC5lK)R z4u1h6Q&V43z7g!G!enRS{ke;yOXdYR{w*32{~G-gOQ1$(3rDj_O0@A1V@!pD%#L#d z{WH_VdQ0QvZPs&y5+Jz-eF(3S(6|4F5~)^(*R&@wHzv=_o9~psrT8)HoY+U~al2w1 z3;EOq{Ce?z zwFXb(s)+~~G^+Rrs7@)k)mzbur*?V~Gs}t@Oyk_?R%&FFb;<=W=qG&_`Agy{smYCj z(n57L1Hppn3vG}w-J;hcoS9`K_6na3(15TT8hVsFygY(*d87BTziM`@ihG9+ zEF`xUz5a(px%cwFFem*&?KY2d^ z1!8!#!iR$O&+hOkYge5EVj!MlR=abx=JHwiT)!*WHGa_jDt82iup_Gu5?W)2miFQX z?&^V|5t~DnXI?^3ijDrqZ5tu)*Mmmh0PbR4=9OBB<}bhT=C5@RW#2fZ!R5 zpgu8V>6jXxj2-mZlBsF*}(|YiZITZr_)xx?X4>3tF*SxGJjR) zM*RsWs_7r%eZck)6At6qz?YitQ2E9J6PCShwAMGok~(b>8orOcytOQ6y(K*909QDM z{Y=W$!XkOr&hG!&uK)gPo_Sv>NV16DAL6!q3uJ9_lYRk5g^CY?mk-Y{kv!q7x7;g% z7V7HQXfZ3wNLMh0>6tZuG)Vl%)N}NYPLYse{U~o^;>Seg!+MP_u$o}Oy0K9U@E>(t zNVvvd&Q%(5r66K5d2&qe?;`*!Wawrvi_4AH79az63|RW(!#VYwQ~H$8Mvkor^Sh|_ z)%yWXJQxORg6K|X6l22T+fD|QXSJAFI*~sI8@W(D?E}5QX(JpIS}$Ny%*}sp8G^tB z_@XEzlKYlT>T%+Jg9%bgGN{X2<&n~=!Z5K^C^9avr&u8E0*Y&&h4s#;AdTg7 z^cz4eYDgS4TDbx6V1UgXew1|NW{Ru0C^LuvL892;47k4S1e9(J&qmRjg-tos z?;rML;#xHIJ{t zJsail5?le6eaZk7=4k9GfyQV#7R@v?ib;%8@_GyUx?u*N(Mv()}ZD2KXn=RGDj$q*E$l!R&-E~4)>D1?>AGO%@ zd|w>)PCoT9D)HEstD>{@+|KIUnSsxkx}|&vna=dJN{NJL$hk>djW9XS(#WZ3KB9`Gjt6}Zi zv+PsRd+$7Zd~pf?Poz11KEk~C!cTuyMBY1EK%gw7a`&{mN;#1{g3X-%_ZGT5NA_9;z7*XqL4 zuiK5Af92qzxR|2d1#Y53Z7X=CjF6#ZNozmmR4K$}f`S}7zI)5S<3V_m@VM^?B9-@x zD>3w+uh&4dWg?$Vmi(}87)qr75DMWv&lT!B&yzoQ!o;)S_&&Y1|Kfze__U@ZcI~Ot zLXLDb|Dv7oRJJ<^T9NpP?ex=PCyN1|f&Lz@_kXL$4K-r3O*Ki9g!2|`xFs!;2)Zrk zl~yC93(D+yp!Rdmu-1*~k^GIrtzPC#gc`5AqR0$oc%GP2jlkKd)i%N#B@dfHdvn#U z;t|{Fr0=K@8I>T1MXAkzy9@#IsZYrdL}e>ANLQnLHHxl|`p+F$pes}P{agS%PWXLt zFbnNd9Im_Nvt6F>ybg)m=ve$oEAC4AKFwd|5IV1fDRHLyDO_fUsu{)7Kd2$m{&*aS zLPPl7wEkLfk;ElvtVEEe7##tO4LX!f??XtlFK4MOkg{LONlT*8bT|WH7E;*x=s&lj z8Aj+%haJ*sVXJmxKMeZO4x1rC5d_?fj%T_t2=Si7_aN~6(ha=(b8w~xX9g`>24qBF z!W9m(LfwGwg5;)wc)D4CZ?Flz1C;5u1b(P61@dvQ3mB=PpmBg{{;3PJVC;9zuozgM zlodXv`wSNeT5LSCZoB30no~796;c0bPu3-O!0E=i2vUdpkEd@aT{T=!6Lgn; zI|jFCu<<9fyKgV~R*xfo?BA$7q|6FDQs2422qPi3_d$%i)VnVmj`laZpSL$Qype(} zfFJY}E@p;V`iTtnuj&_>-j@*F;5P6e{Ozm?vDfMia`kWf!#B4jssQXxnOEw;y=p=v zq2$qW8)fGB2#*3f>W1?bnHPf`n2+CzK2JQ0O|n_u?x)Bym2tVL))@;xC~WK?znI-y z-3iT;{NoqqXRwAx5>46HcSgJeWaJrPK45MJ!!vGRc>ejUM5Ckb$;mIgN`9U+WcvdH z{XZ1Xx4tU~N8}xy16`4SGb%{c(YcAVp;6A6+qN?Cisw)3zLrQ-=F(R7Ec)AVdGN&^ z>Lm7cMExjdf?W!H{cw<08B+ihKI|mF0+iuq-7fvXtMX;Tp_%(QMHs)U|GFhGtAUN$@cR&tSUQE2XcKZ34#Jcl~g zt}(KN#;_OY7$0>%;gq3*>|z#rJoT(;A|nXX=zvj38T2 z#^m-};FWT|plrTIGI4o6qjmar+K^<|T~8lo*yy^)U_40=ANm0 z^WR;MX#R?yUX>V9vcD3Db-nOCP{5;}_pC32Y#R8XG(>`AAkApi^n9f9e=5V@R3~^Fufc)7Y zp!4(Xw4Fe5QvQ^m=ybH0jf~%AjSIU{>Z*U71KRks(J8?&xD!M@l>Q=#rS+@#HZ$DgcxgNY2u^5{2yI0wDh};Jw5R zVVjcmnYhywkrxRM14+Wz11W`0sD@qeCi30s@Cilp zXoW~DlTzE)%l5&r{J^SL#OX$+`J|;l&qeX;{meg1N-iM(m~(w-L`dh-90$66j||W z!#3=|IB!-Mu@fnnIy7E}K(Z{~SmiH=eOfBWY+I6Rd}m_RP=S=93XERoBim+nQ13T9 z4VPY#F7)x|IE&AOTq~FLE(rb5j?;xuhEw1me{+}><6DMnI?lk1|3hiC=kNo5arMe5 zLVK+=y^zbHlvDODdZd9N$3Y*E>d>WTh+Sp8nK)4BP?WE2*=Ry#O=XM{&57M$BiC;(7#-Y1uaQ@C8&=byk_d|sYgth4d=>|qE$kgzs!8Ui3 z1ZHPIIhIDkLliM5M`_8VOPO(NP1aXF7)Wl*fZjbHc2<<$NTnaDL!+Ee+?cPfTEe?R zLljib)(s_q%eV#oiY|&~Qoi_*eON{>9ROYWO-xKpusj=9&Pd2EimmC`M^ZF&6AkR}7C^hH@bhS1j~2}{D#pI#6&0SB z+0v*zV7n7HKYoXBcI&`M-~*j6<#_3n zh1E`n+>Rf7vaeA%cn>6G2ynle(%tZvqg+yPN;u4A*aA$(f<((&cIaePk@Bz7z#&OBGjrwRXKk7%SuTcvbD0hhrw+6lWv@~iDhdepxF;~ z`_0WG-#+&2*N0V8D(hCW4MhAg*L^kHpC|BRat=3-I6r+N_>I>+o7ma7HpZ-uFRKF)W`~#Ram*8bhn`@Z3tqf&C`g4D5+{ZR^TrqK3M_JZG4%X4NQY%O-|o@= z(?(RhfZV7s8OMYBi1EE`T5ecdJZ{dNEB zv|W4PFlmRD-E+tr%RdL*>E_M4zncKd5%%^Tu3wv`poCpQuahb6+-5Ug6Rgb1uyqu` z+ik#JizUDyS-pKpq*4Bak$Dt#9DELVzSbtLjGcEXZN{ZtlECz$`Y01j-ZXuyX@4Y&*E&1+Kr^dcRIAx;-cin=3qW;hk9G!sDI?D5rU# zjmpWD982P*X>EXF!o!%^o4U9L5XINrEz(3NmAQn;pi&-pQm&D ze@TJ96z8>fz4Jdtr(;jVXCW?}?M zsM8*I4j21CB5i?`>ucBWBt}&JgC3Ffmh?@pxz{*sCPeckr4)EJ7H&x5MwF}G)>BMz zll*fBLtgr9bY?Z&k-GAr7~w8y<%;tYZ+74wP73<8;p2|vWOZx@{LnV2(pRr=4Y*G0 zd=K(6&4g7KV><^xCWyVvBqP^v6$sYM_*zYzdax9L+IjM?rLPX8jP`_Wp^NsB#Us3} zZSu9Wqc4pwcS4`=QSVb`bvW`Uu@3@YhKLH`q`($8IpjA9fuP_h;Vta z4`Vmb!T8^s)qwK}@aI+?c#~?Ul9M=Gf|`o(g@l4|>G#oMduiugwh#gVXrl7Yg05a1 zoCr8!zn0m{_PP1SonO+MM8bbr`OQ@(ZW0Rtt^ua=sh@sqlH1;tmaU9#L&FvV^_Am| zsbWtM?l{ljUv0nFRmQ#38<=|B!(Knj{ZS)5;|l$Sa37lrX~VJj{`5xv+vj@5(aK;A9H?HSH9xNvzk*&W zMth$YxaF-BlkJvle$~sUVS8Wqg72}BMxGGMM>b`J{Vu0{mkS1FO6;a*3hFQ&WFwsk zx>gjte|0%K%9=x`bby%r?ca<1x@n?o&2yx+3bvS(FF#=d1P44OoOTh~xXg#Ylef=H z_~HxHp^Hp96cj~LHC7tXZmq|(TV3#JG4PU;D9VZw73QJGG`o|sy2Z7NFYmLi#~d)V z-{`hZkPn>{i_Hw>4cZh8X=-TQQ`Crz1xicBeDw=oO_+`|aO|L|@>msEgyk{UpQ|!F z*pPSp?jDQ)%8$Jqpc^kG_bL;-aj|(FD}U%PkT|E70D=THP<@@ZPT+?cvXWekZ4O{! zFaZ*CZfXFUd@}r=VTiJE_HHksBi8b)*yUnzD`F<2uEG!kaHP0qjzb<8pGMb^!j;E$ zROxP1rnXWxllb5YrM11#Q($p%1!fjV)IEaLEIIz%%9{R0meo-&RZ;TW_X)>`ansG? z);kE{KP`*V+Y*a;=g@uQbFh1ac18W3a{t8*y6U{nm?r?kmO(%Eam>|hoTE1HU*NxR0}YG1Y*lXh0CKvF6RN*MXOa!CxTD^*q$nmSo7 zQjOQCtp3|()3D^#vr+l+F5g20GMm=Jq$6CAjuoKh`Ehi&|}EiJ9d3 z;{DYvT&<;kx^AOY&`QlQCgqc^Kr*fHn&>ZHMW(2CAV~ZcTg8|J5EAw#rOlwwdTgUL z*h0;a^GB^WG^FU7?_Xx+u^A*{bJ{zl%VnF&L>P?avcBTVRkL=)oZV+pCU>Wyt-cf}O)`~?&sRF)TeYJyp*VUyvt7`6NP zCnHo?3eX*ynK;5E(bpUqxvzb28=)wH*icz8A_27IuB7JyoU-sT$`vL92`hv+p?4{D zuYm)oxk!}bRx;xHG@H#UODJ9=U7QFeUeD4FsJz(qapAF!G_l=?;TKQUgpFCIE54*< zh^(%Oq&?fr5uhe^ab%@Y$g8+}p84jqq$EGpRmYc_A8{YjYeN44IY7q0CM=S<5dYKP zXC^{YB%!HvHjFAr`*Z;y8hzCIjB;h*SR` z$T*V@(c4C={gA(3jB#aanF(!}o+?J4F_jk-QW781=&oA7dG=YwLYl+PTelj*1(4$o z9y8Pq`tVo=PVH%!{7xazi~iOKqzI-x@|fAA2+mx+vMOLx;*p$LoTDmT)hdF83P5^P6VounYEa$Bm4 z*@;@I1ujL?GO^V>Obnz5EU0CjFBbw&J2CaH#1xu~NK^^>?hixVfF48~8BM2S4MD!E zX++x%AXakyX|y&>^o^?3)}?|jZX))pa0aZnlc8}CIHOwIuE?TQAs^`&^UyeRW9!b; z1=%P91-7%_w@Y+b5o>-CNiF1dJSbfOn=8o%k@hqyRIheUGl~F36?rAIHgatgd$I4~ zj}y#S!_b}gW#g433*MRDaYZFv=wZc0$gIz;>EE<7HJCrL$1~3}Dc}*?;?zdgYNZU5 z`zuGvp30@Hlds7&$tzr8JM&B?;es&U7ZlSTOdFvPb%mV z=}B@qmZ;vRjT8Wh`{`y&#jfgbJ8>f(hj#8Uvvb6T7~r-{6CAJ(TArVY5DAyqDzS`c zj?=q3Xim{Fg)pz!TdV-t&AZ zIhr+(w2BKVIR5*1pr7vPMg`+ zQcxck78aRHD7_l~cCapvKwckr5w^!P?7P%$)d3Vr@=d2ZA%Y$eZ7&H#Sh*Oo$_>iF z259&(j!=iNt*`--Fi^@??mhJ?`U?%ldkNpL$W947HaZ5JSw#tb7d? z-9t2+mr|X*hAH-=2*bqg>r`&Bw(dy)5rhe~*OZp(CN(K672SLcE3&{p#Y?}62~E`MhADI;iP#mvqeeK= z3wk)MTRDYMf5LdeM5~(HVemqKJKV*q3lmPzNZwBGzQ$6Lp&F6f3GP8Ug>|h=Fo*U9 zSCkexIa`?>z#-0^P()25RYijbs%D#rIFQa@H+vq=N|C!`o0?I%*7_QZs_mZg=_8Ei zU;0V{Y_=<7M@5(^JCghgH)%XYh*}pfk?*B8eBXn-l-|xq;DMfn4Ak@$e$py$r#pN*kZpcePA_Od=0_tR$c%&?RK)B5w>#z_{xau^pxMgW ztImkE6sJtV&H=ihJHvN{1b>({WUz`A_QR7q9NYj`Q3cV|JBtTGaza`={ znGL2{7X3w~@3#2&6KU$dF+eT()Ya#DvpxN}1lK_1D~uC~fn)0wfO>a5)z@(oU` zHIxr*G^g}E*&29Kh@Si9Lv1?TCmqwiWD)QMx$B7%6Hw9qY(lUb=Ncm1ztb~b21jI` z*<6JinvUYos`Z@Pc$5?JB{!xvUBpT+R8i$qQXY zZv95!3p&Fq8jYiL{!(s@C%Y;USB5sI2^B$L;Jkvt#)=F$hfUkK{pB~Cxjz#tYfb?Y zk>#~fUG?sx_w`b)I=LwBF{A6k!5v@u72<{D1w^)APwz(3}x=#<0?6x@Ya10_0=;q_Rz?DUm-|FTR0)>zE?X>aX2mb0Bo28lX zK!{wQ?6uMZ@8%pSbi<+f%Yz=vL#z?1`CK1}znt|&DdZ`K#-2J7KHF`yVeZXEngG#m zd8j=4MvU7ZkgQ_ERbGW>T-A|%H6^@L?FAqAVB2K;faSwZ1jjl;*r@6o9K& z*D$od1g=WJvbk{0-e-)QCtuH^4~ zB5JUKB>pL}MBW9f69)D_;ch{jyZ>KU6TzACg1uKNfMhQQTz+*)Qd#qSErkvTPiv zfPUfW)ek|=ch-AHnK2or6 zXTL4K)h?Yhq3v8loVb*qM(P1m1uQL9*GN7#Dq~$e0|#=wH+?x4*Ix6_XQrgyP;%Cp zwfn`|z)!vfX`$p2xd0XJc83JpmM_g@i$0}2uSlONnQ3=cVB&L1H4CC`2gd5A#XYD+ z4oOyXZ60(BTs=pFkhO-Ry@h|lMH9_2m|s4RxZcx$wEARaHdp7wG`Hq@6T3m8sk>#E z5CN`$&tO@FYN}|n93n?95}S7MqkQ4Uvf4T8;1^Uz9iZMcVmG+;vEGdOJ}$@cyAhfB zl{NMMv*jn4DXbjsgbMDHn=xAZh7u1D#2Q;NsY47qiTwu(Tr5uPnTcCGQ!qeqi6|v@ zlqpX&CvS}z%l1y2VaVPdK7!9lj7mlWl6K#$WcC<9&Kq{RAv^$iGj(bv)9T9egJOicn z5K0`-O5eRiM^i99E^JjM9yzhQmNobHEv*bg1%+GkDF8OGTfxM&LNT=GRE>fQMjNdZ z&4k#kd=uz`$IO7ZJ$)8Ftjb9SpNFie8Gl3U!p!WdxX4C~jE&Zj>x#j1OQH0|pUY+j zMTzSg1^iW9^(?6QSC*;Rt;EHP-X`~J^hHARuNCId03TQ2fQ<~g63$tn)U_M(Tl%gDDQ7H{nEadEr=ll2jYh&;U zsNccc)$ne5bdB-r?z$&qagvi$M@o;Mhb31Jo)YcQ6Il*AL`$+==Fu1 zm%n5!cxcb*-Q1sRYi=BdzA)1aBCd8Iw6J_KC2yDkeD}9Nsmx2 zEHGDPLNo%G$i$v2Vgu~^Sx`6d2=quogE+*e_Y@MR^eS8Re)52XJqn8a6X^!B ztcb-(ExhvCq1tZbje(;aYu_A7(0c{@0Bn_JzlgW5Co4t8mdXae3lSGSz^OkNdW#nn zohZWOhf)hz)Uh1P z@DQU!;-+EVYNVNeTaS|anp7NBQwv5(8_IpPLdv9nFyet3XLLl-?0va&{<`iDK5AgB zjr*XMqo>SUu#U9xuTUFgGY6pl)isVL*1u=I+ebPZP`v0~A~%JIm@c$;UC>7IWz(2< zix1gJ3<7<4`Jvc=4cD#Y077uCU1iosB6PQUdTeabhc%qJzhz(@Skm3l#q6gI=h3`u z2aRLdgA4r?F$WL`VZ1`x>g6FxGAO3W@+MFfkktL-gUndJx-1TPmR@phmKq%iMckPp zqTRXMvqPId8Ow+E-xoc*BucC+FI*$6;?0+)AianCd;c`{vi(A#61rPA(t|cW7$rKv z9woU2}t$xB89_22r)_$0luAn3{n9ql4nQ_ zj90X}iQ=*$RNzrh$g5OzgG>E~>{%yw#E4kYUuNd@6V*P8gYA0^EnP!mdT2Oc!$1ZO zV-;F7S}gLYGbxnI3~NiwRfQp!W%cvOfmCSI3+wz}J15{7!;h1J7EU3(Wp6p)$F)OQ z-L(_Dy_0hRS)CRi1m+I-zo)3?6U9L0#bwciV4p5kKjZT`N0ehL_*~2}!te_}_bCH8 z?Yf?u%VlC^!zFn{trxAE5=uV-ZnG${JH(67+4?TOfr_}v24-HldGunVopv{5p|jze zCC7E4D0=Ep?8u})w*_|LW+5cbStyl(IeM{f9;f^?O<}%-|4YaB;OEE^>GxFvQ61#2 z=Ln-(pD>IH>1#UWopB8#^5dJ99|rAh*r22e^`iXy*x5Tvif=3dSU+A*$AQM4NOwni z!$U~#BTU)D$fKe#$hT2+i%7TsmQ)(Pr>#gn@jT%vM}VN`>ugE$al8>X9bkAz z!hXkoiLkMwZXmpVscb$MkCEpbUvn`HbOk0tWDXj(!^gDYUKsF}kqb&aRZ|LpO)y#u z6K4-gm0GD%;df1}Zl#@XD|MSmFc>K4q>9gUOzQe@E1~`hM{+0V7!#SpTD=R04rbuC z@}1wsw%KQcwpZlDM4{}jFkhI#B;@>1AB-%_(PgGjyxw6rH}~$n@Y~_5ODHU?)cB?| zb4VhYKK2_p^*JtL-vs)vGiO`uV{5C#7z+ocPCw3PQsBqCrSkEuvllyaZ+)C5E-x6r zdnQJ>bF%1DwaET3?H?WhA!+-XxtB;X3NzF4{LuL(z%(c+C7Zc|QZW7|Xp6vhJ%@V&Jf_gKC7t#DQLF1BCT9R#xDJTwMP z$_LROFA;WRs`kXXke<1Va&$y#QOi4~gS)U@QT!W(awXtBF_DtRF}UREq85JrfQSxu zfOMJO(I}&*9cT9Cc>#mil!!b%!)}bISt>_+wZ;Zef7M$;xqqPD;n{;bHV}XmKnVIv zdaH=0dal+}En-pPC`(UH9DrlwsC9MR`|}N{NI@xAGj>c8#v3Z*OtEXO4z&lLoD!?n z072eW;Q(-H5c{ihfZKt3Z?em}S}Hh?;!OjQYN=Fo|^x)_O(u50t{%B9Gi8?6&y$2ou|G zGaeXEEI3^i|oSRfkB&gvmRNq_AWuaCrf3Y(7URzUdQ1JvPN`uw1f` zy4Xp_6Cx;}7j(Hq>2Ewo6{^A_!pN27g!OWdV-3GRJHpCpH(rmu=}r(Z;ijN2tU3>S zIi?PG^C)-LT25L<3s~YmOT0`Qa0`m*O}&thPmFF|L*tKY_tWJ{=+h z2;ALS9C$ICFib0^6TP#=KW&>9hfK5!Qwd>;VTq?}Rd_-wb~N$Y>!5Pk_}BPIOh=%P zT(L?&Hn)2Dlyep&^0=9>UCbf-#)~!GI@2ZX&eAUK_;yf~;f5`0=05D|&*{4u;|gEG zpOC^%rv53fk~=yBAetZG7wL zkEe*&aSL5w*QL|;_gP7->AG!Ery82Bz=pI67l@uM%xTY8pe|5BPZyTtemK&5w@gHG z9UryHJwnF*sx)FE%S|#-ef$byoOm!rW&m$ls#B*LOQ=(M47B2ls5SLBH5St&M2OHZ zWQP9&@Rk0bv?k$L`%2~{Zkl;Su;uMhk6O+_OcWo@uBSLfoi64WLnu{l6VprN+8U1< zFX`|glF}WZya^;}qxcxm=qdc;9FU57KfOP|eRJ_C zEYdT1!Kv7Uj7ggDWkHq=NewqlbMCuvDQt40(>pCL*;T4kWUOYx^nkYASz9h^Dedmv z`}CL~*6^|~QV9yqTNWZ}hz-q_U~dWnK|`>9c1?ZbtV)$nDc4}1$|^s$+e?hf zQ9_)dmFyx7M|Cn)5kp+9 zeYDe7!Ci&<)agd&Z=!j&K7YMpJC+59I|}RsWqdhz9F)b@a_`8*u|TZG6Cgl%ta@mY zYW@Ubrl0%4{9$wvh~De#b&Wria-zfXiN-qP&#G1HZ*oQ1L}@&z>tVdd7B_as;hTGB z8kd>^gK>`Kr!9t4Tn|!tytF%v^koyi!a#rk*ytO?r7=Y@bT~)zkPW$yxu3bqy1mHU z<0~m0q+jvDyIiDUD540CP@9|$@#9G0=h>AQUjCqi(a~y=i}^WJN;}cA^%ARXnZeh8 z!)aEF-kWCw=JioYJCUqd9iqXXoqJLsCn}SdCcnnqj`GUG#+X^kXxuZLjrl8ruLBu6 z!?jxv7ysJLCWBN(YXYfuW#SkEbC#$NN>3&croi$MLdul!v|T-vN=t0?Qs)MJ`28kL zjl2{I!NOm^$T-=iJ_3NedyoqH$|BP+LY;%izH<`XgubLi7e}ad9L|i=Zf3s|sKjIX zc^tF#zRBpR`!ofMylQGBKk#W@=f|9{qZu5eNc=gdB0Z$&#eqn(y}-uoF0`d&o=)#a zXp<8uGm~HimSDO~3511FJ!seEbR#vgl0I`{jJF>FG;q`!qM}m-+%Ov(uW3&+h~yJw z;>qvJge!$h3v~gh=#+LHZM6?~eYx;>R3Ml=smcwVez8^Gm;}9gnCEJJf1pAh|Jzb| z>C>I>cs~#_DT2R6nUwVxShuQZqx}l=lNKD_Vgj*x%kq=*N&2uM=|+&A7VKP#x=wFY z;_shM*F`l0XNMoT^cXKc1WFTcOO;kH1Fu zjYMUbIRZjnb+|4Z-?eq(tngr|kNYsnaiYxIb6a}?*7?=2z_Tzs!(+$CRjO7+_}-M>%a=g_Ck>^wuLs zP^7fJh#acW;z-&&f+t;pWSo#y7rHUc%6nl+I%twVxo z4Ss4Wotn6~b>pXH=Yy=g!{9$Gq$^?RthN<#7$zg<%Y)!3E^U09HsankX zV14dU*h;!(!+IV_OLZ`+ym&!s9+8TvJB8Bc)mv`6i!}$`0VSCzy0HUOO|2S7-GQ52Mfg<^ zK|*mV4S4%|K;14uZU(Tq492mwBkKB)(ft6sYkWY_VLR?o-&HU?nZ>ibix=pV!DnQJ z%l~v8W85%m-{b{$3=~D)4cFCX98OH#&hT2;NR+RE^#nsxk%8};u z;!4w|BUE4-rMk3v?pLMid3zM&v_f%{{>i`%wPk$?Z!^($rd+Evj(n7ip#cu@B`GW< z1r*Cg)o&QF48~~useQo!jD}Q%m6M-2?Wz^l2q67daPfEM%~NH!R5`A$h617UvS9l3 z*FfQ~J)U02tp1>U?wxF7rbt=ZGLyG=(Sry|_;4u5$6uq~McT^9F~d{XwF6*xu`ZVP4J3gk3tyf1j!!#Rv0W zrS_g)qeoaP;rwvT%|D|eHrTWAf%8tEAXOurx;-3~6)XRtTl(GJKPy2){o}8hpKYhl z(P|ZNyY>N5BRoW)7G7GtHu_;w_Y}1lrXIR`FawF`b-M75cAlR8fKOZjE`S&`zaS z{mCzydGxP(eDpG{_xsybwmLMBC<4%A;kUmWRTMsTx;ci$stskF7Uk;dIu2;8Z^RX=QaF9uboTz{-#+k$or;BYS zNKS(+OA9BPH>qa}GqRF-O;k`oXO|J2L`e+D2li{eWm#i-NYWupuq9An z%rKru8#So~#f5!dV{wU`pmJXK^gotqbKLpDSFZ&QP~b$ww=tNm1(l}@%o2)UOh-U& zzvBMX^k8m>6+B$q^upbawC{hTuT;wL7SMA@EY3gR2FbKkwdEFaSifsp&nPQ(0WC;v z%#zRruzqUJj2Nc#eHgw-VP`|0+mnO>nxRzS(d~}@tk<|(lc*=rX*oOUYwx1bS7oA$)P4I^teX_W#-=C&3#T_Lyn`SApeJp&J>)}cFB>q1ORL+0+Q#42sDbbmg2Foa5|?TFIh!;@T0Ev*#R7_JB+uJ%hq_W^ zr(1K-o2vs#l-eIHr-r=rPqEn=^r%~X_h@{|h^6WB-+tQ^Xt25{A+#+YB$eyqGds42 zct~AG_h4mG8#f|8+TApX4ou_AxS~(M`V9k9Whs)_$6%mQWl9q`+lh?|cQs?ImK(3j z8Kkf%hk%Yo2X5O77wmsP=3cyoBTFv`vsgjEYj;hs(GuaQ3hCyvTs32J@O%h}j?YcI zh!}txP}SLN=5#>ntE(_tzINvefGo+@S5EcpJCwAJ71UTeYpoG@8c3{1(qLYs9cC>+ z#vYjC>m&L$|K9m3NJd1c46>R1sp+6r(2zd+`pETEi}dc~s(UEx*%(*D8iw4DpWQB* z+gol&7CkfYlvHem)NRz*NeJF5nKsXCjqvW0-8fY$O3yOZK>k$1N#F!WesJ4j^%~K8 z3Jr0p5Th6l z^PJ;y)jt7Be|Rl|rfx3oww{3GsxziRc*bdvfJCLep4!dNXK;mJp`ygl)0L~jKJ2a` zB5eH9)#t-x_ZWWVoQd@}Uy-?>twP9x_3}*$UAkGGvIE{9BE&BU*j#3yk00XrvGk;> ztp{+HN@}VSi(25P$v0>L(*mgAt)h#BJiy(lk&nD88qm`eG1V-ZI<&z#w7v@{*iq)=c^?eWSZ7IEVNAzU;Jw5NbN-hn!QIX?kS*I-1 zvp|GFv#tJnqECHXXmXv9GK7u?2+^r!m!jo0f-HdbN;xGpVV>9_^ePFV$n10JBO^~E znhDEtJug!QaQXfK$izRx?_i>JDn}~j(I-Um>z}IT@mw)(+GQJP4$*pu5$@n>4KHmX zT*zpTk^Les#r~hg^`Q<}%FYI_bHiA_$(r&(AJcCQZ@gR^;E@e+#%kZ37@yV#Fa!M8`+BzIJqDSI>Rp~KATUQy4zYWdKe5CdKm!P z2{)&^*Et#UB0xkz6|>y6Nz}=eNcq1~;_VhfT&lg>d`+=3x$G+GRq`N<{?AfRhZ%#P##E z(nK2=u*zAa5Qsf_w(4~jy1q7Jb-3+o9Q~9*+3RPRRB4@mgv%M&Dws@Q8&oY8?5up9 zA7~CV;~Q0drkliP$v3}jT9)pgBn^I2T4UnfYX+YxCbMk_+lCewOi1~%486){Z)jZB z+32Z@r)9}BCcHwlvV$emV4q<&zcMzH+nh~S)?T)^`9~9^t^WpXJmUwb^W%fc3Q2>@ zaV`x+-RY`Qwo^~! z&ZCZ&>{1X;w1~7tj(s%5g+trCq3)66NLluM3hs2xJQ^*<#lzUYEir!H+af<}51kGb zSitTj_3cbfU~BQh;qX`VU&T&{eJ#N>G*kh4G=nx>W;l`9;zTkI#mJag>&cM**#@0> zMh88BP)(7*RHjgLrj|5HJQJDdSx~v$xIXGT7g#0n+k6)0u%^;*h>=p|rIU+Q7Z1a}~(!qKbEaua5F>`j6FB;Sl8 z2$5u^DwbwL29zKMs84h06jb^aB$*pQ`2odr&+>!FuiHdmK@1Xv(8xj9t)jHOD%4b+ z%boF94tWr1M;F)OW5PjQr3jt={Zx7GunYR@G#Xq0Q`%2Z9&XdfDn0q?B$0OtVbi-R zFsN@S7KN^kVbcen7B)7qUPl(9YcZ%NTbD{mHZ-=eb zL72&G{JcqYNG&ZwYIEm-*b*NAo>Yhu6ySGn;mWkT?Q|oI>DG+jU1o&Pt6No&L4dE@9B#NS195WydYmUU2t- z5c+IhPQGQGqeez!S(GFZVwn7V)ggY7l`iQ=1rnBg@U|ixZ&!PUT%-rZ`%JqW^6l#8 zkaUaRHM1fc0}Yo(m;{CL_gPyi?ps8?ggg-^V~T`4ddE+V<W>==I`KEkJ?+qgTqV-EJYh^jaH-cA`wfuqt&)zD@v z_2(SY)ID3u0d?El4K}ca!|Dt1o@9k>^>bmmy^yNS*H6(LpDZYan?J3datUh;a=arFm5c$NjFOc zn;|o!h?BVi(54U$0lOTOPFTe~ABem8dG!G(FjX%fvQ43>4W&d*2%(a>JQ!)%gozK1 z;J-e-pb*-(sPK~QRYy3|sqK<)T`d<_dFay2pn1vF*?=6ok#O0V&0`VQ2D*!2Xtt&U_(PQ*sxS<-!^?kKdfCF1d_r=&X&fGRh=o9pu+RR#5b%3{q1dus-&WpjYnV^sn z5>B<%4yZE#9J)@B$fKm$82E@x)r%<9CL zgFtkietccZs+D-$8WJb%0M4$DP9X;Dy1-x9vQ-hJgbC&R-UV9lCN6pI%949FQoJ^r zc%R9+k!SdGlQ_vXb6x}$&*y9M9 z&?~!FKMW91Nyq(NP^wpDF1DMNPHJ_F$qLcck}D-b;SnxtXSXIk>YgoMCOl=xaRU6=37a(u?XXbMi-bJ0ND&N_83gZ)@bi;Sb94y~Ggy7&qD_T|yC zE+_uZ*Sk4uhs_<^@J#Mz2P97RxRci3EJRo>3h8@sBe~`TxfpO~y@*%3P zH=7(e)M6oxwz^<&h06e0=6?O%Ex;gn&&ohG(#PtU+om!POha{f0(5b(AsPyn%;yeD z(O0n{rl5}vD6^{5HWNfeK$8z_wMax0`I30xH;-2kZEnN^|9yFTAFFYrezeNg|1ui0 zYd$Nb^zH<5GDFh@`3+FY2QM~Xcnm~Cnn2`|sezE%0#%9!cS7NQ^?MEIW!!6WT2`Db zAt3~pSQby(;NR z$T7~Xv&`U>U|BjbHT@MFu99VMU!n!yb>C@{{;q})5?#$5t>@Ut>tP*(Yz6Pf-a=l3 zrCD7yDym;3^R5wwP-F24@sY_Rb2Qt-F8KFzh99El-@+9SQ*vD_+}4Jk>1%Gv{ysEx z*M?cjlsJ?u7uvc(Z?lV%K^e&2=WuaP9$TWzw-TnN{yF!V(Ao(P$^F%5FMIDzG1ut2 z3oao{>=?@jWDX-Y&Apm-Gd@7ZJ}!)_95w0ksd)X!6K7_Nli-GBuPe_JHk!A?Xiy7i4y0_tEW4 z3iLpR@N8<&4fbd}_{R|>tra(o!lq|GT@LFuQ#Ym;HL1HXeu)rE1|&lPl+43tSnitB zdX-Bc7X|B1G$y-6$hU9Hj>hctb)7y(pfjd7N*Er=3#oTm{jIqQqr2uFZLZ?3$WU)aBQWY5`@4qSBUk+#h0OKLo~hki^I-EFy$w z9fLiM=-JLGlav1LWfvx2tI{bCHkj{y0T&b7uG?sO&D3gc<74$1L}PIDd~hkhu_MeE zy2k?lmU_P{yv<>)w19h^y>8Bh2c2D0mMkIY?VXO^z4;N(RotcsTNJ`adYYR{a{||$ zr>teigjH?^HBgaLvfJHJArO`f{?@7mms3$=?Jx~r3h3g6FcOyhNr3~nw+m}vcgE@p z=aA`X7VmMERgr)3h`l^#qcO#WOBI?U`XQQi5ESDp^CL|ZLwTxly9NwlnlooC><@yf z&=cF7LJj{YNc3MrL)P|6x2kUl3IdJRPsRxSxsX(HzgCPR@gGBif;80;U4kVctK6}3 z)1rk>kH@wcl$u8}l1(M2+!(au(T8oFY+9rpD$N5p=Sv0n`$-dcu5cp(ZS8_l&RdZ? zv(~~6!+y5s0s13V(mEkG1ggK;Ns?z@W`BnJ384z{7ywhGDDB$ZV>@Oi)2PQQ`^ojA zh8(9MXJ#2wNXMOk<)WUuyN!jJiEBI=rnX1o)d{WZ)&DDVKwf$bV6A#VFY4b+}#npbbJab2ed616Wtk!18dxzh_wMAtXR zvgMWQ<_|xS170EyJ9u*6YspERg+M^IgoBYL!m-4uU7A4QFff@Pyahw%D7@jcDe6t5wH6SWi zlHfSMy(ADtr&Y83JlUsnA}HoNd2tX+R6(1icH``aoRF2mOU^czfj^@b+ct9O)O<;e z+p>R+>2+<%iLT7fH?=wVJ6cB^dn-=o_pF0+hu-!#Qeg4RG2Ela-+R2}U;Ov@c~YEX z>uDmjK~5y9#p1)mjrsQr=8ZwtDi9o$tt&H5nNl;r7iah^=cR=TkOrltIYJ(eK--h;bIY+kl9anRaVLwBvset`L@M`6 zI}1Y25s}dux?Pba#vKP=WH+$sk_u+WF3H_ow*{%Q(rnW11}RYErpaH%EsQ!fHCy18 zKXYtZ%3s^`o%y8ULGC`(4VJ^=Y}(3vtVJ&!ZJqhUWu$x+N`K^N1SJ807>x{7O$^Ga zBuIgchQywz>%Y>2lipOA2*C38%(u>Y0+m$=X8;7zEeiguE zwQGAg21gjTC*py+Hh8VBu(|*&<2}fpI?GvERuO@9A;Xa%w-h=&0z{+_pUJLXK@pXfBH~wWGtIwiY>Dey0k{<+wf%a~uzGzL?vLfrdQ? zT&+pDqM?KU1I>)vlBju1dbq`N99a+>eu=ZAVEKJ9wyucUSK?N%H)%Y~}KDv+#NpgJN!&p*+^kTZ)ynr4$R z$n-EA0l1hL#i*e~h5C++m4>jZZyC76tA3dU8M1gvj+FS6AFx+IWulv5CbBf@0Z?_v z_$Cc&MP?Q$R|()T;={%Fq$Dx3W96S~2LB=mg7~mdgY57PJU zX0hb^WW2MdxfLhe?HG_rey~vtDO_ShAlKBzYAKw5YP+a;*gW%#`}NHLMJMSnsgxe^ zDpb0O+C2Wq5XSiWw(4Yp-(>ey)f7=G!hP&caE}^1M%+BYR|U`Ub@5CrA@U|`W;QcNQ5y{ zI;8#*g1K9&zpdHQtZZzAVhdJBIj?uN9J~uG3R|&2$Nzj9R#2VPB3wS`Uu_R1!3Yq8 zTkbRFISH4xe5A90?~B-0ZOKpi*3;(mF2O~9i?coS2DB(hy47_p{B!CY;OUbrECBVl zRjtn-T2VPLJXg1wjw=4*r%FYe11>unCw$AsWXh$rF|xvVqdSLFS;i&`YUKq589avT zr^TLFX%{gZV=^@>TR*bmW{B2#G{zj)KUxR$h72P8Fc%$c0|Tj1%#pp%4`VgjWwI}h z?4jBL3U-M2i^x*zJQb}OCYWrB{r>(wM`K8nTHiWo5w!-m*O;<1QAC)uh8 z6yR#1f0#d*z^jw{a;;U>KZ#m5< zZ2m}~mD8(p=+y^1*k1#|!?E8@bZ4*4dfyXgIAgIipnXRO`juS4xZ5@$KfN?Z4uRu3 z%MVd7{QVo5i_oE&*c3c{quG6Z`U0LrXz$o(b&NBb(pM{=OGxg%d#war9-!wCAk=o7 z+pY>JZb39P>C1pvHmKS40U{RqmMMK= zA@8ForVJ*oyR^+hZ)UT;S4cgu3!)9DKUZAo0?eRg8={8wqR+TRK2lqy+>rcP zX~WoKmJSVh0{|O{-0y8HmKPLOVzx&pJ;EOGJt|RZVVYpHP$e3Kq~LMd10UfxiWPPG z0mWO0IF2pn2OArg90CJBbpvZmMrf_r#I%ZL$*b-6D|_JjWd32PjW<3UxCxjCy%O4?aYtBH=$0> zq(&m)@kD*e(nCqy7*7%*Ui~afA;bE{iNCT#8YFPF9=(^`X*Bu5I`>APBcI8wN9Isd z`WiimrD~rElY$qxDVB7Dnv!u#a~UqG21=e*(iK9UN9~F2S~O2bs5i#|BU1}pstii{ zI_UBsEJr+eZeGY-4HL~9y{G77OO(a+LG=gFy8u<- zCBy$Zs=A@oh#wIUL0Z;*l5W8+xVBDA*+Ydbag<8R9se-brOqd`!T6UvGm7CTTd&(dp zdD^G+dUMr?m0(38zHg43>>;%RUPfT;!EP(qtWrbC;3j7Y_R&aPXR4$o*bfoDWGZumOiLi`5czzNmR+5TMb;#0e=LoqRX z4<~p!ULU1Bt6Ty41?l8S-ky{f|K`JxQuYo0aK=ldjL`Pcza6R0wOCs;G5yIelOxqg)3It?$@N zV4m++Z6z#wXZWYlJ>=vp4d_-QeI6`OO=Ql!K^7@+L@Rd}CK(i8bo12%`eHWAL}>J% z&RcjnUp^Z^Q0cp+ z*-sS;pzCFT-GYC1IX~EHH4h4Ukddky<$){po5d|J{#iC8_r_9aUvh$5IwkZ$3`#Rr zaa{KO{|aVR3O%C$aCtu?q&x3Lg);h-smR7z}&RIgqX{cn>Ii4;dH;Nbm!x=CDjKc|x@qwM;+01pgU&a&Dk1TJ|vmv+xxoU#unh z&F*N&bVYahpXnXfJH-whw@^AGo&LsNCy=%(-)vi3qn5F{9B>#{?UvXW4 zkYNH@H!tyt0VE2zJ8-Jr&msXnmtSd{d0<*c8S6zscG}IC*Ov#xUuoW>WZcaT7o2dh z|8u0xUO)41K5~Ds=8r12P4A6dOk?2odmF2E-0fzyS9Qg}vm5|w%kpq~(W@}^JO zv$I~J2z-jnuz`^w4)FBIK8c*c6h)>4O5fM#qSf641V5 z*joCw#|tLTVk1dpBJzZ!&+-?N?5)Z8qyN&R| zUF*(@nNB4yHP-5+s z`OlAlC9NFCns8^-pN`)wc#LoL*)Cvt+0% zXRi?T#{f=*?^O*^iZOYAJ9FOBGr$R*kS`eicRHVohUwtNu4_li9dbB;+8UPv=N!TZEGlZn&(wd!{pFN}HN4Of$_{+~1`A})d%?X1}K z+!1KB^mdPhe79M=)PJr=_&P4laKa?9NP$6Q2xuoid=xiR9sOCvhfSj?8`_p4*Z4Db zd7<62ccw?olo)@-*QDnJvp;ilms<8fB4Vknv-E>nKbkT)TA0R=Q9Ua}VW^0)zh~!s zBT?(7xKT0y1|@!W@K46x(&SZvsC|N>3~GOB;@QW$qbU)hA-tF*vVx<00(yhFh^^;v z;r2`c)R|(y_Y$7H%hTzImWat!5G$ert3?%3yF=@r+xMXuAaaDs#daqs9QmBA#FLiv zaSE2VFQ#ZNKp7*sYfJ|PQFHsJhtx|KRoY$spl7VeAtR`PZQJm%9wA`EjUUP(=%t@U z8BrHMN%adLi^mI=9tCVgLNwyC!+{3r&#Kh`pImkexgn-)Ap?%D!+hWrJbL=5kYjUDTeJlQQHTgP`Sg81Sx7OAzEq!TfZN>;dnrpVZdoqXlwAw5cnlds{y(qaa|WrSlQJ( zqc%x>6j!5|I63#W>9}k3?Ch@nmUYc3a_q*W8djZz+{dAOVUA{=`8hTVG?>2<3rV2{ z0@Hgw=Tk8hl}<}(S4_gARg1d*AJDYoujT`4=0G|XPc{o*Y*}Vmx`#TQN9}*{&Tud& zEggs$Z0JXZKO;-n!f|69J3?*6I+_N*&HcXk)Nji+mrUf#;j6_P#)_LDUIfk{P`NKy zp}4<`4JI=rkIc4Ystiti_7NXYa5NOK?;bFXl$#YA+ zlwwdLB|r$xo4hTBCh)TS`W$8~GZaoEH1auK$hXx{dTm>>R&P!--7DQ4N|s{%A^eoaqwLKoX`G+MNuxcB<<1rD&Mtl2>*>SL6d zz@U-Tlf?OrA0vXo%2tu!uDb>`BMa~RFA-Gn1x6R}^BzLKRp5ZWz4u*q=~_$N;PALE z694c`YXk7B!k{i4Kw2_?W9R1{gV*766ba)!Tz2yZi<}q*z-?kCyHH>&W+J6?ZgLa_ET-}kX1 zUFT}Do${d#R5Pc^d3mqG5hH5Gr7)nl6KH*`sf8iM6!)ck+Ihi3$!XiQ6=S zk}`2&=oB4%VO(HlCRsf78FgQnpb2>C+fEbq5 z1vpA=fm{dMvRnE4aeF9Z5RJ^-;iGJ!e-r@Hm+XIcNN?_!enB0zTQk*1U(z;?*G=Z> zFY}UUL4xH?Mr6CkkZzp5q00?Ddg5snssiv3VW;vOE6`LP)Nja1=2FJP>vYbK6}KA8 z_t`Weh$CI2=I!Hz7!GQ0$CNxV({%&!k#U=fKb(#7ZHg4e<+ni1Q`b@)IXIaCx6#bX zKWdAV6L<%S-^?>P1l1H!Uo(Tc6#9P@QA!syRpZIRyyz}-?r`ZZ6QXCtn2>=*mX-nP zQh}F3xOq^sz3M3|Uirk!S^Y?l_qJ@SpW4=+Ip_7^&hYiGxHvprY4q7F)EmaQ_4FKw zMx$f?XZBp)3L!-R^}i%dNwEJk`LR^Gh$w+^YALaf7_(6BYAT=2qEGx&c0mw0HL|j> zOJMhh`NsYzHs$>efeuaiO}J*+(^ZUH6SEu8bV0z9Ig5Od9iMflVkU(YTLzm@Jwg;+ zCy}gWB5-J&_@SsxJ!*hvDKGy1<6NwXr05TaX-IYMz-rW_Q~!5NEx}zZ-qc@g3+PhU z5?qC8rNRMpzCkl*Cm=}p1ug;xT0UOo4$m5op_5r{$gZOxRV7k!!EM1rGWV+Fpo+*p z6uid%klZ1$^Lt<@STtBe0CKN2&SvDgrU>hj-%}OBLzx|MP-eHCvaw1<>S1_%wLYw& z6i3qt7qs`pbzH)v$_AIc5UEdZ>9;Hj0RinNmzLSN4O1dy{9H=b&5%Z1%&ADqL;gU_2kXo2p3u^(X94iPp=A>P-6^3c45lx^amuL;x zKEw?!t!rCXn|3T&RLHJWIgV#{A^InKXmK^#-v$h`}woUb0fqhM+F=4K7<9X9Uzzi*IXww5W|4N+*G=WR$>}Dks z#=$zCmn|qm0PLll_soo!5^|EV zG;}n(%0H-&&OaVeA}UuARH-aG0&+!N7%WOS1V|{9bT^hHR&MirdChu`R|kSjFl%}Gwhz*FSCRq(wNr4nG~I%` zhiIIhCE@DHmZr9lWt>BP7C^LU7}pbZBr`DJrtxwksv8nYwRF)kh_wN7Lz>YsIZ*G# z(M@T^RFl`D&BNYs<|#Ssrjs-U-H(<)I`f8fRlCauGX^YbqnkrHXx`9Zn=~&yq&R%n zVUbE<7lr3V)dacA0N4)FcAYP08sN1$V>qt>NUsl6=*W6!>#~M&e8>YmZU|a(*Q1p- zeLa??rkhuv_W*sTsiJq3de{EbdcwA`cNQsZ1M&Z+8Xy33{d(K)ujo>&!uOfVX?}pP zv4Q7W%Ii>O&h`g~yAC*&<2jUy0#wmqG&C@JVQ!d=*YIK%rtQEtf05IS>ZAhmwpmI* zT6ju$DPw1Qdp0mt!=-IR|M}uQaA9rZpU0plvviRT?qic+SD)O?VdyMcH@Ws~Rq`fz z;qcTp=dR)8kU(WbAHsSpM7CZBqdi^qeIF*IX66Nz@wJ$~G4%L4Uw8Q=9VF<#=qQ0Vz=hr7b|YL+W1! z(;GV8-@PRpG1qHRY5YaBGdV?^#t*p0&OBs28qoI${G|;DmCsadltLG-{gF4`A8eu5 zC~&?z(oeUXm;t3mm^G4Yv0p*(o)=@#U7-SX*cc$L!rQ^`p|9wvLi!3#p>~W{E2@c3 zclMbd)Ep$z(B@N^g*ZSCnFfsQ*3N$zw+{}UpUl)=-7Ljf!69l-y^OD{z4S{1q8W^} zk~}ru<00(E28hLIAGLurqps$BDf+=%xwG4gW>FOiw=jBxBlKzBV%CtrmYfmLbC+OHz+%E-e&C=P>i5dZ5^qq}aBXI|MM8K1LY(Tur z4q@!HAKD3jmXfgFNkHN$T-!sx-*6&a|2~f7iAwNL{$2w*1IWZtOSK+{4BW11*#%oG zq=nL9L5K`BlT)lIEu<7mIXg(+8YRZurc4U4+#u!aZ7UpmBMzJfgr=IgryVMqLwL$p ziV@+`?>*Pe;p(h@w(}fn2a(}gL_xa%HUqz(`>wa6WW zUb4Gbh!^ZG2l$*2=RlY#Dos})?&&r6A2{1oDM9cc=3rjy+Q4l{Q8ZU zWxbeAF&g8;ow$dzobi(V0I1j6kw6UQYS~Zj7P-|KiO5lZ){U9gv9$xB@X9Ris*T1^ zhM%0i<^o-@X)%gS7pq`#XZ=iO6x5kbWNOL@>O=s(UB(&) zLwn7;eMx;aqMd|2%nA1c6P;j)ER=95_~}qsw`=ALudNg%*L9TZ@fk(AWAC?nBSZC% z7^--G+xv^_8+a&0L1ilTO(^gM+c~|^(foN$yX=!w1aQ(OKkLn^5_R1+l4$Ah)k&mEG0Lx+vBrs+sAYnLMYW&q zM7|}rD$8d&^tMMp8XwKr$oVN=U_6!Ykx5VvkyD*FegjBdeiS9phY<$+$C0#pNu%D_ z<~9}I$(r%>yT-!jgQX@KbhjI-eZI@apIiwK@^~ft0Q?M3q?-YJnHk}?}$tfMOS(KRiJWN?P7BcOm3qNx3pc*bQz` zx!ME|aT~?65yM^A?c0$I5<2;7?cSr9AexP#sBB694hj|;_e`B^wDO}ngkiIYsoIO` z5mdPVENZIK8q+wRrSs99UC*5Fv%-*YD?kAv&CN3LIG3|$xtG`x1-AKJdVHhTM^%W| zoNGt^+?C}1p%g}T=*ezxcolRnt^2_)X?w$>@8aXBzU&63V)Vjf@fbfX1bN-@X_5RxD)K z%kri%tMz7GZ`_#rW7JFk;Wg~mYsmzx038N|l*Zw>)R`Q(;E^-7;O^Q1w-_3RwUH$b zKW|<}$>a&Sbbqw%Yxx+3DKgh8hP~_6)q~YMTgr6L9sLZXJQsH}ipX@JN(k~MapMBu!)5H=Qe^N_r$`!+ zW|-=1H|&uM?Yy`gkQ?fN4m}9D~{;!b@OOsw5C{N zy+EvtMY|7#5!d+?FzVoE2N$*Cwdm+Um%_&QxRusMxla9&nmb`>D zd!gp#m0_Whj=i#}abn%ubpAZ(x4eT#;p9hzZf+U z>|^f@QOs8a5cn@*?v4vhoi4rVV> zlsI7Pm3vM%_~ad?c8V5Cg(Y`;vvoevPcsq&G@AKe3> z&KOo2m+LJS$+NrhAG_m)(Q>SII~*MPsMM`wpA6IjkdXn3Ff{ix% zqm%IvMhAA%JQ!jJdW2++SfHYvgFh)z+NS$lQ-y0Z6ayO4cMk3+4@?Q8CX8nb-)YGz zgA=k~^LM9g2}yHX5NLPrTnZjk4`V%j`~v^i6*!(2NSl|!zj^@7&~Ep25@p!o8~WLW zU}}%g|3wT%LN`|u*6L?4`@!|F+)tSqdf#XX{)eE()Cm6mtyQkJ3a`ZY9FD4q4AQ1T z{@)ba-S_TE8~!a+@I|K-5m36-8`TNzo8WbRi>fH1oj12YKv92|)AHgp8m{POhR>DI zcHgBj8wfnr$%cv^3P(_IVl_x+zzf*2D=21J?StkM&XA$Rv_=Zg9RY#DvjsT|`=kj< zuUoofRu%9x5>5xD*WT;+YrF94uLu;E%S2HbiUI*@fMEtV(|``~Fg!+S4I713L%bqf zGPgZ6)e6;eBM1+OL~1C*Wi>gcJTE<2^z%B?451F<)yNF)R5>Cu@M_K)7SA;P>pNWp zopXo#rYP${7W*_G5C`Q2>;w5u%VyZ%b3 z#OP*_K*GWr<#-eqg=36i-t=8KH&m|;5@aaU==Us|ky>&RGdlu%H9Q$Hv5g32$c)r5 zM22ngzaJc8X;DT2)&OruQ!rP}VoVj^F3qi5xH3oIoyP-F^_Nx#ocZ4KDq*bC8N8B2 z4RmY--+0PoXOw9#{}dVtLi#P2cDST5?{Q$8W$!O;@HeCw(&87)BK{5z`TYYGx#@pM!%^N#L0z#KL zR5%#mk~~%YTj>M%K-=Ey=XW9HQ`fjMQorP=76A`fcegno3(#@%;xjwb%><|I2g}>!P=D zM=y+nrbw9v^+?1h%1n;iw~@V>SLE~j*!^c>i_>H7WFN&54|nnGmmoUz6o`@A6_$YT zyoH*SOr>ax+;ujyXuOKU&_LspM|f|Q#6{-8nSaR+tJSy~XB3bKE1;?7?5J_#p?C}*NIzfT$c_T0MWE)Q1Jau^glO2wiFsP9C_&qWz!lBu+P2M2iK-~JiVN8%@0@= zs6lM7$doZmr;@`PWINlv_HyLFo>#<||1lZhkW}UdMb;)njx7pOerM zg}b|!SwuBYXt*m^o#?(q@XO$_pJ095G%T*GBiY?A{N}dkOSu+kd@z<-Uv*lB-}jc( z!;1q^(s$|r9M(!Mh(kxyb|^fcKKM`*{f)a6-H{5M=(qJsdA7E>Pz2XZt9X#H`HRU7 z)WO~Gy7RKPT`o;S@<>i%_~e)3x4-5}*t$_*&}+2h&c6?(Yvaj1FReAqJWUlQ8mQ=7 z_fd5#Ir1vK3)lztQtI!um;;>jFHl}y8!likdMkHw2#2$Exy6$Iq%4kTv{PGc5pSa4<>xD8dqXv7iZpk}Z? zwZBh+n819|mS}PAT@qT$l7VVgAJBx>9mhS#p_M5`sYtlK`bx}61@6+!Orz@KN~^MJfbuS8WQS;%PQguMg;r78g{U=tdsjNSng!5lLUvVx%EDccm}}VkIcbYwMj4+YXO&a#lm|J^v{1s?O?hfyjUD>aTIjQ7Irk_S zi;X4{?gkaJb+q~Skt?tLjYuyj<{8uEYgUQxz!Gh0O1Fy%bO&m$e#Oh3JW?1q+_DaM z*%X5sqTJXk83}wE%vS6xaq|dq{rLRsbSni{^5gk`@utVK%WyLo3UiBV_)TM$8^p+u7@V@{NSnFdq-GkhAgD>mAR5z zNT0hiv%lQ7TzwaTWa>r|4xoplb*D&mh>lRKHmhADbl^!IV2?R2`N$=v*ZcbN@p*bT zUsUq2V!KT|F$8+k)EE?t91vYkM(8K7>Q_*_p?VfQ^3ui{W(H8QkKeySM_c>Pgk-$| zz!^o?sQf^ISnw90US@b zf}*h`-2ItT#vAPy8xbb!YhQ(q{%7&nH`O7)l$|DeBuXO4q@1CFmVBfPS+Wa=xW^gn z_BlkCT_dRUJc>^sp7P5l1dbj#Fht@5hTCOU^yMjTr_ef)g~w@F%_?8c^D-E1CR1Rh zE~SnbTV&7*ZLdpgtm`1SbzwiJnt-G+J@e+TbS)w-m{>PR)tvEg=C~JD|7j^K#9&PK z8%w*1?B}6wr6WBBhn3l+`wBiks@3+#r~=U8gM&`6a1+y&)o76~j%r&$J0uJsH_2zU zM|6bVQ2aIGB;(F{hbnyI__A*&t?#^c3XhluW&Abi80P7mQ6Nko3{I=m4kX1;AQ)rd zp%=GTb|T7bHC91i%YAlvG2n=lcMnP{C1J+jhc>Ay+#@QlVv)?AB{_-4O0FoX1%4!+ zz2_Dm*uN0#=_sTmGLui(Uf+p1=ma?L^P+*j zy(1l@o`Km-$`T3T_6j_-QXI`++|D~ty|dqVN~?@Y>=Z^VJZnou+#?SE5(j;2E~h~HeR(uV1AxqBCWJi)^2s5CS$Xt zso7X)!Rm?M*kA0h@eFkX0`JxLoWrQ?1*U7bla@g{1bs^%0C)}a(0iM7b>gx)?CZ#h zMdEmu28(TZwM^QS|LK~;GJvuK?6m0EJw|^Zlfhw?)3BXZ9ot{&M*kXVN$AjALBm?zp^522` z9K<-7YbK1(>z?!-sW>+@PGYl$A)#nP1im_ zH$(LXqRB`4c{j09`K@W4>q;~fD5#$355RK`3(1J<&fOhHXG_qhWy#-mpJVt6nYQdmWr^ZfYC4K2rQQ+0OmY=+Gg#uHzBQ4{f5^3`c~(Vfw3)O zd)8+gsf;hGUH^l%qC-0L0oWe)VFF+Av!p7M{ng5!jZ$B_f=J}O2@6R0SMV+SdLkCz z)v3i|#NP8q^M>R^Nj04gvX~Ow`uaK!Fe@E)t>OI+knv(WO6wm`n4FKnZv`4BEb4p_ z^yIEcuW&=Ejub3m1^u>zoB}|KZP7T0Scb=Xhk3!qmVi#2+CVMo6R{y~I!U zh2p}f<}e1Z`Ef?cf>@#Sak;#z$`~TTxud?uaDtZh(?8%x)Tll3!_^HPNSy?)O2sDp z!w*yJH2`t0V%E@61fN&gUx5>1pb^Ci!~Z zGdA8U+O605QYIglHm1J>(<0FMVCuFlhZ_{*CLk5ubG{wGq6oT5Ky?TsZ&N;t{D}6O zd5BW5qpM`k<9#-;4-`^aPhzPjb-IK{zX90o_B89AVM`Un0~6OKJx#$56>~xT#enLn zg(j&w9!xeS?FDke6yLL+h0TeM+vozT1g4`?8=(~>z)DTnpy4^V&OzXgg6k9R^m!Xl z%Mc2(X}x+N!|1Z_cdrnoXrpe+i9o2@fmg7{D@<1!oQ-Ywc<P+6lFuq=JWax4Z<=Tt; z{NXkSDu7TAk5?=nuLM|j&IAZoZLH$-fvb9u$ibN`q&Sd~76wA);Dc>W>d8qP`4;d^VAXnFaW*#(7%0PyxYhupZc%M zCD6n&&iBpElk{~Vla5AU9`59k?ZzkfEX~<#J$o3 z`NA77ERx`2*<>&Yu>9#~F-T-85%G4<0^^cDe-Z2N5|C&L)zk4MA z>9QR_<|CrLDIyfVkZenw61OEW)ZfQ$b=ocqp#7PvP;9&=;-PPmEp8ag@-mI01n$Rt7hnJZgBUb6s-i1TJ{Fp(3r6`hYr zx?&!n_fB=V)+Ph&TTQq{BNv9O+tzb#pAurbguNkj0Ut}ZbX*-ZY~)uKAlG1|M~vtj zgne{6G9%i$XRseQXnZ&6^*E6jEHlp{5Ath%fuqwc5q%Z2A;&ZzY5#%37_@J|E`s%k z8-JhJi8Z?9h1BO^7QaCX1oJS4^@!07uCMq88`6DOUjUOR678#NtjAQv;NT_aZAG>w^+g=5op%$8QX1gzbd(~Jrn1yWs`LN|*BRw?b2n8_lZ8Rr{O99u@G8foCF79^g z4}-j~x{prfsdQ;Fl8?4qq?KC%p>KY(FGT^dJfx{N{2{f-vDfDlK7m>&Zf>v;5n!X+4j(&(W?v9B-8?}w;D30ML6RWs-BDl*ztNaPOUv$$1r0e4l@O0+e z%ANA4JeE^IW{9UbQCC=~phmk3t~|DP5mP02Fa`Eu#1YGhtziFtf+!ZZn?NPz4%-GZ zcvbMPQ?0m4K;qs?aivfB8VLM{!4JKlO~jx*xollGH;~u%v1`$#&6zlJq$AtmvII2a4R>-_cMnihp z#<33V{+()gFMWa6!GZV?T!%z2qEOKJz8F#zM3D0jyRc=pZ)(nr=+#sz_RZFf(|KA$ z`=L7lQnc1-{T?zXgA~VW3C>cr<1>n^PTF;Iyic13dC932P|;qZ6C`*bKQp2w7N8Zm ztZ()zC#r6;LlapLSi#&!Ew&Y`DoFIM?j(Q6o`?qfbl^|kb;NpDd@Cw+c`{d7U&kyF zh8rY`>V-csM=G?)8tOH)rfE-w9LXRed^^DJ33G_(qUQnD{?XPNsileF0!4zy{ihPS zebf|rtum!oSF2CsqKmMg37-LZMHIxa%v8Y@TLnx~P~MzI#_s8;t}8Oh)yp%kz`7kf z2U0@TLgd-K1(2+@E&egc8Z$xzQJ|m~Vj#Q?Dzh%k3_K%6cda`khSD>7&*HJZdjH6N z`f|IH(EXqliM3MX3#R#D+rHacpq3)PUOrzI(TyYLR%55lfz`P9BKec!`mw4$$SVXw z(w{O#c%80VQWHiEmV()MJf5f4kZCPt^jt(cnyPB@r~cGtEE2{INJkDT;9|2S#mt6E zWk9EAm*IhJYBqva&u6lKmKyZ@y%vUtOVmUU>I1W|_L;p|2(%Qg7VDH)YgNAwybi&} z-Yge^W)64Xk>TvYi*`P1!Y3}I0yt1UsFbCusHn8CN7^)_gCeZvoY0}Q;1y4#{Cm*- z6|w_9O-k7mKni7|ypy~()wJxRmcgt~u7L`3Ujdqeo%86M>8NPrUa(VwH!QiE_57OE=nrS@4{mSRweJ67=fvRpnA0UI~Y{KY67a z##G=&15WvEV9a)@Z95#$n@V$!U=c`?8ES@KlQ|dT?AVAwsS$g1mYsejVzG^{)S zGr@ss$p#qC9)OY?1%}oFZ*KP%TL2ktSVaob7B#W%DE#feoz#}KjgaKQgH)N{k~P`X z7sFYmgvp$Y(RC11zX~IWM8m1Q2}lR)CUZ}5_m>^531N$V|EXp;7q-VW?*Ew0mdZ>A zKu(Jxb7iTDp3Q1WLPfXgKsT;>Gp+knF6!PKJP+K?qYGM@p?Syg{UFknE41mS>og)K zB*k}-na{)*XGy-ITIQj~M3JFH7tX(#lDkL;Zcuw|@Jxo-3YI1Ui^sDOemC4NyT8WI zn|Y<4eWutv=REJ`#)TFtLQDQ9pRCY0JqHDU7l}XDVqk3V_++Y&Ijx(#@qeME2v0z>@sAA!NW9ht~LiG|Q;J)#{gA7JT zEG9AmbKUe$yJ<(97Qr8<#Zc9&8|H}d~JqC~rXC2s3A z36zXxSKmj7Hw+nPF=5?e(wRQeE+<dIggwVYP4~fl|N|On|b+bLu4*L4+-_wpGm1f$NFytpQyI2 zikWEK36>W=!QULuJI}~L-VYEoG_M-QCqt9CJ2-PD>iebGE00W)TK*m0AyN)>AnaL7 z4odIimhN_AE!8ubX3z^N^FG^($G`Xw5^a)LxJ>j zDBx6va}y%|KQ4x^we!F$TY+hu?j?psg#mI<3MDKZ8u0A20-Y-K5xv#nMX4B6&RGT% z#P5mUBQHP*h`z==#SUvHLp>lOb2Sno*mJ0xM>+8RRZ~!$&)VxUlAULD$lJY1>c}2w zTz1!78Gh_wx>%|OB3Yk1aSK?`^|RM~)W6V;QP*(L$Ls?(WY_bzkGC`SR8k-5_l4O~ zfeOK>#CkcgnyKC#&N3Ck!1Bj1*gw3n4!bA<~)h(%|fq=C#AD@a8z0&^Q zn8IVd@g&qo2w|@uyDg$rRE40cZ$JtU;jVJKxJxDau{CH{LEi~-tx^XQTQcokif`dB zs8E2FK$~91oqooM>wK=s|Ba|1WNU_}fazp!KaHde|b5yTBnPFqTdI%*iv8;EPZU3Xm*Pj&{L zKmijVaVYD12{LYEFijkblXKBeD7y5K`=8bt?RSs{cGG-Zo`>BM3WR^oYbhSBwnOSS z=}Dkpumbj-b=q2cPd8L=NZCiZRM>KkX%{J<(x&o5InaG3yic4~3I{mhs`#>z2=|xN zU<_U%I#CGfcDpfvke1nj;L;mNF@;saLb-0|*hKh0p$QO1{J{+3axhUu+&#y?RP-9a z(K0Ao9WqlF0*V}8M%D2%N&%TPi_6FmFnUwnL(Wn#bl-aL8=L6f>x;NXM=bdzNgraN z?MRvSCPY0#0_LO!uI9Pf6&oMm(v44V-C95fl@3l96+8cNt%Jj8@$9+`UCb0<1NBQH z*y+}|_O<%-9yi;(D*%6OW2E{$1a8a1eQlsR<36v$+h{xe;s2RQCUHY;c#gWD+T3mHI({bH#tqa!(uA@Zeaz?fQsRo6w4?=<5$a*`ZxneN zO7ni2Z?8Tgn}WUEwbbe&wX%P-k5hEK*bdobGUjoB{umu#pjPGpbh=oitVxrsl8hLN%LhfR%}C-&I0i*+ z841NgMMGlg8kYRtUqf&E2qUfYbaHPt%U=qpE$1OX+T=M5db+^>)lrTJ%TStcv2|aP z0)=}TU)y8`|2FwQT+!BIjUhxrSm?a>S#X;DxGId%WRw>(0X~`WbnKF;BiCp89R{SW%mHiY&B zYjA{H8Ltfu27ND?;5_aLqpFx(bj`1krK1)86etp#jCVe~H-*NNWt46`KcAvj z9Xj6L74KO$y@1;^(h?-AY&_5ewapj$9!dL(4kYF12)4htZZpG`M24Z65SX`ARhzMI z3YFxmzaT5501Vt+p?+}oTHvGCc@)g_fMpScPCScc^EV2c=VpiCRsg?WDNsPWwA9K2!oxy8k zQ2J9NOhW?er#C|zcUqIZWbX-h6)3=~{pA9x6L^yzXC?{wPg(K~hX+$kh@QwnUJpEruOZ8Mxu*vT@N*YJ@VSeJj1sT@c}N>X&nNGBQwN;z}Udn0zZKzlM#6omKgkuqDL-r`wL(+2}Ho{=FHuD00oo%WE$Rg5(1^ z7`anuVenEZy^Tnh11cmr;co`vPmEvZPWM|Pi1csc?-MZ4?b{C8VN9JxYZ>B@rW2_d zSP>!o`duP}hmhT~{)SykGv+cqj~+qO|@+qNt1uK#(@ zy(hZkJ-6dV_m}%=jXf7)?ioAQZ|>(AF@|?V_2+?T2&S9Jk@K6zh4bl3Gjal=-cmL? zSM~K=b>f9#e(0Y)H;J+lISNGr+&%3UwC~`s&b5lNAfs2pjBJ}@KJ~n*BDjyE;g}RB`ggP!>{a!?aj|Pga#HyP+gUufHz^!O}E%vC8^! za6qrXInDZ|XeC&zW--#BYYqaLhA#OLngKD&v#_+MPdZ~3{4*WVq^n1LA^5k7b}Bgk z@g_vDfmNNxz%0BHsspgJP~gCqiRG(;{8~~R9>mpCAOBf){ZLg`UK#QGUXTo_G&>Q( zDc-nBr3I{CZ$dO7_A>2Heh>m4nm2S>8UlhfOoVoPSt=GuE~b8qoA& zZAbVL!!2qBd;>}P31mR)lhNGT+AYTR2>Hjj^l)Qg(hm)QQkF!gV|soh1K%Lg+o zzs$1@DKZFKX>&T08+F&2EkUcQ zUJs|QbFZXRTp(HA44Xb>k+x%{yU62S2A-~VX4X#h$R6w&4X-{*u67sC*~e8U7VDvJ zU4E0J#W{3gkYuXOk$)4I>(`PVVq}WR5L2>;HZvo=sto5;+sC8^awBgmJ*9h1B2U`7 z#(eI$Lz9HY6%9})TSE=#ca|w&Jl_jt?A^OlCIdnXO9U2IlN+J#yIe|9!n-vf?q=e| z3p0p28$70G9_K%Zu4OpK1H|>T9Fmu&CL(oL;;Ly?hfX}B7v~#r!BB>6X{4-f)zuWM zPTeED?dM!5%E*;Un*QJcT7)CLRW+K=ysCSP$3edCEjEcmtF`z{CM;-Xd(8vEnl=A| zL@?3p!Gkr>`E%bJ z!uI}7S;eWa|BN*M19r59*6A*Tk0`zp2$4Qhk7zl10KJDU=mN?EJxG$@bxWL*KdEjh zB1%4WHeAq7AEZShqg*-)s$1rpr{Y=qp}Jda^hTfR#B5=mfi!d>LeRu6KX~5-Ic>e; zG5B4kE^r~=hgja3+ws*K(T6kgVdpQvc5*KzoBOJeBG%8zxc>bUOhb_$Kb)dg!9+ng*RX8{47nDa1(`xB8Xz+YCY8621WG$^v_6#%4E29yUZZk}R%D(wxU?HM z)CvXEDw$u{p*U+7A%V0_s-1M~9!r2fd{I}h{FUj6!4hX6arNHb72slB{Zet=-`E;i zLNc%hc%NtfIDSN;Y0#67S=Kn>`4j9Dd!~`$D|;94e+C#95(dCYT1V5#BA|wMmlrj+ zE7bVD;niiB`8x+qYJ!}X&&30!*(S$I3P;W!?O72d=h4lFli_K}ZSGkpN@8aecetN$ zaJ|bPjNlYRVbW`)RhAS0u8ES$g&3czwR7Zm2Nwg5SZgWv{B2-YtBm-NH>*KsQ*cK#4_`EdZE9p0mTs7ZZwpFi&1W+qa#YhvO) ztwpMRWNk1`y1lg_BGI;obY>u~j zHj$%4TUP`_*_ElXXBP59o*iX`y7%};%J|4x?2n-A|W&-W*z^OgS>i=3TJ~r zDN9AW@)+2Nk8v9RrDp}a(A%p0UXjUn;G>7Vx9GmX+~S82dV|oOlTtz4RtqDC*BT41 zVqkM;@6bDr_Ee2@yjUT1LUNo%)#xiP*m)7ZEiV4COf*UKO#L#2G(#%r{qq+u20_n_ zC+=NYHe%j+!XvAl^SZBI;Du@0i+CM*(u40F*b9pDRJXQI&HzXaAaIvn)z1Os5=TRE zGP8QMEZKSpam6+PWKj&D6z9Jh>f9%_aDFsoISagc;F=G&VgH*TQ4Oj#WTfg_)u%;N zDbhavF+xkkxM5KDg(0(tGye6R=V`x9z_iLNVIJu^LK%m;n^h+&O0t*&@M|EM2Ko)^ z*1%Pvg1oPphFD(%L@T8x5OE3$itCeu;}V0LI5P(rT!8+B zHpG@K2vYy+b^@krmpu{fjINs7hRm6QEIwEa@$pG&s!<#p4oD6ixZ6@Wdd+sJ_@b3# z#-;qBYj%Dyp>)r~s@A0PFwLl;oc?ea?jZuYo5EOoRVeVZ9CK$k&~L8fawG53%;L8U zbAvx$vJtT_qxJQpt2q35>yfreBrv;I86qUbkF}08KA)o!{HYGqh(QMRI-2~pCzasY z5FygaIlr?Bm~g-T3;?yJhw{(`P?1yQgc%|I>U^E@M}+i}U8HfCy>U=PpCcL~ zV*N}zNl#tchg87D2L4n0j@zgg_xpr~is_CJ-5U>9b&#~Bb8PE)pI766@0fbU+99aS zc^PA+e^joO-AOs(&b5kDzR{roI8Ujo(zm|D*J;A=XIbQg9K*YpaFM*jTrg-|kFU|5 zFrNa^=W&+j)iVlQPUP>TN|yx)vBm7s_{lsUFq-@n*X4|(TEUSV<;{c@XB+fpcYrH; z;hIg&j9HJC9}EL2!B&2{2=}t`{R0`PP(q$$^mbyu9jaP!r}=$A1a=&YAI3Zgq+YV= z0#IQL_%t$^rr>WIbI-ua6Vl0to9bnJXAE?w(c~JrpO=m2nIL9}-zw8CF*eehqO7Sa z$QwKKy+OJ3{z}uAyb?bHi3x@rTF(!|Q{aOhl1QUH>AeO;O*7!&QFuTi@RO4Sb%R`x zDl~mrzl~oseHBo84MS(Ehs;8=DV{HLI7ffr9m-7x?0K|Y-%ca97^Fm3Q>b%U#|B{K z_HgIUQ$_M-$Fcfbm_~{z9q@A`rdU>&{E-=oXm}`GM0RF}{L>;X^9TeR+exzLUYKS7k4^M93+lgos{C)CTHj;K7)MMvR)joc9G96!c z^7Li4o}49eukzU+>HYUyY4B)3rB+#bJEjd73O+3+Zg<~WD}z$*BP%95R}DNLmBcvf z8VNn{guy0Hc--=AE;C5$#&lg3VbmrYIo8aRzydY^rR+O<&6829Su1(WGR!Xnc+wqV z)-z?gWGe*0*DFz3UcKLjdb15Kw}G0)t^Hn0VJxPO*kx^fDfg^r?vimiyb}C&?3;YAL#POcPfo^q~M2rMJF(K&F zsX)ObNvdI%wvE10UP?YE|$+FV9b+!bT6MVj>Goa4f}5z zu2JTv_odma?BcEJp^{|F>RNXtWyPt@KH~r;0y}3*ej3E&J}nB!8}Iy{n3*f-%dKpa zio_K{h`w+U0z3*AHjNP&H)8iX~nLx7KtUNhaD zrcvo;-hD*d%7n}^eUhI~JkV(oaplt>*ys#FDo_LPH^Vi4CO$q5XeRct#qZx9k#3Kr z4nI{Ef}+EWC-2ncjX0k$wAgiNG;HDJx*!HR>lRCtP@hMSCQ$N!d#R$f)kz#z@oG3n z?o|gmez+5(_s#CjuL3OsW1^Krm#vq0_~7r7!_55cecU=g+*Lb%CcnFpJ#iz0jg%+! zuFoxDH?fhZYU`OM?xsIBm7(68D%oO+HiB~77L9dF=%Y)Z=C-cm*seB-(`gNsj6<%C{ z>^4mLIqZG*x18nS{hOwiJ6quZ|y< z6DJc~PybF@JP<1HSmIzyyB!=zArQSPumWWxCL7?PL4=*DcbV+Y)Ba5k8drf;*9d-1 z<`)sk>zu+&9RrISs_c%*WT2j7KARw{g>=*WWpvH^@WHy6AotGJ!ik59U#@J z5N2DPazb+?1nAdCS0`#PGXhJR27;J7Je(er2l^uX-Pav%iIfk2R2XiIqhs88b`Nb8 z7RbN5VVL9gQa(cM)0N*->=KW+j%Ora;Vy%&NH0;M!%(@074uST+;puXb%VOtVY^}1 zUOx}w=$m3|1S4?g>0*{;1`D1XbyORI=(7*@pHlkXBGJM=%J@~e`_g_N?m{*?dDq}meW`Es1thvi+M%QgqUeA>*T` z$Rjp#&Q>B>4$*UWT}J`L_HqM-KV@_-wmu19 zaDUs-GNMQJiwDPDIxf`s3^ECc(=>zyJH*o4>QIW+K@5o%5@j(WZ*4y27MfmT6j@0U z8J?~H84ha>INX%Q=CVtQ;%CFj5$!rn#ZRZ;rH2Q=I8rUWIl0YQA+^RDR}H2N%D>y@ z;Qo*%@QgpT>P@HPyCCkI^_UZ=n+z|{ln*_|qhgg7>9G|3>XeW)cAydh`=h2kH!P#B zbW;_(h2jOs)-h5zFZQf--dq^(6ZC~!fiKc=hw5L;>}o24VkQt_v@egQh}1ZKElPWR z-?X=$&tkxd=*?=1`0G-oRo{pE3-74dFO(u(769(lo^5N0dEL&c-YjiOf$Rn%=1jAh zm&;2*j1f-<){L%R+H{1RwfwQGuOt_Ex59EO9LgDP-gS6rWkH9$5}2XI&y*2=Bbsqy zr0r{HfLVCIwb4tHoZfcL=Rf$@NlRPYbxFd3#J(g?_V|jK)C0Z!hyoAX2bl`}l%Lad z`g;7U3@L*5$wsB?1C>4M)>#KymYy^c{<+<6idUORR|U4Tpu@`mTbqipya+@yH(wT~=h988DOLPVMUT(ndWy*OTM)&7HelG8K$P zSON5Bc!g6<>pC?82Rgc_A@!e?p0!&J87l-dkN8IT%>ljo;eV2(rmr%II=mMM=D!0- zyx+)Inycq}%f22U-eqO4JY#HAf?v#WO7qUfzU2a&a4 zFhVnukXJ&_k3twa?e?4<^v^$B8~kSN^{cNJ0FEvhi-4pOZIqoSLH7_`mMf{h$oO9$H3feu|9SC|RYT!ZtPcQHojXJV{CbtQ97fTW@W7;jo8dktlUwTO{ zI<^Sksl)I^m8OA&^LJVsj!bO? zdeQvS?Y+^61;+{~4NM;th=rRd=iEG2GM3n+3dn&)Bw<2qOxV45=ai#=(C3!vC|71S zq{l-H`aExqLwQk`?xBIOI=_5`9!apXue-*|c#2~QyM5RWfs+87{rA&FP#FJg&Ut%L z{?!L`SoxsV+Y0HKtXBa%X>l-3pyf7zh*%H=uW%bt^+)b ziLJ4dv!jWD4gBB8&d?H`g@cojk?^00hlfGT!rIxykwMJbz}ZB^#K_Lr6 zV-izuZ&av+i?!Az+j7(wfBM^3T+nNo%{)X(yIuy9Djm3LY~?Xfp+JGDV}(uT5C-ay z$ai_xB6EddYT`8lbKtr2*MQiJSn^?K?#bLC^rZ5f_q78%KHCEfx}bX5yi`KEiKU?k zDxusUpz9=OMcb3=G;>U?arf(Is5-+e42_edj?<2rv$Z7EB@2pz^#?D>x?g}9|$ zsBe!cN1^51Lqqdh*+}I|dt*HMeOsLgDZ4dH(Xg81L7J!qoEvP~fZB!S6yc=2_{!WC z+`tR>8H_x5dDu8;xVBF*SUeO?YOE+;lD;Vd)d7tj zP#FJpt8{slFuA!0gh8|oSenyK{)BAZzM6oDyXmXsFAy&Q6R>hoq>wg0Xzqa4)FnoC zBsbqRn;yU@{>*kp6Q(c7YtN!|J&Hf-RR({-YP=pg(kFjBak2qOI?I(!<(tW?8v5-K zFFW)r`Y+sBpwiDMXfd_HD8+P+D*K4dcpmF=yuq-yT5$Z*r0r4N8V*{0D9wnEaWQ^Ppsx2m&|dp5aDgUx2;lR*zx!e*E*j%QJLp= zbB&_*AzPjgpg~i05JZM6grLU^?>+f0FxZkU2gl9@ZI1U`GzN!Z0`s-L2eD&*Y*iI0(#OsB?T5ti>^V z>e*ag@5_BLR7X9Z2kZ^v1s!aMXr49OjgY-BZK2Z@wX=h3SGDAbB)G`)m@L+~^f;Aw zfOk5uoa<2r@9I1=^u{u%BVn33K^-L1_J=^?glr~|v^1&HSQ4+rd2ttB(<2q*tdbQe zt#^_NrWTEIeT8HooPWZVB<9_uCyhv_pRAK9Jte)#Lv$L3nP&vHsIlrNOSX)QiSwOs zU(RBZ9fT*jQ}HKNm{)vBG%c<8YWaLzO?ObwV4JT4=%7yLw2Gr)q=Ww0c)R~N&yA7< zkyGj1M|Vz}J?`u7A%gvW7ns7Hs-zL`W4akV+SdkzI|VQ;@B!X^^^-3 z(|7ixul-*Gq?yY%I#62of$yO#R>w&i=WjcHD+mY72zG=ku}orX;cF*65;F~1-d03y zqI(8ScqyEcc$dO6D+J=T*BNQFscCWSe)D7oJ?zSMSHsA>&k>2XNOl_RGB9IF-PNr- zuUDI#HsW#5@!ys&D>uh4{>_qq55ffJnBNIh&AqFJ|Wk&;B zCwl`&6I&yXf5GQJF6855kTo&3F!*0S67bJ$e>uj^`p={p6iuA$TpW!|oc@8{-`V|h zU?gPWWciP?VoZNS_J4)Ugv^ZpIm+@k)-GnZYnZ}vHcCX{t5ri1K?<9q-5f( z#Q+cyV^B76cV>{ZF)%X`*7ZnSu`*Jvmy+>{DxRTKW{RsBCT#<=NgNV#ZUnaU3{!;Ekp<5C! zt;)kpfVwUY&PELkXhy_t(VjguYckdlPR$39VKVo=069Q)4`p69GGVY%>1&Mg1?=W@ z#l`zmuwkbo?3s^aWBDZr|9m9WZN*l_v0HxCuBq zNjBAkSLA4mA84M z*i(nPKOn3mfs7Y2Oe_dWF^2p>u-_HjW&5hWPw+SOs92qVk?V>b<$3p$$rp9qAb++p zruGHF!`=_WPish2r zDy)zJKbEJrlg@m?!P4|SJNQ=dzNJ2r>yRdE+##fpu`z~V9W^C_I61FRq3qKScqKQw zS61DYPJRnem6H`H&&_m6vTi+f==Yh0rWg>=#OWN)#A*R{Vr4GQOH zB>_wRLCIt`+g9@NM8xg5w8J3@*$-9pI300;H|})aRw)* zM&OvQ+l`>rlaS!B;aAFl^=sagxRqZwOx?OAS|ZePtI87lB8A<-Bhtxtw$tS)kdFd6 z0S8ZlDtF6W4tY~%{(O`GG5hXip0Pv>F$SR%hQLyO9{xLwK6OGAO5zm*{IhRRERGm5 zXFj|7X7YF(k)dNy<%dc%4$7CRG4o*#Fm^ox%Qnk;NgnX;En=Nsy&=jP1F`TfHHgnj zS7*2EB?g!pYWp@uewSGJ7F35_Kh*Ty-K^`?R#Kz(pd?DkD;6M5q}i*K<%p5DVIO&^ zWI}aalTx=PmpLmj6)8Q?*$%Y>RjP&6joI-lT&N8~+UKDP%J^`#yAsebiv!`Ia#gQ2 z$1NnJE!W3Zmj3l-+X#R9qbx4hkJk~KNRQ8Pf;2dGXC`I|d@&=23 zNEC%_c{!U{*c|9e7F80{y;rm_e@uQsM=TZSs8%Xg6&(0K>x{47PbOM;6@@1IYmbk7 z+y%-d`y5AOTv~Si9-V-ev%m=i7$|qu=?XXN+#_^>s+1o~xC*iuN0+(9?m>tIC#3`) ztK+3%OD<>e1n?wRh@P#ENH}Duw2oc8aOR|BzcnMxO2}9uy2O!OYEMH%9p&{Gx)~F5m0e$^ia|`jfLC`G_Uhx}$`FIIkxw7(H_D9w8gBRKGn_oP>Uh z`tISp6t(7Bj-8m^6%2E&9S$ z_EW0ALw{n~TUFeH+IJRJeb9w^XBHI4#w}18#82n0oobsyt2e|g4i`lnU}&~&OGLlo zWCWN0IPZOMvQZ!rR!Z5(N+qw)b}(Lg#?f!D2I_IomtH~tc9fyn z#pH&Z&D6UJ0`U_LJ16VEw?Wky+b*kaAmZHX5Fl%7lt}nMO`=~oyP5E%oUp;Uy=1+i z$-=J2`Kib`b`ACR!=)_>h_41k*rQ70g5mqpDP)z2^_u3aZ_tO*oe=6eJQ6@F*9GwZ z(~5+796-Ya!ZQpXHScp1VPGUX^}#dgn`m9@Ke8*at_9`6?%glQ`Q1KmidlNLY<2W_ zRVtygfdI@TTQPxX-^iv*-llAtkN_w-PmdU#!pys}CaHj51!)`70?L^>w3zEz449PA zJI*%n3X7&%wnLX;w5{Je2Z>{6a8w-nY0)nTXosw>?;oHE-iU20@)cR8? z__b}^U97pS7>sJr!F#lrJoBATQ@E0*TfS=tiXd#q5UiMQrHVW&EV^=j7G$qu^r&pX z&s>}YPqMf&T2bkQ+}^6yf>Gv{m`C^-%3}{@CNAda1ff|M(ro3!z?H%T&o8#;%l!Mb zvFGxuYM>0m1T%g1haJ-ruWZRMA}vW=)t4A;Z~Wh;ND&JYNIhMoxYxgcEAewbIK%i3 zj6LBTpK2o{TDFi-J5;--2o^&Ir&=N!c9~?6-m+CPcjKRFYXF?6c%^}+&wDuMIfgF# zw%hnxP!`$zG<-DvdPGeF-q5{qIL4kA!nuiv^k%h{1*qn{rjw`~y1{}qTfUS+q{x*m z??y*P66-uZyA{wV4Qr6jDmm;BYK2{16Wnovbb)bw$)8rsWANb=X)Z;H58aSek>_;^ zpB4_d*qSW{Gt1d3iFyGwhedb-I2!50G;*A<=3KzP zAb4P+00Zt{+Z0~YOmc&+jn_~q#Leee1Ca>tV1pPVC~oeiCXRClj?+%}Pn4zEz(;rp zC##!zEKCgElX2D$ln^RiU{Kz0X64n~{0NjHPqI$PGB9-IW-8qmx`o(w=aPqx%Dx#+ zhdpCYDR5SJVu7F`WwCTc;dA^YgU8)$W0uG+JEy67GK?T%?tviZeD~w67e<>#DEUMk zbz4AfAwLT8P07mvh_xRYA8XDPu|puq*qTh4Slf5WbRGbR`=b+`Guh&m(-nJ)wT`-B zUL}`l78_bTXND-bc_cofIy{?i_RZQCnXrewhTsRhR;oUQop$s4OhI)EBqp2gWGeSV zyW_k*k1RbO&n-=#jQ)=>#9_8H1oa2Acj2$iNzzVnLB%9rIOa$&;c0&ftn57Yw`DGR z$zv`aR#|6aCamSM$=Ap3zA?0pZwqHD6A-ZYhP~|(%msuzic^0qraK$%g(1>lvbOG` zw9i&z`LHyrld({0tJVkkHqcL~!dr~N#%Nc)w(l*~R7hoXJe^Hft|-jkr5$-LMSMU6 z`b#(~$I#l%aUYu^o!a>`jvR({bc^Sf$MDgZcN1{1OSEqztp`moiC~)OazNNvp@84! zK%^HjADafm`A?rH+mYX9_QztQ6#CKxrdjMT+jw;?%Is)ToC6YV7}#lStV*{B*)|_+ zfEHot4_Myin`w(>V#N>gqxSf=#^^1uIKc{$nlA)qyX$+j+d|7|2wDZLJ!f#W)bh;F zQ1COTh?OM6kat(LcK7$<*VkCoW#zQSn-DaFKPEI~52-D0YI&Hp&Yg9m!|7d)TaHBh zXg=#<-p2ht113f_7ae{bh_z6>tEU^(b*c(-D*BJOOgnB*hjTVJR%Gp+FiTemvlhyK z!0&kZ*`^UzMGB4v`+dyCyRng!){uVk6#~E{-i%{Yq!%%L{Oko06kxf`1K2V4LML6a zt8Lhk-*BX^%m2#O{diP&?uLxV9n+c9jP&ST1P^PJ>p~10&1+jr9ISs?foH`Fbs`uI zle{yN(~xInW3}#kE~X2^bF@TFk!9WG8i$K~iYH4SB%8tl*V1GJ+epHY^Cn@-Qk`1> zvkr1)I1Jms&#Ev8M(3B&fRk|9Rm~%kQ92v-Zb9=W&`o#^YRU6mo9^;G0I9I7@*65; zfD>_;!whA!%ks0!0{2FKt-2=9a*`GE1U`Z?q+iS@mU z4VlNinpB&8N*}E(cBm_Ah~U^z4)AACF(>!sGXNZ!%dI}!t^8Ebfda;QgF!Tg$;yZ9|<(>p;M zRPrnI$S&;3<~QT9gYkTbz&OD#MY6fVc^{j;yR20*e9nu9N}iGJg(g$ZDbovT(gpmy z*dWi59Xxf)I=*Ji9$K)ty20t(X?tY+n{2Nsx}{Q8zLdhY?F1JW#`~i`&GeLk7PshL zBzyed{V#fl1L=ayhY&(2a(_F3q=%TxNM*_LZS4Gx`O(b}j9Gz%<;||hXU*~Yw{GtSG9a>(M_4PfF+b2Fs8IFnHyd2EjdIv zL@j>C;=bUuVJ74+5uLmaJ|PmWk>`1`7yigAsb`jHh_JJ9B$jLp~_#qQU(}|I>j(O_ol5A=qWMQ%otB$>Ch?rE7ma|v z9UDo%^g}F`=(X}d__x)2B!r)RMzw5I=Tm(=yPcnu!klMKP;9ePiNH&SA~ftg<|VdT+GjR!F!bVfIR9TAi!& zsZNQfCGV2#lIOB3<^O=Gyq%RVI&|4WuUSUNrzSH6qPds~l@MGMxpB?&&)fRcOS!{C zSW!jy2Y}TX`SJG-!PjuZ$J|oKV6XwZ1>#<;z$ogJ*(*%x`9OsU zXruh?VU`C3eN0-ds!^<3gB&PbRrLDEMqtgIJ;}ksz(_s`M36>qba?tJQ?({v8rpPaDL$Ds0a_OQFuCkycU!#7i!4|TJ=|V+wra z-QmIMu3^MC2kkwY`-P!#43my5uBNZE>P#`ir+=mIJ|QVp{9Zu>50aU_#B~v)$9~gJ zVu;%;rIxrWMN*m+sX4VjY9Rvx*+)v-Fpi#t&u0xb<%ZT_^o7@>)w~EFOQwiCGL|B5 z)^h4Px){$eeH*@q0G_E>LO-Xp@d5PFhnL*6gt!|JE?|Fd$v4bfFH!4jrRP}4b=#he zw9Bxu(2&tCZ2lhY(Dua|OE7byRtG@Sg+5!k9%URbn;ms)e(<($0sqeW1s<`R&U!Nw zef-4+$;_SDu8|R0$l00`l#3w2bGl**TH(CBNu9}kMVuyTQ8pREW$cv^9hw8FSj@UV(dYn-9N%aM)#gwi__{Y^ke4QI2Yfh8n z36682%pqO_Lvwq)BE?|}fI}&=tQhY4K;u-4&xZ)iVj>S9Mu*A&=mY(Z(+?MeK-Z0! zqP`9Ubl6%CuxOeIGL90m8xiv?LMsPm_HngPa9UykO~O7!aIO3I?qmtqk-rr4@w!RC zF)g>eXBrN!e`z^OlQ0mIusB`?)VU&ym;MH0^jqZ1^EpYZ4tpi96_!LKDTbN-@+9(; zk{a9M3cw?5AG6s>0fh^_hf*V!Rjz%yQE+?Tzb2W^Qxh1=L{$M7wmU_xz#TrTmygJgGF5#kStzO7<)w^ol#3sF_R7s zfK()diQH#sDR}=<*$z#lVsGFeujZ5Hd^pm)sxrDxz|%xJUBjGDpt)~~=vDMDTZGD- zb(TM3jc@gtkIQ9b*_M?0Qa(o^Z}{myv;+@9grmu5nPxkp9mbT$@PD%L^0CO1G{HJf^Czj2>GD=n8%gDj{U%K$^EtN3rj%0_p#vsq_SFFSQ z$lNmOk_0AAIkskmNy5#l%BZ*+X=Fw=k3n(A&w+=$kx^q)SKjN#J&gd&c@F(n83ppq z-e*Pxg#jC&!+R2no#OuJY=a0a{6b#HIf9z8A$YhQ)%Dzwd-SJjTHFo1M+v8a>YKD9 zZ@C?l+|$=O>rsZxUIn#-KO%K33R*$+74w&nF{39;qi4>;%OBmkE@gK03ZZ%WTihxL z^rM)K(}oh59kyC17T}m>Es((2ugf-bDPc8I_4IL{P2MBH_ZT4dWr}n2%Jl~6fy-wz z5Hh^+jZAX~^;!EE;F`u^KY(Kjz40x2kEx4-4Ng_2&AKqep@PxxH|_DP0o>=HM#>%S zIWAF%`i#c44u5T$h08IuHlK|N)Q8vaC0i z$HZ)wO8>Cn{j3v+*~nY8UCAJZ{T3q1Rv~XMRK4$p?6?Z^Yc8r20d0+68MqRJFkG!) zOBn1-#5j1+3)ls8TuR!3^4jTaB;> z=;a5j!h%qeQ@iO=<6k{HG+nyk zo&HESWK~ja)aB4^PI~!b4&Fw44654(7|f(^1NTqliN@tX z^r2MC8g)k~7xF6oXu!vqbCtT_waH7fhd)-km_zPK6(_tA4@anlWE>T*OM;TD%t4(+ zS(GT*w0Stf5(@0!O=VJc;}uSm|tcw7GaYwv#3mq3~)%o6iO2=fM+e z*a2PtP375|Mm#~VH{WR9qP7Ajcv#An)=|jE8=omXv>gTYy*WAyb?`y7Ao_2<`acHW z|BYgQjn030hW{Hr=fAAM|D$})|7l|~h_MqgGyRAA`TxPu{9h*({SR`nF){xaxvX#l zR|lvArC(aht$CFm4OTYFGOVsW;!U_S{LItRpT-KDs`*la#ybkPA*f@lBe364Kx-MH zWY|}KUrXYDlhM_5t8^MRCWKbvKGg8VKb32c(ru#@Ece1m5lC&^RY$ZOV&0oX_vSU; zzWC+tC)%;@mD%#LW2&p)V7&k-?qA1o;qV0Afo`%rQBzK5eW+mWX>Y;Q^P z{7CV?+GQAOo6X`$-9`TSAtb0_Pgl4S1i-GEW`eb|*7)OXQ7cZR)D;~L{(4Jj4vqT< zIeYhmAbXKs-Bk0L!}qfs8?Z{vzy_RydnR3918q~i4Zj=H96a4FNi4;^L28{z4 z>Aq@JZ#ot>7GZWwqv>pp6<=B| zfw0vK>i+j>_yNNxYS@)MbOea1#FIeV14-jZNmZEFdK0;&dC~)0dHyiBHjQ4$C}t(E z_e7xA9$l5FRb?MGFIP$J9Ja;Q?I4`AFddT%nW6&D_iBZ5?;+%|bhC;s_T|7}{b3-g z&pCC7p)6bk^frI}wcMZ!twK_-SZBL^=|I?ct0UnklZ9o~h!n@yJpP`iR4g68Ts+~( zPjrDXW=|V;mRrW=wxhOR*!t`hsOeo1?gy!vJ7ggAN63j!{Ej-1ubb~^gCtPCwAnu- zC;G10?YzLDhMu}M{(!bHTx&6$1yJ=sVwq)$%E8@7h>1M!;wi9K4Wwt$NNB5=H3fMo z;gaK;Cg+C|1cz!!6`!p#tZ~4Ny@Aau_7;Qp?glY)C|uHqZ@r5MXOmVohHVY^KM9YbsYFeN zZ`6BH?XGT8u-eY2k{Hc?3@|@Up>LZV$rEpFil#N2tTlz*s?4WW)VGxkxf*t~Lj+!d z(+B=EY#$95vRH+5&4N5`0fu0>j?QI1giK9s*;2#?zBswq*R~ z&jF*#`;%AWEc6$zHi;KVTHH#gmBkN5TJ};G$`K0>v_3^peXpA{XP&H z(KpG7(Xw#ml;z%cgb9oLRGht~LmZW(BjtbQS%L((42v3z)LSWpvctURr{j!01o34o z_4h_QHQ5Z}DW#5Ztw6U`0~aMPcn`ZX49+N970dWR4C~gt1RP^kL$Qtl98SERZ&%E; zJ({Ic5s>I$FOSC>r3>P6@Z9*$<`;29o`=Kpm9Yrr14|*m|$>3OExyA-3D|1ucWov#=f+^ZE!{U6`(jw=XR5!sgtF+oE z@-j&aX4Kj5q|~BxHgOr-ftw0ukfXxpIqI@_69{i_!YJHQNhw~cKh5Zh4+zkvUVk$v z%IE^DJ>pvCv5y*TRL?U0S#6AJ20SwzlA8YFH;s?KUo^0^EJ#>gwfNPSMn!)8wPh5% z2VxCCvP&ScKM4MICEH$60V6c#>J4^VfnRyj92eR_r^L&9Se3j=P^amhZs6$swUI|R zq2262GbV;OScS+-R4Eg_daB^t0)f{sfzGq1<@Xr`9bq7$}5aWza|8BkHssPs0b zWyy~LML{t~?;IlU*jgw%Hc+E`^-O`aZva--i>XbPz8}S;}}l4c0xi z{x5!WjQ=KA`(HXemjB~UkMZBdaQ_eB75|s}yMO=Q|NFn}|NNt}vHz1|EzJ}JdV^(d zH@Wn8uCYCIa#M2A*nQj;c40*S2`)xOP4Q@w$g?upC<4mSj9?=nBX-cmA3L~T2z_YL zt$>|OW_>064H+V4xNt`*U2zgtc+$wNjxR%aGkQO^$dEBIRS=nVPLY`!0N=CI7HG#TVY{3O;GG>r|hM##8&XPV@G!=r{ti_=& zrqh4Ox~<8d@Y-i~K|;5@TL=4AU?*b- zaJH|#YI>|XwEP*M8LVVw-(DBo5oJZ=Ud)*viiUO*v6_aBkVP>j;(m|gbFi)j>&GNhanl?k zqH^mGxP?`PmHpums*Crt+BmUL+I;af-aTbBf2}V8TK=6K!r5f8a9RXcJ(JN*5HVPb z)Si)jwzxkylx)n#P_jI|xtx98OxHt4bQB&(F*Q)0(pJI`t;}evED@3Bs>8RdJ;|MGV6OEEG-ntuN7d$(AtitnJ zz42QeFl=%5_F2PojIGH^TB8n#yqB2V;^<(XTYcFxS z%P5AEVhewF%5|AC0$F&##+m>@;bkdT*9BA;0qwPI;O@dkzcD508pyMbdcOX&IV)Sg2Z!-=4XZsDX>X?1?%1hgFW=w$ zX#PMDn4Mu?+9OYx1S$62sXh@|k{Z4bNXgB{yjA-fw$jgVsc!zAU_4j*#JM83t0C&H zCn0xtL2!#g;~s#{Pqzs$V-;@5m6)`lQC>n!f_ntau=i90_4uZIL)A3CHwZ{2z$3u* z7F4y?=qE27CvDxXmSP=uj z(;RS)kvWLzVuvEiEOxn)Q`HSR^fLeI-2MqX{|6HM*EI4E;q3px49)nDyZHa9A^M-h z!vB|%;D0}j|MtG+;P@vb7)21vTnwD%LgW?kwI>>hc)*600{`W!k?w@B^;!(TRXmK4tsB%?TM_1A$Bi7Y@-4u;(@ID^c zZ49yO^B2Sig8P9WDQ1$|+5Y%W<-D`k1E+~QG^}WNpwd)@38yRj+pR@@T97SjaRjw7 zxmY6hBmaO4Uu|wm_{4OHFqi$`LWfXyyb=7;IF}xBw=eCFt3^bIT|a)?wW?(uj%7xp zZQ<_;Yx9I32OlTgZipd-c668D)jpgp1?l;vD!-?u0Q~ldfeUs3&|_c|(z|dxh>0bP zWI#I~bBagN|Auo4`lHu4>B`JluE7uWnTwB2jW)vf(udM-dwGn*phDFyRVQ*(SC~QeG1V`#X94HBE!^!2lBVIsn{Vi>mlW zd1BUXgi~i??pB~aS_l;e(qn$^!w0URyR?GhbOFaU;!OW^n}@xLz;1o>XX_g|RhM&> za{{5CuJA@P`EQQTP2;X+=8M+v7?%pS`v@hIf~+LT7Hx3QE&L)AMn@T1RN@cTkoEhJ z$8VW(qjfU-Nho7<@hHO)yj4DT!pQRzn3-UmSG_@e4?8Fuau*fw^Bs5!P1nCKKkm8D zSz`dQq{Mo@NWp4mP`EW`s$A8Xa~du%^WDMWJdk@C*&;XhB~sgaY*{tbMuk*`vWB6- zDhfG8=w?@XK&*QFlXI)Ak&)YF#Yu;Ob&FV1^5yILG_nmKN(=(u>O@9rrztb18aR@t z1eATtNoHPEE5?)QPyJZ33e(<<1@UOJalDH*I=RtGqg#ML@Fz?TLHrpEQHjLS4a*W( z>t@1}CUD>G{-+29@=%s848(*NFuD(v!uM`17TC#)7TegGBaJ8dLnMsa$A;Q-O>$s- zOOZb4gYHr=YOptjGX^A+x%~;2r;{uwmBK2+xTkQSF5f- zw^(RoW`Pk34#a31%0h1{iTF-)BIgl|+>=Ag+|EG6%$s10#Qi z_pIzwv@gpw`B%_oF|B11L@gL>o|J7pg-#APD z36iB3W&exL{=46VUX7w|`KF2M+_kfd++-X0F%O5tf!k}V$|xh@&lq;*HW1|oo4Y@H z#9blq9s^H;(95y?hxQHkvKBZ%{rB(sX~2a3D$A7@C7BrKsJfEoi_Lc5NkX)3MP?8p zw6@M6!Lgeq)$*`?G-E_Kd_-X9KUfUn>X$z1SHMtq%s|Rq50%8WW2(AuPe#Fvu$f;` zUTx~s@oQfAqe&^(u(mg=|CX&69>&jD2nfEwW4{_$Y98U?kxlPP4V6=S<1)?I+ck;H z&1GF2+B3Cs>iublx8DDpFO9&Tns-jvPI~ueEF-|+l}`gM0+JyA%OUbnE}eVZdB+8e z#}Z^)l-91DK!sYhZ60QFH+?^44o92qG9t=sSTO>rf_)jtVH>E`M_P$=GE66_6^b%C z3JE!rVSci}3uP&c*6A$kx5j=*i?~DK7~AR^+S`44x~Gf~ou3Xe&al?Y7O}2VN&|Gu zE(F;nbOBF}S@UBL&&!6}KHB@ydq(xp)%yFr4{6p1XUrsT(qaYqR4H_do^2oAweuqC z<>|669YUUQ?5^TSwI8LogC5?P&;(OyNu?ptoqi9ZY10EyD?=adnLv0_b#DJT1ExHD z;%sjNK8lhxs7}7gENXP7XI*s~mv!Qk4Cg}8DxO%E5toLba-I6;dmA8x2P>Ku+D9la z(+r9bc2koiWTn|z%uEL8R?2ka`nXAR++28t(fjCgyT5glINet1X|^yw9@cbFGJ7(pTB>S*>r6uIz^ltJEAQaWV<^DrRhjph>?(@D@SQoFQC^k<=!TppR9S5go0o%gL_!Sn9sBF@aWCisX%b)uz75V=;%P*if9%E^*_8;5^+8mQ`| zF)IiaI_JVw-L)bmO^krsxbexZ7*w1E+N}2-nPQZAFU6o9n0fm;}t#s&J^W zfw-Vbuq|CIs>|D(1;E=o7?c`r?PPL2((6=)BY9Iy!u4rv%wWZ_6~tPf&16?M0Atn0 z2GKv_*?ieu-=K0yAbRkU@1ydO*bL7DnWIQ7**S+>M*cy1Tqp`YXYX83QEY_#f<+eD zjh=+nDsqbUWeHL^5mGVo20+B{#m@i^MP(ExWS!uYJJymf#HGK$8dM_-4UAjyBQgW% zaW^R~_}t6}Xsch?_*GhE0a4F>Mrmj7Vlu;oT{nL84t+sE&RxCFSwK_7IPl_^yz=9P zw8bh~Uuvk{8=oK+G_4E?`JG~tf_{VR-o->*I?v>Z@83#d5^4ks+Z-Nd;2GL;tzuSD z^#BGl;xrLu*G)H>Tn70g?fk4z@+K9kkgZ_=qCUwY*>%BmbQ=J*s@rDPNZtFo<;lT* zRLsRJJt3?7wii6IbM#FKBq;tzm5VeHgv{}yFhB6<1_x-A_<_KMc(mXTlV6x?gcwpV1E;t1>R{Ll<;{=-|M1w#m;*(F z=xvkJjS9W^%LTTqf9n7!C0&SLN+D4|jN;&qVXyM&;`b|%>&f+24V=By>PV7)zBa(k z7s2K9eTL#DOcjcXFW)q#YR6T6|LH#R4FLTSYrAWy_O*?_HI@AKNU{WlqlYIY<>pVh(C@5h6a z1|Zs;uSQW>W56Yt5m$q>KEjT(FfAmr!|=`>;i2Nbm~BUyP@p!GTwfYtpDySff*j=l z9i+<{NE54SDzl0a_y&Q31%**mZUg|>{_Qqj>$0$#m*2w2&7brh@=}^I8ujHJluoUa2 zzz}i<^i9z8)9MI8zXhO-az8ictA_~Az;AD4$1Y~@c$(_y>+i}Dk@*RC2`%E9u#yAM zCNVCbknb&rb4kaq0FZ;K;cOW=i!VI&KLjTC0y zv?hx`H&EOAKv-p;%3RHKyG^n#05#UTbL$b<_;jg!{MYX+kw`*R5(o;R@H3q==q9bp z)yA!iTTbK_Fu%)Zws#U^DWvePxZ3kJ5(r}ODPwXXEDU~nNgPUL*BS@La2jE^&z?;r z5lP9|&sW32X>0lRJGqB0)33z=s17tf!FmU{oZchmE6QZM*66DPiZNmZ(7H^U)vB5I zkNHBqCQJ-GH$;5g6*J{;T7K-B9-H$&WdJPN=4*I)%#i0^sTMEf=0A+GBy+Bwz0vB8 z4MVcgHfQFhpfVb!n zRXfi?*u>40yB}QofRk+Fo|uw|epOzOy6S|`K$6%% zHf>_vME3{;?VHLoLr$&tif4|Pupt$l+d&fVe~cGp_#AZ16t8rg#`9_g@J9EmfcVHU zt|5JqO5z6QBgTS~_{TD1UR#q0^*#A-#<9mi>E!-GA}q|TvtRBpM%(|w5o7q_C|i8I zN&qs(oF$t-Seqj{>>*T-w|sX*laP)>=#afgNn#)O;?M0J@|R0=G%RlZ9e^a9*xIVz zEhbZhRKQ{WfiguXMuy4z+d^RR`3HU0&x!$D=Cq(YNP`S(tgpy0hcl^-F}-T^KAxoH zdwosVpkY!yI`Rk=ck@1IL;^=p7IsaFW{jZs!|A3iUm{|WO3di_YuEEH}`k${U z(*^=_D$KuUts;l#%L_RMUF!r*M))-)NCl}SD~$>3AxY(G;9dhu{|4hW)}zK1tR+Vu zICueKPH1&a?rcZdZ<>OrDcTC-nelR6v;?~RyRTZ*&AgG0Ge zpeZGVazf@UA?9xTx}8}L-FW9hgw%hfg{Ez?wSw)-3Mb8juCmtW>kRSGWCuE!&3V_| zmfog=+X);@5jCHVe1AEIpdjbdTnsY+9D0wsg+iifwjh@?+8V>$P`(91=z{oc92uNL z@*b4;4;TjBJ|=-`iK4&7WdFsvLr*`K+T=k>z=igQl+vUdJ|5lZ?RTX!RyBBt;VWr{ zysE@@$wsJgg?uH0dYV)CgnuH6H~rKeSUC+d;3wSGoCRqI+>TuLn!V_l2}&h+=?g^V zYd;wm?=1>3u`exCZryT?6PKHBINIz-6-!-*#o6F_h@(_IPw$OqpCPSO6s!++XV8^; zK0=$%1v?`4kHqg5&A{Cxqo>C<)}2^!4h(O&rf?B#f;^zEZ%>j-N5ku^p}%z7;|d{@ zeC@oBvA&n^Gp@2$ezF~_+N!ue4hmF%NNJMOXWzLH#zhD>=ICKVKGbr&4fE? zWp@+%c4m$eepLx2K1Ghw>5iP(2u;zC{j9^BWXNZX7hJLUXA5L-G$r{QRZMdN5$l+C zzh;4N$nn+UD$qJ&ttxp)c&$R~eMb$9riF)c#ywWSW%2T>uk4Nxq8-)?up?1MnmNLW zwTFgGgc+x<>9e0-n=wZILyruJC)Juvc0nmfu8v?VyCJ;(2^Rxu?Q$x(O`f~A@cN+M z2Jfbh5WzCs@@m-F0M_zDZq6^RkXLPm(^cBS*RE{Sr-JOTP}ZENL93fyG7H(l{g3C5 za_wH9ne!&ML=;r%pUm8}?L!MrH1obcFW^`Udpra;s-8DM?Qvrd~pB*LcT> z#=zvCIkK=tm;hMl&7Sm84t}l4{v0FJf09~&!sE_&mI`oVSaenD-K=)Cxqq6bgIfIv z_Yebu&SdGzR|#rNi?vR~8zReqOYg0Wf3o7ZrU*e2CVASNj&U6|;pUDmSpd?o9#CF$4 zskUyCKD`KlFgaG+(_k38%y6_J|M5y?~!J*&+W8)Vv2ldu4j!sTSoD1M+@ zd&J!B=e*H1uS*UqPt*ZNjFcrQP9r@TtHiIkBd;TiOJYmrTJsBnWm=^`-#xc^?jwUQ znW@3y$BspB-Gx3`N7(DxgU8aGQ*lTYJN618WYr~@ocVXB^D>EJYaC-fCNNu|j67EI z6I#SU`S}}Vyrml($TKOvzK(6K1Q}NF6_JQxxys8J!^(g~?w6^QL{O~4QXDF*YYo&W zCOJtjwTXJf5}c?a+slu=G(h=F8V(1-M6!pZ;f`Ewn!^EpCjR#TnuUE3-jGg%w@}(E zDB9Rb4o>9mVE?UpUhA+ubJKVeOVQN@3y@8s61~!t7onC)e%}UoLU#t_@e)z*udWCV zXc0R=5Em~>OLzyn=XLU#1GSWRO(Q<;)SD|efQa)}H7!31HhwplEp;=RKW7yA)fuqC z*P~CY+Gc*@@_)=es-^xop!(D%GSyfa5Ha7EdjTEx8yDfrV>M=*5EC(lyYM{2MoJ+Z zuZrkP+BNn)40es8s)0lXtW+xHGX<_Rj9ar@Fmo9{iF3sG7rHq;ad)=E)t)qu|6Z)d z%mu#9EB^>!A|YYGm-L#5L2rVE`?NdAE#H381w8LZO&N3KmdRvlo^hG&fAk@|tP38` zQxb-9HD)|%)J=h6xz6F1=S@Y;6X6=A0P(~$fxc2G_`|6b51w#b8ckp82}RJQzT9AB=hGy6)>q%&|QWO7J7j|IIX_L8+vdHj{$!G!-MP@;NQ zzf-hEiZ0_m#j!7cTrpC~2#d-4z2%kX#;S!-S z)B(`aD&M?GQQq;DUEy}-{tqn`txU;}WG)5ZqJjSV5;|_WA=CqVDYth-5FhD`Ry6z@ z(r$Jr5&eZ<&V!!z`4t>$A{=IM^ntZB4zGq1OEd$_qR^5JX?aTzqj@LqoKUfuyAn3Y zSdhvItJ0FhFXoeGv_oIMF!mIzP4nOWy!v2Le8bSfq^hx_jBf6i`4L?`70x6xO@Oaz zJaY}vnN!VvN`1vp!Y>g|#v`cezcy6YW<)ry>H?*%1r<#cM&lC8U9wZQF^&y!L;tHBB(|9lW@st;LPOsz`^K|+oOEr*o%!kChc$Y{VEt*X?DYmLB-1{awXJ9b4DINbA z>QVw2Oc7H}SSWv}1?@};bm>D;^!R8f@HzdpUDi*=9UB`-6TrJo#={5(Tijl23nQBt z4vbx++W`Xd(m6*5DMY_4UVDpvX_f~CWY%maU?$|5VxlCWNikVm>LgN%$8OP@%hXJ6 z$nEL-r7BG`0&bn2=BKV2iyDHMZG%zTBIuX)5yf$Ws|Avozt63-Jaa%iu}pAB*7SsZ z3Q=5L84Py;jkXVIe+A5w2F9|q1YS|*v2n`y_#m6_Aq52q1PiZ}f53yd}Rg2hg zzkm#+8`Gub-vsC(Sp@tBS6D}m60hnpEJioVp##_$(ZeBZw9CT-tC~CocyE`vAUT`J zJO7bj<%h{BltM7BUWl)q^GXoevbXa40wT?DXX6#VW~%(}u%TEM;QKXo@{R|`M=_4{ z%7Le-yF=Z6O$818Jpcd?ldR|1S_ut6fr(`w(Nf*j?8}8l2X~h%1GdkW@Y6GfN6xBn z;pGsUw3G5HEw_ts_4%5t*cdg3qNGybOX^vDi}hnc5ZQ7G8(wVElx%{nE1AYHyJp^ZH*YVJSBXT0H0z!nM_vnQ ztbgUIvmzH_vaUpHx|!Cxz^nwrQ=O2&{2_V0mHsX|L)L5<)Djb5h2YeKHYr>#%7}xX zkIuaTCsBpoJW@vVzF+1k&cCpVCO?nv{}hiwJgQ4mQlCS(o?H2AiOv<>_ppE|hD@pX_JO^ao=y-a-4GxnO*UTnjLaE3nqD`)dF2itilP?ZPF4KXiL=45DTKgE&|IN5i~ z^zK)AJtxeqf5r-a#(n zijYeP7jATpmp$cdyh^^7Cbq2j3hH!fms@|_ilh>Fthhm3o>RMH-d6;bxow2j*}{Xr zrxF*NSQ~*H!`soTz9;zA*AP*%k;mX3VK@a0j>TLC5bAwe!$~VrJNx3Grdg>cY7*EF zAaWvN=YDE>>TsYtqYTIKFFlsufMvJBJM?_yMomy=IBr$D;P_0AX{ITp9G@rX$JY`s za{>I~dBD{JE}q9p>*SMWi$9JpzpTK=qCYY;$`%Us0+4G6fIt{x^ei)8r6EKfu>hP# z20+r%l+X?}RBJ`;i&#>2xIhbsEPE;Ew=4i^zlp1eVLva{mRWX;#gmcPBNxFm@n8!> zdy2q0po1>xj-tVl#uj>~TQ{vHGEs^*SKkJn6X6PMNDr^EGQ;5YKL2BI7v0Ik# zTe=Vk4b3^@GsPC+r}_=uD8%aI4jY3cBJ5qqDOr*W9x$pM)*A={F-M;$I?(}24T1#z zs~Q5K{urbiB^L)+pXnXQ&C^~-xn!nZ+T0Lc=3Zj!-p_vZ3Byh9zEkcpQVJ=ZMe$gI zZ5KD5oX#c)nAS7F#l8vQ<|uC<1E-OrhYx2A-d`20Www7~R|P7HZy(`=4K06`nu-n} zxmJ2#F@L89f37u}Tn;OU=hY4p$fS>3k!A`Dv&r*bs`J#tMc4o7J^?CTy#p#1_`xng zls+75UE!@c=a8SL$AA%6IbQq$Ua=5{0OIGMJBXF4cqdQh-VWBXsE;~H7d^CERf;}I zBmhQ(3ZUiihD;4>-T=~ISkGE4DL(5wZH{sK>d*7EUVDV`$1Mwx1Oa-NOt(OY+D$VY z#HKwihWR6ncvacWk~bZd0KZQ>ti8MYQk(s^Q3YR(b=o_sU7$`nJ(;Sxt6Sy^bo0c7X(vN7+(#6O^eY3`tide}jjql4xgazljh>HX6Q8c zFf`*~XkJ>0+yQ=U;2${yeFEUJU8Z}V^*{DOT1r7EtrTsA2w?0vp&9iq!n3hGUZ^rK z#$ej=_PVK2MyDh%?mquC5i;cqm~3!}cygKqLM z_rRk}p_68c!ZWs3^zBq8Vj5+>sl51z4QBJt&JVA0b$Iety#93A2)NJe+k*{o87Y6| z47U^eKl29mL2E8osXU?5(paZ-R}1DupF1Q}sXISy+LibuWj>kX5IpdZw%+O9x&gc9 zQK{d_q4cX%m%j3FqBpPmpo1+p%4Qc1ob-=E&_bxdSuY5a`*5`Pk5kqCeqcI+d2=`> z4`b=QnAYOMzQ(%3bEbKdzoukDgbovk=OarGV%E^f9yI8Ek=h?_3*LMRuz1|ogP+%C z%_QMCNl;ZN=E1krjRw4c!c{geId`j`ma%M%;>I}MIzB!8FB~V#pYdg|EPQ>xAR~16 ztVEtoeU=EO`lB$;C8L9cDtTivk{)ig{ygK9f;$yxWCk27_bt;_TH)nGc(J-oaca=R z@~1jvuI+>cIAws1J^qc!GZb2fxdI9?8%Wy%mmcFlw^z=Q=ahdzQC{ zjUF0)cn^&9gHP*ViD&ZT-T(q}EM}@O_U1ec>tsm;H?vt9#jZo<%?;irlEM4C;tmSe z-OC&K{1naqnmL0b4P~Z13yUY3n2G|X876=_FM0+9b`imRTlvDY8wbB84chcO&-L{9 z_%1wKV)hJko)WFVwWhge()b`F^eFjG?vzo#UX^(3at(zPNN%F&OGLp+Z!RISi(4z| zWChtG26a4I*m`rI4ci-LK3q>rPhNn~V;^u_yF95r`)qz5b>1g3G{1vRbxJ$mHDpYp zp)foRvs9e{XLNN9jM2^nN2*FG7{j8ev||DEHuv3HMh%H#&O$S47vYRE(5UON zzv&o3rKbt8d}bX8iDbCy`ELK!67^2M6;-|3pQTZ)UF*Rts2Z4yS?`-~@EzLKpHfGH zqR)GWh@TuqSy&D_jm+?igSrF^QA0goF?i^p9yRZfG4=If0CgLPq*-U(^wLXs<>cA2 zgT}^I0~H>0EXkD>KZS)CO#gNg;z^1#P->c$^fLn#pYe1?Qf47>1G94-d|&^n5V^`Z zVYWffycM(O0id%YPQL1)tR`AF@s;;{av-@sXb7#^*-_r8czuDJQB}LVURT9Y_+62< zdyb+ihBi8c+Os^0W=@F{B~Z@e>HW32+ojez&;|6e$+Q!#e?d!d>E#Iu8;n*QyM(&^ zXsA0hG`YOYzv+paq$<_fjKG}8bU(OLdd;RS`8ima*Rh!BStC)PZifhUBgt~ZJMe}X zm*txZ7Dd>*gAUcJweWY}of4(NkZr>r+<9rm3^h@6n6I1cklmA7dC%>)(CD7Z{Q;Kd zPLBj`K~N6IZKrrr!MjWwPk!2;1S~cIQV$BXlGg@iha&(Hss=WY@D{-qhs{8YF%q#$ z80ey$4|lmmUS{eFOF~zxaNKQ~qq5)fyk*KxsS4qKHlgO4$ds^n8uvrDzs|ymfHq7q zkJ}cb{E(~Jk^!Ajin^b}MHgtDCn-uc$tw|KQ}$I2)Aru$&VSJ7gWh6Zb3(1)RqARd z#W-6mhHBD)tzM?5=86LWO4|MeE;D-t-Yt@WoX7;I=aQ84NhkWe)G~Jv#lEl%a<5R8 z13}rs#qq)zf5VUFaLf@rqw)EA%mMLxMel>WWS-Tf6*U?iwJfS4(bOdorWF+GS@z3l zGEvy$q`}H4pSNnWp;xC=pW-7TI@gMjs${3oL5W5#?6Wx)Jti96{Q7argdEJ2_7(?* zC*Qz$AK~|K!0Eb20s(iC+u-lS#^J z%nn4L7{-BMCBs>L*WGul28dxr|5LQPq+iPkk_I3OJbwX2cqMeK zEE_0g25@zXInYoKb)b=52w8AkZ3@K=e~ocEGe%TdI?Gzd-G_$R=lKB&f+>=8`;NRz zZ>yK~$+NDAJBB_!H8nzVh|~RN7q=GUWNjnda;27Y|L+>vY3^!8=b`SZqj_vIUZw*o zKLcv~5i!JLkIhI3F62Ww-a9I)z*N1nvE|AvPf@)L_s_$bPeP22eu>tcG)dUsp-hxd z+H}OD<0fXM*ue@dG&|G=om@v3{g~JG;r6D+gxqAaxGHs?H?kkOpMk&bLf#Y_dr&&f z=e|25E~4S&J|LQ8$SpmsqchPo%SCRXT=?&ev_tSsBL;4x`{6kzln`zLC5j>zdIgGkWkEH*b8rB>ygX5v-%jG%8M zs&4Q>^>XqmLb-cnO>Xp@X6L!~N2UQ|nkJgI?f`$an9bTV{|mN->7OhS{~`+hf3hu1 z?2P|OuNyS!iKKRk&2kaF_nKBrKGa2fJ=Sa(iUH|G`l7dHv}lbC zH@|?xF4&gP_)$^%Iz|3nJ)?J@1a)i*>@t1Qf*%+6v1n2@^sp4KNHqkqYh{E+?XjD> z_qJ69wTSK#BIBYPH#IP+DXp?gjkYFxcE?S;S%pp{j7@v~b^M*Y)r@d^6Bn6gPu_J9 z?drbW{(FScFFN}fB*#PO^-%`J+m3KF+WELc_wmQA{j%z@e&FnLz|y3Eaqf&ao$IE*s|i z%-M{f?)!AcXtR1_R7rM#zWXT=j{lJrs+9_yOXTD+qX$WJUG;V@SP=+I#wE)6@4`ks zb@GRMW3R&W>>oJ8R=rL>*?p_UcL8v7&5DbS=CCy#qcN3}Df`K!M% z{5{H8pVC{!I2zTLH5?}c0i9AfPcUenROxH>$bR)$b-U8wE0EQ-?BeO@%_fLN9=ft6 z^Ye%nsJra~!M{Uu92C!q3wAuxR0YW>Ih%0Ty}1a|fZSzW_fil#6e|QAJNVCH!)%;6 z+AhW)KRXa%Sp&3?SO@eusm`Ot>9}^`x|Y zl=OgUz_MG(8J2VJ+xfaNII25QtEV#q^;^KaK;;@g$pK;GE7p`%q0_@Ugf-`{r$9*& z>rRBi6Po}A57l}O6G0s(c=8lc_Qw_z@ZsOwAp{}G&Pyr_kDU*^z7!a(}$tpaR3CGPE{v>^rZ2f;^77$&r&Dtc7>b5wcrzE7t|6biFu~`ho7g3V= z6B~tB?;Fm!=~~ZD*v@vKo|Lub5SHD}V%ORtD9Z#TwBUc445F3|M9Qls?U@j|Hi~4T zonD+|EqHC(T*`J62|RZ8G?sprq3@Enn3JJYGEY41$yDBT%%~#!iUp$DxswZ98QcwogO;7>Q^l%*|>WF)xz?>D8|Y$ZONtTrEbFVI zG%ef6af-&Ixsl&cUF;cP=XL*9_{Y_JwXDT(lW-wX@2C2}(Dko7@IL~X3c?HnMM27d zL`qWAqfVZT6Y#Ql@+(b}vrDnvpx`pq+iP;`;a(UAh6*1Hk-nJi&O<9l6<<4jlPwyB zS4SD$yy@Q@Z9?04++02Gp)vbQDXXjTsqd*5?`y=)Z4s1~$&gh!e7l|u$5d&`a>wbD znvse9fRDr*wcfkoC;++(1~IRGdLn^KcPZfnQ@m2jw)mx9PZ${HDL3Kmk^b}u_34?5 zG`aO%rDghjhjmAD;Eg{}anCj1s@k@NAK1DZ&A4o_#GKvFs}`8`2@L5Bs;sE6F={~l zW7(wz77J!Gnhx%qCa27|c;r67^5bLdSuY31GEx!yXD*{mWR=|wXszMYV8mZe8iY#g zU^i|jO{-Y$Z$oxgW!=BKkwsy%geymsUYzb>vC#^LS-;(Y+%8*17u3jk-V-FA1Id+M zW+Rt?P7w^02A0sl$DZj!9bU)uSaMeL$$p*pBZngHU^^yqn%e@_QjzM6$2PMlz}fwt z`#8Y@VQS`tzv_(auu>_>4n7q0KX5fgEg{=XG?4ZIxwn|nE%Z+~qrin?x!V#Iwas<1 zCU@H)=U+#_ zIY=#i<4#NGgdrM9I=Lq%J_YamCEk^G(URdyoOQixBdS5q6y`e3a_t8_3l$R&8eo9`dtEC^NX-7`#UHr}1x`(-N)cqt#sj5wdd zkmF<|IeE0k;|T>_5ufPUHR%vvg``bw%K;`HF8xv_+{bLx4#pHfV72;GgZ(3us+Nhq z4ER`QloPFng}6SQ{VbrYzS|x9bN>L4M9(C_03`RN$(&kK3J)i78V27KSY&@1as!^K*9%CXoGD8&?*=0VJk1D%Tvu^gj!lH%8h0as zLD{YYb(3w6v(--uK(PH0!Y!GAbQi-+k(cYlMTH=q5i+0xNKw zp37M?CcSe}*$Z8_gA!h}-q|n(@+QB{9hDmHubE5CL&E7FG>kw6k&IK3po*cmq};ur z7~12=GP@UiDV0Gom7n;K<5r~Ruqp~ZYoVSoMg&rz#N@S8%qKHc$g~=LT)7qn*xa6A z9u--!(3M*R;G)nFIgm&d%aJHMq`#qsm%~6UAOTrRHU=0-gx7Z<+h6ZHn%s2a$N_ZC zf9FN_-4N;uJ2@__B*As_j~M^mHn2H36-2Wo%g`N7=pE|rBO?r`$<;9HEb%Hn*=0rB z+%7QG#|@^R60La&SA`OuZk9$@f ztBhFI47kW=-f9R&#l%?dm-FYuF*663Uy8V0m&9Am3~OuG(2vxNJ)$Yf+?9;Vhxc}; z(ij<96M_7&UIe)#M0G#zig+WtSZlGqeDczpj{JB=Q(?mz6p+d-Iv20CQj=4L9}}1$ z?I?*(g`43aAj#(nM4S|_tKmPRF`!T$yD1Ql9`}YKp^&DIC9584X}9*hpD-0Qbf|ei zcy295L+Y!?1)42?lwdr>Kj&);e;|!}CO>>(P@?_8%*!~gltAb|aMPYt7kGo6>#}GD zU0PF`M&{Tc5J)N5(YQtft=WH|(ZFJhudg~B{2ksG$KPHak2uB4ZGH~E57kecEM;2a zMCZgcgZ=$(xqDug-kW5ANA?RoUkM9R+sFLeWhr`cn0pbGHf93~q&sBlj~i5@)l;}n zTbjY!hO!wBQ;mz_`d=Slc)U5Ga!_O8wmu6Lya3q=wFVIv&1Am=*$M#iG&{tr9DQf} z(Y>gkHJ5a>sjHYMGf4o~JE^o-N8^NOX+3e2}^xAGCJA z41RHyXu+R{P8~ElMtynl98Kdlk5&%8*w+k-?s3A}F)PH*8-Ph#M9IiJQhD|f&hhe7 zIbic(MGuPaIoUnsW$9Z7>!^|_3Usk+>j<6g!F|@u_B_$d_17$gaM7Xv6HvZM|L1Qa zrwa~r*a!M#t3U5E0X`g&w5Z_#Zb9Gdn{4CR8FaG~vbvz6PiNF9?pY;t&|6TTibv*> zMM|_4iLyK01O8xg8p+9+)0^jMBR(N{Efe9#;~wQa?|P667`pb~uxL}nRuVdwpB1g#8Qgd_KM|3Bu>y% zgg$W)F~F1{7r9?q@YK~MY1I{!!e48D{9N&ILqu`?o&cfCd=+tT?OPdHQwHUp5QR?-+lkH(faWm%X@FuCnb`o$Jh#(>t`QfHfGzSW$wxP|tBn zetAMl__)kn8L31^+CnH#0dV6WE&!2lkL2zOoyn9Y=HD?#5x|AG9pq8{60uA_{VSWX z7ZxPIQQbHGqbxjBcs^Xc#}P4=r;U|h3xz_2fgZN{eElshXBeNntLp~@cybr1)K6to zP{)2NdN^(J?IY>8u!?Tu3@{7Q1V9R1YBGMQS|kLct>)9VscF25Q$H;21*177SFH=t zoa+mo#SmEG#e_6_Og%YzTh~4ov6$^CaJax2=Z)Oqa_HMINx$%yb)Q8sKKbFHy~BNH zI$2ue6lr!h;#!ci;*-}px+BH4Ni`Vj|ysVOcL87F?0@mhE zWx`jxkhV)=8^A(4NSOugBOaI-!<5-fXx>qwRECcth&hCZ&Fe%eqcq+rSS4ndC*vy* zL19c8j`>UxHDnG1e-21U?NhyD%`6~D+TP5TlOCof{6YyDQXr!k zg3aw|8u1-gkSq5Yoyd_Lf{WpUJ#vC!Nv+M9`2}x@Q4z$mFzf6LU`CZE2s&aEWw#;8 zhxup#ftdYqEvO!~IMm9Vv;2o)myM`7 zO~v-fs)8AThYzWW#x%4&Q!G6d0ia zxR^%+GPL-Q*^9<$uRI9J*H7o0L)y|h{!(-#bfZjmhG}EQUFnLRZ&jtO;}~OL(oN~o z&FS1I3&lFu`z#&@V3{w!<1^`vt3yDXn?WtwE{WwP5}$ZMtt?!jTG-;jzv=qf%iqHJ z-W@A*h>V@ft;)Y^*yBF8P4}sORQ-I)WkFXRe69_Nm%hWyadCfr(4;5zsz+c+2t0C} z-pP!m&gfo}!;d~itz2+`qP4`A1y_6#Gni#bWi zt*BZ+>LdoeAy?(`F4-Gpz{0$K@(d>o-X;6R+d?L0*9p8JfH6?X%4K#ccBF^!*Ikxm zinD1*Hrf>DA{v=|`8K)Ngf-b83%5Y13%Y^YW~sKQc8P_pW>^zkjHQ9{XVgPU8s}HB zAM0KO-x>yEY>vs{)FY4a0YnDOoaRLAeC9AHy})F2;?2U%htY=ekxUQJ97OjL92L`V z6f%n}l}iD%1D40(flESg6nLlphWsDgyGSQ_WIDH++*ESsth9XVNj?< zO@nsgU*ERIrZ=wWZ?Q9~>&TweI2U%5El7CYL}UXeC;UU12=ds{d0+;(SsW=gxZr}E zmpq+MK2`$7zMpUzTNN_SBD~M?SgsVN!;YfQqU?*pof>cKVP^b@V87~~NWz>7ZCA{jU_&YYO z@7#f-xt8j@ZOTDeh(179_+*w5V|Bc`+hJK0EZus{pm0XAV-~invHytQ7Wn~G@~NH( zO2Sz_eXLu#&Re&o+2jqSO+u7Vh?`kBTcJp7XBUkY^U}Ii<4Go|uKkOTaUEmFn>K z&t=XrMjSbQxZ2wV^HGvBM})>lhl>c8Qm#1NS{9KXtL&VV1TbvEsEK2vnP~vl9&O9b zWBzRiRTLeaZJ&~TXl>J0dfj}(zu{&RlOX~=bZ_PSbYCAtu5LoQgvOlc`4}r%1QBSI zq$2FLD?{2I!`f2c25Zx)JZ?ALSilXq83oEMqf&vswN=fdK+wCs*(VGY%N5yaS)Y5! z!|EL>nYRpa+r?6X04@uqR*)`Rzjhz;OJ6EaeIa!Kpb}Jea6s z5s|9vsKg^JYKjA(Z0p~`IpJ( z+0Jh*s``k8FaK@vR&CLQ`{n4Y^_1mw6;%|-MZwb!C~yHt#U)j?LE{bnN7Hi0$LvzW zr>Jf%4+}#MR6L6uK@5rX#-fJh*<6r|^sa`4#gY2aV4IxtVpk@@YwEqwA|_P(4h7$8 zE}J^jS);K?7+Uhf0>)I3MB958cCU%ytxzOCX$C|jLhYDX8$pdW+0sQjq^iTE2n0{j zYGGv$*Y>naX?a)H!Q%zD<-UpKEM`&(1W%}OV%wM!5r?c zY=I%;T4P4DH>41{HL5!)o0iB+)EbNH+^BF8a`?V+`fmn9Sqj~eLlI}GU{PRIXJjg` zbx$qyd8=%WBcy^+{k#DbrG+-&f>hU>hn{SZIA@GQH?Sj1yH*BE^ONqwD@#v#h0o@O zPwSe#?^p_F0h>PAPY+F}LnG9AXV_o*Z`wYqDP7ysDU#6z1M|&|KwE@R(*WSy{RRf| z?L57HNIt2z#rhaGi4e6ZBO|`2M(mRX-cVOuv7)F}86P4n+22XlXt3Sf~&Yn)i9PD$4_TNCl;r7-2x%H#p|QL2GQ*8%i0n5B%)) z0-{AGuA&56l=!<*$X{SH|HjOUiS>WYtlEK5+j(n6LNm}uv;+i~NyJ{}1!EJE)tS>Z zuRNnYAi=Q52fiW9k07Vpj#FhB)U$ZYtyA0JE=_RXQ?3THA#WBb^_1yIH>h#1^Lkf= zPeFI!Aycfc!=E^mQHlbeTVn>+R&hDeBlWwy4iCypBh3nXnREiaeOm$}D)KV7UPHF@ zh*zp?nqaupg&6Xn?$={4jpVRO*BI2O0W?M2KzU!tm8ty$`$-9*i$GspNZEZENObo^ z)Dp(!{PpubsaK<=V66qFmO~GEAPES&Z;+ZXXD7wiYKKv}>0VrahMnmc1}&R=9-Ycg zo!_FgHD_vf9ehbb{9W<>!fgMW;{7X|g7tqAj#!xgg-yZ!_k<%F{t#Z7Yq0*Ww4Y;8 z;$$cq^WgSa+(&D!;}}62;;xfYTC41Qvuxgnm<&6{VV6(N8q;$zDK1|@9{6mORl)=! z$5i9KiRoi}9**h|o5R&WC2~ArnZ#J93CPFg1<9n6m?ss65_yDl&(bCB;CwwYT1xCAfehj+`xr<|FTmESmk z!x*zzcSI^o^AV{ArDYJ`3dA*j!Y&tttgTDK57zisZsisIqkL@J9tA{kss0}zZ41Ke zfI2R7?&F8pMmsPoNcnuQ7sq~#Jb?)QK|(wBDdBSRl19^obGy>Zv8+nlAH#uBTmoj&*afGqV<^P4 z^nODvbk;~Ba!#7p%aemJ$!ko1kk63q-BOQwT(wIWPDokMc#5)eD`(rAiir0Mm@J(IQ%EG_OWlYq5AeU?+x$LfzbijQHc64t^8zf=fYz z_E9VNtZ{fSNeCmxyf7byZNisEMvWGYPXIZ374mO4{ywON0e>p4?z*52&?YW$n*l4li6e6IL*2M_()bx$AdFxZxQ2gW2)GD3NsBEIe z{D9(9zVyKpMtR8Qws|Ju?_Tx4U|9YM3KhdYK*;|E_kUtr35h6rt1!ZOCCu=oar{M>^_Q}koZbphWw}z zE^vvigwj1RfjT?DTp@?#v8U5rux!^0@#UQ|p4-#ig|?=_9B52q~8 zN$*2S#UK491`q=g&Q$~IGfoj<4~*2n&|GE$`DZs-Opuo&J`~=Z6GL*U4BRgoGHYQs|2V1xIHz86Cb$FGH=2?Mt)~^q= z%4qiLh3oO#_+JETA929DwSeOc7q*dzv~;N{K;Jr+i=Fyt@|lWmXpKU^rGA+*l-kof z{<+sbx54)J9s8xIgiKjOc53DaYGXUQ+8Nqn728?7ev4r2 zbXGw*RS4?`Zp|Cuo~?N?11Lyo?ocYXRB&E0oqPBDZL=!(W8PdLH?HMVpPtG{>@7~X zZo`f-4jofPAO!QoBWm!H zx-nU<`J$g@k`F`%#dPRLrVPwphV3F6&8!M(r$9{ney+Ti=HT%~cL+M>2c~Iiofs{h zwE9`C9HyC$MTl)*3Ho%pBc+=kSpwk;X-Lyry8Z1`GQKyK$6^s{{@1*!Rr1Q4=t*nu zhQyyFic4Z_H%fuRbX=ZePKJo)W;noRNiP$>Tfuj1c9mN%{j!t-F&?agLT5(rZX=TK zQ!3R*Bk9hP7fpN;JTmu$)*2gA2H{YUgUGN0g>iWIHaEB;_u(kpU~ll$^>RF-jRv9& zE4jlb9=^BI`3h@Uwt|NgPLRNODP$N;=9&9mk#4Aa;?s=@?lwl?aMe#(vXjw&rW{E- zGs~j2fU3LWA^>+I$+{xbmN2`)@~B|Ld_HY!M0r{XZW~7?$i0XvWQaZLY>If~J2Bzv zZ66?;o^DH0bnC=s8n(G@&6e$Xn^GPlS~aMYLA;(nE#?pePP_~(v(81P+N7blkBdU3 zfRJ+F&AsJ3iYKK%enVbVt^lgBUAd57I~tv24Uwpw=@$76;W1(&vYw4l&6#~LGtMC` zcj!cUs`lQtuL+C$PrBCnNeoUy6Yu#5H?2P2v(GtNb`E0mAmqtAPvZ9@qcLtVZx4AspC|_U1 zV}J@rV|U!yws+iLcc8rXCdw*^>zOPB*H!|;pTpNs2;*>=GM)q=WSCY|FhBq(IBtWl z;dGyR&D$NS=u?qd@#|3>uWtq-D~={6R(x(|8Li=HiiTg{G0d@fwMLu8Zgb|XN5?vn zeQ>x>bJFC(wE*-B2#=1N5BBQA!q$hnZx{9p3oO zqlNLqv@BS8Mn3af|H{?TJ5GoW9|)~kE?iY6vN%j6mv1s^^F{95C-PX>&ug{g%Zc3VB;o>a;=NC z)pyV_J?5jy2>oIrf~9aQw7%m{98BXu*x3cH8Mr*LNydqH-5m$KrtM%^_&9=}cNeXa z*xxs-TXNAKG+<)Cn)D4*97-+tZK5lGu$ry{f;YM=u~m>XbjRTj7ix>kVz3Qh?CqHG z=M?auswvG1s+>7#wnVuOlpC}uql>N34>t~pkC3p5UY|+WQz^SVA-539w5{55tXE2R z5}0#bfj6L+^ewUxxb?$7+z}Y~j%q)$aEW<}`Gq`^oGlYc&k>N_;0R7ZGJDbF6s(K{ z1>%6xWN2KlwK6ny)7CQS8GnE>HC$OQ?icGk+n#C^W0w|M5jjDAz)k-$-Sb-wyQjKm z_|fdobEvKbDZlzY)pL%q^w{L?Uw_y@T4Hr{#OJ3cm-Afyw2=(p?e*@MD^efC9HSZs zQ|ou&F-^Q__iqArqSOm{fO5G7Aw!Yx5Aw$gTIXT_wPpv-$JfQ0O(pyQO(*5WAuAGt z1A{AS?TX}B=KWJSX`-ENdL!t~aflazoJ>s2X1M7b5NY2RfM$bD?xgXc!ZdDu$Dqy` z$UHA@2D~$1fXV9F987G&wz?tI9~un$wz?k9I81BCI(o{)B;#yq%QY@nbH(DbBwtMQ zKlU)Rmo*RLll!6tfW8Pt=)ORJ7*@8E+0F|`;Ek~ZiAkI;un&ZHw_ca5R~|sP3V` zdewf;^j4l!ph`hNxU7E_<8WtV)tR{xuxA?EDME+j z)VRyr)m>3FBiEyW$zzL^Ozm@n>+HBzrPUuFQytv|{<1HW0&jniu#s_@S{RbAjQm`Z zfPl^w!R0gUz#1Ne9)r)?fpd;>L#If5jnPHoR{iR8MswK27j&;q4vL9okovZH-YD$qcykBr78BtXj-ngV>d~0YymDk3} zdj}XsjpRrxSqP90lCEKnh0Sp$(G-FYM|kkp<(FJQ@%w7wD#v21-c}Y^q(Tl;m1xik zO4uvx3&@9FnU~bF9Znu?dxUdJ@^Il;-8+JwnJ`M(|sG(*rF^x}0oG5-)A&K^`LJuz#;k}W+)34-?J~Z zrytHfN$p~0^9~M^(3QH(>$tW3R)Pr!LL)23?aF>ExTJ=9`^;%IPJb>rTc9}dkp3z8 zKQPLhoCe`Iz1fE|q=cV>`A$bHcgU3Um$$P@6&EdMA61|t*yFpT0F- zYi_xdzOV2o1#@2ry12}02=2b71ZO{g$m+e67Ksh$)kXIu@afUXx&YpFCswrhA({(cNlOX40FHLk(lNdNRC zALVZ8$e@dE#2`fR$2<~C0VLfO!#%O+DuM(msnI!;xgMJ9<01)zaZ>cAw%=3H#Xg&i zT;^76ln?f{?qd8FN3=-J7FrA>?4n*ad@vW?69my?Cw-)JJTPpC{zHgR5u8A!vivm< z;Y8HJZ%f|<*Z>;vYco$h07_IAwA zDz5075#|>h5EnK4D=;Luhe2zx>+U5((fB@z@T4Axp;+fPk`IVbkAULv>a_YN`m3Nc zWDF|MUtg>IWi^Fq1v-EC$N9ghgf!@x|3Pm2r-XP6|4MlL|0&w?Kl0o8e}^1mq5n(k z4~QL^+28>bE!Om$(B6mF(|v#3XigSAfj4XR{P$fs*elW%o& zQsUS_H^?x_BIwETWNV+K@q3MMcTcQLY>tL>Xn_WxoYPNFEY1p@wr_%WiHc)Y7DpMG z0%q#1_d+`lFBELgLTVT@d7Or(`~H_q7~y_%?fp7g%1aWmII$e_MM)N%n6iX-r5BuYqI z9;0^fK%E&=JcgqVHEPG>9?$;Hni_X;Kf$Tb@Q`O?T$Y5KD8$It@Vvqut|C%l_f-v| zs`b!V&VEe$*I?n%L!JN;Dtqf2gz@bS2g1O9zo5LFiY$g# zS|2TaeJu>OPy>`pB-+4iZ3dmNH`?ug!rU!LUB=aQshzZpa01L99)Bw>H4jm>+hwXStvxAi# zKv^fPIld!Htq*=&3TTMfqb`?kXC}F1m#~`?U(AM8zm6kg9dNi1 zYyKSh%D4k=B}Tnp8*khVX@X(lx9FfX%=zy*-{$(W-<(I(FyL66BtA*~mK!LmXGv)) z@ng2jTZbly=~*PnjZexy2LVe?w+EpXwy<&`>>`#((s-{n(F`r<1(0%T zD;_CZ*-nTSRY-1KPN0ja0LSuMx1=rm2ynD|U0xLHlO7eFpzKx6>ri~up#Z+ddI$T@ zD3aO>kx-*$S&&BeG|m5r$l5G59dc@^QqX?0$Og?_`~mmu*fkTcknckThvVyfG|==q z;YLZCwDwwtMD=b13MYq5Dy|THuwc54Q<9|gp~#cE!~k5`DAwx3SWll6S)<)N99fukdf&vkr-yM9-r>_P!;Hp8{8Q5 zOZ{A5lRL8<^V$B8&`Ts%|5RLrdJYvhg=rE=fO_|vmTs;|C3z0m)eD+Lt)N}TEPOp9 z6i~jOH0@A36kZ@k4^u@LNPJ$`_~BN6FkGX+8iu9HW?;V9jy7<0n(^CH6k3D^Ww=v| zz?%CxOaHR`qSBs3S3%_`%cD~FNt0c93Cz<0{*|S*4GA4BLKazNUsx|Av~w|~d%nJz z@ui03J#$#Ld*~u*z{$^%LLg5uaBgI@Q2?_TyIvT-4OidyNq6w*tgQL zf_-gmy&5_kANNlJWWS?c{wTJj^?}@%((3CN>EaJU;v75qcTC8Q?AVU`QP#thc;|X;(mFIwD{XlbeE}HD_EU^C(fIZIEmKV%SVq!&M>r*m&jENgj zQqCS9mKc85bt)gKXBeJB8l8Q0$2~DEliw0&-h^7e{5SU=#=mTl{STry%Rf!@X8f-r z*7^I!`+u_j;UCrbcU$cA40L~4BO5Kr;>6$-4&@AMZ#R4~LA-QWC3T0>X}zXbyZ1UZ}XN=*F>2fq9}EPpZ`O!&@@Ab53hZoK1NrlXj{0 z-fRgk?aPfUoaWs81NhVvg83k5!t)!LW0TgUuB8Z4ea?c+{o!{@)+6V->q=CEjE0v| zAci$0Mwgp?aBj-#A$H5dP)mD-B2@`47BCi)(cigy%TGo~cK`evf_y}{e1@SpNg5U) zElciU*elI#>5@GtkEQmU4-k(Hz!8g=24OtgvCA}hI8fC4DAjn^vjcMr!x}uCiwmX> z?icm6q=fWp{bC0{C6VYg!f}*oMT8 z2_=yGB_9M~g!9p7UV7YmZCLP#spaTQTn?fOOfZ7+8xH*wz}fNGuGXy!x*pU*!vSdUzeKa-oC5K zuF1gQNBvo6;MWXgNzlw1vbBomxvpxWC~vdBbt)P5T29aw4NPRhFR|}o&`zShBHWU>02KfqHnOHEIoRSe=-SI|CG# z+4JinG84scle?rS<<0nZ+^D5Vp0RqrXINfPeE&T2@6MBdd)f8BhZN}khlYuf;a@aN z|A&ymWCOA&8dhsZ=ohMkb;My(j;422uRFlegjw0ol8G*%dQGrSMyNXT`{>YIsyNmY zLMRz(Sug2Lhv|o_yJdiMKn=}JX3OFwbot+p#FmD!fA$`3HS>fGY5Ik@_t})wqadiI zY@*ju#2KOUvEoa#%*A5J`HbBu@;K@48K&G|b5Y3NbIblm$&w;h0cBGCvaf=C0$TQt!_wdU>F2 zMVOuxv2<5i>OruZsMGF31X!O#%L!TV%)h+{z%D%VxE4gFWvS^=~$lISnQ3!(VDG( zBjY^+-(jbX*{Wh&=0PMiq1OPfgU0of#9MrnPDU0VTBDweO8r{lMvpYY!NOP$$lHOZ zQ+G;Hcp|sTI&P9;h@K<9SoXt7Ayp$ZV9G_^xUtp7+p%IG^EQ(D{g{jdVQ@`5~L`W**0E z1+Kz!h`{vpOD_8LY|Bb8aS;=@)(!Xa>7>*MX_o_IT*F% zo$yO;X?}iWMt0h1H?59?c;n$TuQihz&*e1(Djj&@IxbvbKjHc2DtN2!4RyA|xRBX5 z2&}RZVmC__AjDThpiR<7BWqp5-my3EarMKM&eSE>Tfx@YI8U__OSlXqva5%4a?+bsK3gpLizolvdLAYQGF-B* zpbT%*1C_|e+XzP*Q|@poC_o-neuVyY>A8rh)KAE zhQGAvRgQ`+*f^{xuEnut)fSr)JpEFVh$^9H=zO=U#2&{)hmBoY&84j63&@}21zQ?1 zF=t3ko`foU9oIbzLW4Y2d(;pqRh&laqZg~BjBW$T2}+m&>W%e1l6wm>*IWMNVrNzQ z`v}n*`E4B;txT$}CR^*>Q6w|oQASbY^{f0R0>Zu298*Lmy2dTcJCJIzaja78^|LE~ zuGH+BA!HRjF$Kq*SFgh>mhXp#PyarnPTnjP!a?Me(!&=C#iGiv>qX4M5Xwqukd3)W zJ2=4a!uIZS%wq5%n+g%bUTS4d^Xp#Q`Z*kd26aXoN{By4?w>t+jzj+V)P9%eU$Az3 zGb)Bc#MgfNt^&0{iu=(Y){+xGckJfK?E*6ke|MPtzr}zU|7*v9{!>0v|5Ac~_cO)7 z%=VXG5a;yO({56<+L$O`L$DjP(?*VokEM#P9i(@%$2T?O@d zkio(tY}}#wcfdl9G+ES91_()|GNFD{rzR#mhAHs&V$eCpDvt{Mp*Y=oR&wzn))_y8 zK$8ygz?dNU-0_)&UAl#YJIE%~PT|ZPYU}l0a@bAfjr#jQ45=#1mK_M1C9BGEvNwm+ z_agA`yQ*jC_?Y3zdFdpos5!k0E>!YjD$Ki=!-Q^mO!lY0CwKn7`9M%f=%y#|;I#p@ zlGTxA933#>>skI{akvej7G7uus-+b5hueJT0(VpBYHPP=TnBmmvddri={_8!La0&s z;x216HO6sV02c8SeBvqP;9amG=Bod^pT-ncbBrqV_2FlcHLjqF*i^hal7t8ZFL6XQ zKTN^Xm}~T;Yt3OTIpcq)6mQ8rYX>5s%gw@UAgYsEG25evupJX^pHo@smz>pH8fPO6 ze3NSOH1x7i_>`Si`4n}jdOQ&>V_=2Z_3^cl;c46+pTaZm4I!qhC&aUt)OIXPD{+Vv z-2SVM=Q!^2M$@-uFisBOSM;cmlgj~&NVm|6`nv+faJe^_tHE7yPMV9N%wSC?X~f=I zXA=Ahy|DLBq38|cvjZetrEmkS*JAUvJ}CCSnSl&-9Ble5Ko2+Z65qP}j$G>aXB=LI z1};{R{mw-TBec%2rlUWsw$$N{_n8vC&YI%HI`_8SSYsT^FaYE6*nQe$*e{EhG`H!V zqYuCoBuWnsz$VOqF$AcPS?5Wg5`J)a$|ktW?A=s%p4e@g`@_k!YU;|IhLWnXvXtSEfn>gersCq6CN_vlcz zEJtv-Cd9P<37!@3Lf%S5cgVdl;yO6nJ})Q33Av0Z@o$$!%(3xR5kh0& z8PT73MCVzDQXz~Zz*ATv4{b|gStNv#-IvHds@n)CQo0czSAky}A7H8FD+@jI>vQ-{ zLO3x-zXsaZ;WS04E6jK}e$+el4_*ih+JG{&FUd&8hKwBMT3bY9qR<^mV77EhPc0^$ z+~to7H*@X{&jd^n88u(0LSad z(>;Bxx7pZ(?{n5+N1i`k8r|=U7lfu|OiR2c?Ax=$%%SLl^*j`AK97fgGm32VPqBAt zM~9k`4irNc%jLQ<1fIKCrkW&hnmcWzu_RpRMB+L+cUU3p$FU1Z&`@-oNc-PTwGH!6 z^Xk5_g?`y!(k79DhVMcvIfJf5T}BV{0g}Zm;eL1u<2hEUjgE zjytPe71h7!vKX<2ARInNUqWOpuzGe(+)$xN1L9rm-$Kty-l_Pdp8NYo;S821{Z6SF zBc+Ai+m-zun$3A)G4P1^Zg-SSvxn^1ERmQJKqr6u*pAh9VZI65z?B2ltAm2+ZqMzP zoF7>^!e0aUuC(+&2$(cGXc+gzb$R*o9L1 zrZ$11pxEqTqnrCI4OnHbPzcTUrKBf3Jv&XXpz=oIpE#H%HApS+tZMfbDVb+}h=(iJ zb@F^Y5^VPc-DENZE)-Np-P_9Ux6RhZO(_+#BmT*!Y|6}_<)v{ubGAHSvEr*y(=e64 zldvRgLq}8TRTy>GX+L7U{7bEt@rtANx)qm@vs4yN>a@Gcgp=E3Mx|C`c&_Er-UTOa zi9AV5zd$}+bJ6CZlhqX{WaAm3j_?K7ZDFA>x(YlaS!?Y&SDG2opX8WM>H2Cl=%Fxu zn+0(KdD23L6b^@TvC$!KI#T*XOaS8DKV!wa+7eABDTn}f&XlpeD+cTLWzPc%nLzL} zTsNQMd9t9yJ}aFuZ0iTBTs?Qgq&~v$dxjUY2-0XJBA`=n4vtaje6U7G!!zE}P{NA0 zd31ulnF}nnA?A;`3a6_}1E*`_n=aGbS;HVH$Qi%FA@s1jpiLM+{>eJQ7TZcuw+kQ-LLp2ULqdiA7d5}FQg;3(VJ|Y$hiz`?i7)4=hyiT;5L|i zjN7~9rj7L_Uil00X8DnY!_Ah%$5yH%ESwb50y)}d-Aoz{ zgcbZ0Y1m3J9pc7Bq{kb~Ml5dVy&W1eW1BiFsjdU)9g5fXa<);L2iv)&T zYNa4Q%Z?lJ`Bs=MTt2w`gyEB)vOX(TJx$QwirnxI@76cdl;C^%TJhR?kziy*?1zobx&4+z22Z5QTJRxf(+ zLuCY(O6Fn>Ph(<(#7@^L)nzt+ZTl^cRoIRvvgR-Ar&-(gfdTx040rMCv!uM#U2YG2 z6}*M74}ob#A2-(xk+J`w@5P&7bB0hGlDwSCJTm)(vXI4UCz639wviBX2BGU!o2xaH zF}dPA3j4Q+l%BXoJy8qasPmILeXwhPnqx6aMv}ZMUK=(?aGj?m+Bln^lj8etR7Ydj z4#nU{dU;+?UxILSf~rvWslJ+@b3{TrILo{M^1o*;of+~$FD!;jMkX9X>)p6~)D9=dY zMxM7gh%WSO!a1F=C|e1eZ13>rqNWr?3=)VwR$9I}CrsRgfEMUt$@47iLmiNp>a|@|fqtkTcF~H=(7n_wwRF3k~ zhm)a7MC^n!FxO@WKPl6lRRe^HH9n4vV6|DJGRLQjG(+A4oXdTMpwClip0f_DC`ksg=`+wL6+DM-Q?%~-gX*jv=YrXu@e6`82O|$YzMS2Wu z(EEfPREX+3`Av=TT>I$?1_P-!TNWhWxB>N_h|H{5=_hoX44nr|TZD_);}sOO!I;G) z4r)~~5W}cL5;?SUFS2bBjL^jKZBg z!AozsB`$sQ;6{iD7D)0=LmFtaqOZy4Gew`cq-{uWay^AsOo@B;W&_!eO*%(X%}`Q4 zVQwbprdXz~^oF3z`<&b0LFyW5`i#9t0|x~l?;cmPmjvS`e*kzs2~ zk>^P*@`}3cc|J?!$wVp*7RaynkSKn@lDj+$SP{y23;zQ0*-IHa3V)}j-wq5Q(Rml{ z5bi8BL0X7#oB0Ddr(@O4AJl~xL!5XtyHR>qOS4}u@Qbk8>Rmi0jl+`tfV&vwM-vV~ zS~^70L&Clj;e`YV3T(*M!9m7DUBz@E@jfd-Ygt!ldt2nUqw_0{e{Ykj`v%SNRH-q&+E4j+TB&!P&|=q?Aw^>N*l!po&4e%sr7cF+Ibxw+s$YCW=OeS z^$?9iu|fIr+DaO`i@@3T*AH+dqeqwgzABe6hx6Jzk2m$|P1(c1;4~-Y@1Uwf>Wt|z zph=CGiP4tEzJ^BYpv~umM;=4E**=OBoY0I;3S}a=RmHbdPQ-hN&7j(+XCZ}wM{+vWr0YuT?Q?w-&i928T5q9E4y?)*q7lGQK- zB+JE~bPe*ON)g+erlxkUwKapJFClJBqt>K8VoAJ3ffwL`ULq&~kzMSc0Lg|8tFJXx zX!NsT%J0oIm8?bpagHEjc;4Zpq{;M1E=);gg9NGJHv?9oz~(B}U*O%uIvwC*2<9{f z%4DatfsYmaK{Y7Ok0O>Jz9Vly6tl?ZS`6eS!YN^0GF@4Wu3hMYq2PXHCa7-V;s^Pr z1nojnbjgyuOx}Q&;%e)L&X05BaEBgWn?EdSensW}KJJKrN$fR(?1Pv+tjozcrH%07 zH*+dcx}?gz%>D-@+OdR&>j0OH9jRk9_5{2B7XH9LHYk90bdRqP)uijtb_rqiGxCH0a7{6#{`;4(K`JJF zcoBmD`W7)|^E28L#W?wrib$GrHLj*O<+?TwgGd&PM zHp05Zc_^CL(G27D-*Vic#3eOD((Uj|OQ+5H0dZ4}DL)FKiwwvklS?Dqfo(@RRaGO+m_cR1}V@ zFSEtVoDHqAt*y(JuA*RFRF;G+FuuVhTP-(v$6`Yg&_Aa@*G9w!3d<852R$_5qIJHI z7k!_tto}d<;UAieb9=4Mgt8pcTjBZGA{2VjTovo3=#fe^PKz>Q*&8vcHp{lBzYn+uaM_d7mK0 zKa&+sybL3UO~&Xi5as-iBPKUR+ApurL?QTe?A^(kqV?o8&EfOPF7duWlK2#K=3}Xs z6*g;yf#i@-cs}apxPg32CkYD4EGtJA4l&~c8dLsy=W|q10@kNGku3#?B?4-kbRrf0 z@@JASHOyE-C#%r9I5Io}2ZcX2$Q5C7c^Dej3L75Jau^e=W_79yW#&H-+{yB3Q4Q}` zWh8kQAf#l69gf$BCboGmCwM*9jSf_MdcZJ+#phhg?^w$Ys`7$ zTd=_G)_hWhHukH5W$0oGx{i2NC4ZbnDGMUGF4=wceYo}ty9DH|12EGF3L%Rd>6`k# zNjXXC;TQaX5pOI_t^229i4x%5usWODNdaph5QPo&vi8-+<1&r`E2-QT>=-DEAb*KA z5ngFX!+Z@GVz9C(>#Qumymh7`fRaVwu-M0){pYHJ@awNIJtWn6vtQV>))K0~Dvk!9 zaM)9j!$aL~XhNQ>_=kTU+bJh7ekut>^O2|p{lUdM1Y6Lrb_T3z-n;?`11~2&SM2fxf|6E-O)c z@)-F9m4Ia8>_T#k#J=z)g_2W#IGnB6FIUu}&XEYtWtQSz0fz(uD_?G@ zQOm&F%llYaVHSeia$G;~&l}pC9#w>N@)SVZWypH)LuR~Kz?4Gw-p!A#b=L5+N+9Cj zGu&`=7=>H8dX_LK(`Y+uwHFr(-W#GNHxmn}P3J$H!192=t~4Yt`FbPu(xDPYvpYL= zl8+5|-pTt>$p$Dw$#8Pwd4`P`gos8fx3=A}WL8cDGH-fM#pTdp5utTB%?eA^SVMdG zq!7M0@?KIlBVve*KZpk1llvpc0^)mmBhCawB@Nx}KD7!kw;B=eB(dhu45cW2&*5$# zo%Rx2UMeIORibNpxP-36yfl}Itkxz|ap|oV`Q|)4VbU(%e13wQ`VOrp(4!W0dfI1` z1`~c1wK4Cd>b%Z1Q=BfGxKK2O61pu4mJbD2=C0;jH^g5^JI5i&!B5T`;3P9=6yX%d z>mj7%71S2aJ5_7TIbCiJjPZqQ?p`7+C-t4r32tyv6-|Q^Ls7F$1^7Mio0+#DB7)bh zid-&P!!+F016rkXXffHB3Cmp74|t@Geb|cZi>4Yt!+aBXOldwuyT#{_N&&rbPkBA| z>8hQ@7Q)x+3E2CGafFvM0T3~Zi#|?H2f{wYQ@SU*5jw&y>*MlJz`gsd?vJMjUk!E~ zbdVSLFDaQ3F(w?ZN@J-Rc>yT!*q#LLYTA9jr-F%8>&n9VPcyN<`QD;&*JpsemaZ>t zmUFW2V@h+IoceUt0~lwaLpRYA0svN9lQ>4nvBz=7@+Cy=m`Q5BgcZ+;(e8a~gTnEf zn~ps71LOVbbzk&$Bu$>dNN8gip&SZ43U+y5cp7g&H#~@xFoc*ia7g?P%e*Ia)(3+D zaI%`CP4f(k)C$JXA^~GvH8`&C-PDh+j!(<-S6qn}`-bmdqaAx`u`9H$Bn3d(_3OKRP=;1?^P^>0jW#+R_B#&@pm*RxYK@t@j5pF|SiTzWU{k(7+kPZ=nzmu$f z_1COm5^?diELjPqf6k32IgS1k-irJ<|e^{LCUkNeV=jmJ1EPyUqd5L7ivNUjtwk_mOTz(uvhn7z8OlSU_1g zy5Yo5d9bD+CO^Vw=YR%F#@y@1kIj`iz)?1S3rEGw35R#%=(!Z3zfkwV<)^Khv!130+tlmDHL^^T!L74J!*u)idKKhWLdvRu?eMyFN!hu`9)eYk2@+YGgIqP*6^?zfAa$>^uP1D}E@hYvRt#`}6TS2&H7 z>^*3t@koQp$|-^Y8wOzm83Bf$pv+LiyQ;(i3o8F=8#r~+(|+3^(uVlTJNI=-4e z1kChl{lZ`SDep8YvGVJ8eVR5;#NDybHV4)3!CbGC3ZeymD^r^L)hmJ>L-{DOZ<-IC z!s2h!vC);T?5n`3gQ4SdARZP^n(-(`S5pY(Nx7-L63fF0xJ+Ie8$+_OG5V3ZzBCX& z?ld^_KIKT;Lu4=`W_sJ&Qtv*a7dNDrAQ6a??!};NU-Mgz-DS^+`2WVgcLv zmiFe#8CmaqDlV-WA$XaRjrcAE_b2>*0&z%?EPm!YrmprjfLAgP$&PfUmx6w8U7H)a zk6&TQ4_99>k6PnQ#|DMx#0Qi&8~}#D)%er`Q(Yd_(USNnou9H(J|T=ZrLx(s4PA0Z zNPiq{4nZjMe2^@GlUtri3EsN+j>Wkn_j9$lW@zA#OsNLZjiO@j>B7A0Qy<~(x*M7q zJU)K$@2)xiE9_9F|HpEJk(1#Mn}zxBEjOOr3*gos?HBmTB$CpOs8ZVk*=?W`EOp=E zZ4YDEH^UKT?n-EUalr|ii?i$ev*^mTO*aQKxDsuo_Dm+tRZA%P&B%xd5_1DdQ_2mJfJ0BouQd8 zqM%KaEX8uNtS0sz)we6yzw)#MVY8$6Y|z4XzV!ih8pOSm6Pa4D3a%X%VCa$(_c>j4X_Q zv1hQEMnlyv7>vJ)C7lrxZp0W$LSu@3rxC1IpFVhu!9#LS%Ei*4a0>mL(@v>+5$YGh z2$5Lp-$+2U$j{3=a_ktLkyzVF^+KeXJidWG$CkthNdZELj$!QSqP2fRYb+vQ#im#W ziwd!5Zln7;%3K!fUn4aPyD}{nWTHAx5z)gkzKt{y0l4L|MHZ@{q}ezAjVYtII~_#O z4=vgtS*8MBRRZ8E(6}A$1n>gWfNk0#KeZH!;3M19f;t-n@Zh)Vq1M;# z-7H%R#(cdsT1qyuJAbh#g@R+PyZk!~UX~DufVxWx>)x z-GzjLsS_FJ3#xXe+|~G>J=az59dUHOHYfkIi(~uqzK-iu6>j`BQ8(d$A zATB2^P#aKTs)rL<)^bbu!xQjyiXB_gHzDIZ_|bl#`~G?yKa&ZOtkS;ewdIF`0|O7g zxbLs-)=4GK30Of}vThzehaE$iq(!wZ!eJ5V%p9tw-80zdz$L62cSY^xxmLPh*#pbK z6ss}&1Gp3ntqAt+b^H@tsCEKpu0}*yGBQQ0LDwjNK~L8}HZcw{iix*eqaFhj6Dh7d zS1ou|A-<@1@Cy66L1eHJNoi8XB()V$k+(S=(imT{rJw{+2#aGCN->Zfn+>R`cZL*0 z_ZAye3eWqrpLki@Q25)r^A(`v-Ndxtd1_td{*Kh7_Uhc1?~}}YpCJRzO>sFjg4St2rJdc)I}Oy(PVIw3 znB%rNz&K_XMDrn+nvRf*Z=;8tZ7rF9F~cnB84CGALobiK+m&h(nCa1I zu4y~Qf?9gLc4UO!Z;SI`^=G43yp)a=Gy{x(pTaaPs6X9<*7E~3Y<_EO3g5Y&dg5nc zu%*`LAReh0X@Q+gEaeRNlXZj!_ij?sdZOkce_clbAk2Fl zuu7f?g%9p;+P~dT6H;Ksu@Tit&gJh5#b)GeP$|p>jNo?lv!fw|tF6W$i+-c#S5ZWwvw%o>$VAFpIH0wLSd~ zRZTNajg11;;Z%Zifeu55!qi!DPuFhx&Z8iKGWi~aZ$00JR#xgFZ- z?hVQ2KFLR{Q+;ze4b;wz9S20WztEvDdFE$_KhfZzZoTX09Awgn(p00ZEqMdUxyNdz zdq2Csgrmydx^P`2N#95qBa#(A-+FCY3{%6oORv$uG|%2K#J zm`&Kep~f?jO3=SM9sest*ndlWAz{EvktFlT<)t@WXrv>6Fb!Nal+AopJnH36B9 zCzT_#zc*^$3dpiFrSp-(UvRbbkZL&rlnY8+XR@4^1m2(-Nyr&oD+ey01Y#yH6eFi4 zf0WZ_l4zpuY8jD)b?yW?7t;TJ9UyWwF_Wo^XBf%fh^gQ^X5(UaHei&{rvIho#0G^VJ&TX(4CFtEm%yTL~9@~-k3A2Zx`98UXU>DTYP!P zVf;StxOkAFH{IHK?2}A?6|+#6sFOkD!|vm9951~oXfV=QJqD3jOyu;*13X$8MboCn ztRx&<5*+@=t`9yGny7o-0y28F7NS^YndLIMtZ(zK>O6 zyUM;(c;CPk#fK@q@AGMpLA8@5@NlG(6?$)Xq0@}$iXf2$MfddmgVfp#J?06)kAmgL z$RpimyQF(W3sg`Elb+n_Ncxs!h%&Y8)>6cJo)imU{`hU^v8e^{a@*3#?aJFJ?^Bw% zhRze%(7-Uu980ViYr*B7T43mi?Ghlxw#6gMDj$tO&^-M;JK{uU_e}g!bElz<&~G4{ zRjqy}EreH!pNz9^4`X%YABNxq^iQ%Hrn+RZz3>D)pdxG)^-XkB_yzn>82lj5Rv`@7 zBdiA>@POUpx!j#$*a%R!_&I^t!t>m2w*6Ni@7QcsLM*>C%r zYz%JdKPSD2WmlCRetj0(Vcv6G_$zcBFRCYgiIf*yWX6|EeD6sk!zUi7o)LBbE1$VXjz$+w`?_y$9J2`y>h-3l1gg7vPJ9Ek8I+ zil+XwE|={1njQrk=1CPn`1y10QHWH{rg?b={IMdc83*Tts@kL$pB_+r=FLfPBfh%H zoWk6mTp>F2&SUyna3Edy%HGL3Ss9~#Mv?#rr%dx@H5z2 zzekJW!ZTQ3`=`Iadt|nFBw|A)x9hOo%xte@7_~9Vp&h3V`ivWAHEg&y*db(%Kr@N8 zRDGs0|A@TrL$y4E!9w|G9k;oqQr2Kwa>6kw-fkTq2!zz6BKbQ4D*!YdE>(qXq&x321v+d{SxL zwtFTM7#F}`q~9NLk(>kZPxr-qAH8+oxyPVEpM6C|vU%N&+Nz2X*QManio|k9QWZ!6 z@Kjhx!8f6%AB5trMP$ZBZMDpOw>iAePfO7c#npDYka%EA%Ve6;{qHWa-n<p40hreVCwTL*>{kEQK zesJIKu^dh*&kfFnsVC+#p0j%yZVS2<^v8$)Xe`i)titSsIf<|oI4DY-1lqYlv%Bq} z&&r_`anB`mJeOA6fdeGLe2L7eWOxk|V|JoDb`^>4A3QRoDbWV;}9Rpzz(nN8V` z^gGXvJ1SzIZ)5PjirJ8fQbi2Bln5((C?X5hRq?IA9%=s=6kCTd#yV5&M#&;;W-aV_ zM<@k)owGQW>=RKlR}+u+ar=4Z;DOu+S<^)$KB)!Pcp7%YpXhp^B1puF66FWTgzKsz zGeg;hs`dqSFL|;Hk!dJ^M2_EVfcb;dw#tv)y0KB=`yi=^+nP~iwh<~&0-yunJy54L zxXfeFN|c`X!AMm$Pb?Avmt9#LSE1wM+MHTMh|0NxT^`X=fbT0+q#Y878q#KV^R~R4 zR$v&ptF!cOU*aXyNQ7&UdF&;3cdH{auAS6=RVdRe*J~@T#h|*9c?p>F)Mv0xOhS{Uf|d1s)a6lMArue$TICDu^_V?WoCYRBJ~6uG{J%6gEIq8uiu1&mrj@_4Ga1BwB@=?8|lr`eWe=iL;KkP4k$ ztN~ZEK1tbq{86&*2qC-xtRl+0&z4o&AI-lOd~0+h6o9`SoqakN9=Q%HT`H%yTkCoQ zF5Z^|R4S2r*LeQ`i_#G=Aj6+Ir(to@Z5wSGshaoo70y8xV0Wq&(WyY{tSlGjD`VZ^ z!J|L{i0k0lP1pu2i>B&dmy9mCxjyS zH|PmV62|?7GGH=|uV*dB*VN;U228d`kg|3z(YXZfC7cc+r3yrpN3&prvtt$h8v+U+ zQ^j)iPa>epQh-}5#e6Q4KO*6U2+(CJ+jWdbnsKW8xXSW0DpzHsYDH353N3hC13i_8+yE8K07f2=52QJiMg{nZVCO9?4 zj(M@88!VvmcUvT8GClup_qCylW*dX>oIt1v0I#d~T*@funZt0% z8TT9bd@`g^Smbg|4$nY3qK@6#PF~Z`UPc4;tf^6R0yRqMXvSOT>>mX|$LdciqagBB zu)2V*zP39o_q-K~2v~xPU5U;7=_iet6iH>_(XQ4KQ+hj|Wz6`o=U_BL4q0~wlL@J+DT#l=$VEqK zVztul%hSq7y{=gLkO!&&;2t>)j719L7h5ROR`p9Hc|lMW)6(R}-E+XgeN(L_OF}bO z^mb)D#GrOMQ}fNcWum&XT5AfBV%utR$OO@(o=RG-){<)r&EyYjfO+q5Q z2BKtwcHp=E)9avw<4AJIpQ}N^NzgrEf}3|YT3QEMN$lq&)RyHRSq0EUJn??4Vj5sI zDZ^vbIjJ1wjfv!a!pKo# z&JhW_x0LFczu?alGw~tP`Z*hOGDU=kplcKiJwXg#RHc4@M8&Eu@+3FGzi9UXMemUu zYN`cvWA+h;GCe16%zm=3$a36%XM^Qi1F~w#TXyYPeZu*bNDPZk>UYDs9QZbG)f5g+ zue~+I$Q+&>e!wnO5=j#GX)q}t2`;=aD>H>D!7Rj2T6W2YM-RM6Y1v_HVn4RvdLpH) z1=E%>OhfQsMH#HJsod8)XJJT_JZM{NZ<1;z-)P@NKtx>~)#He!OX={3lJ)>}#{w6l zbZ)H*Ek<5~4gHDJVCoau!=GtlCxJMO1hBVI0+`zZqM!EM;nRfa5t2aclnWH~%g1|X zDK~Ps6zzH_GCa!YJx4eG={rG+svG)&N|o3D^B1{=#S^KKbXb!^20;(3l&`1#ulX*K zh3S&C8gTzdkDy$}XMYs)hFD>6-6udrL}L)372hj}92#cnE^r;vxLajaY|UR^&_$E6oLAoIzpkdW~`8(JNWxfyqo6D6#Ek>jvT z80?gRZ)x>|T-Lm+9kpI2D!s|Q8~S2;wY=DjSmjz6 zImF$4+%Hn+SmW<}&wBjls( zHev46#e9i0PGEsE7k#IwT=E&C&|8WK1A)*Zj}}AjV?S+wxVnHL%__$h$L>3i!kkEgNlc%J+!;B1cG~=lf$eo;n(_ z7J(rR{yx`OQY~mBa|3ViB;g2hD{zgRj<@5xl{0(ReqPO&M9jbc%BYQFkn>e1+gy%- ztLNxs5v}ax%u-%d*X~y6wcNSA7+9HudXs7QUMWC)W8z*c@w1fVV3&RUxwZ$NpYY-r zS!f;$_6|F-XmSU@0D#NIhItobGu{$aP6tZNZ7INfa;{;Vs`MtR_q0vK30ksxghnB8WB=@;x9 zm@gso{EPYYL8OjF?I*cVuwrppt<6-kN(Oh$N$64u!$FRt9iqICeV zxbIneI>x!0I=TbOL5w_E*U?48mJ8;vzNp8oySS_4J$kv>(u3zqkvl^vp$u7)wc$d(K?nYu zDO&8+zq=dR5dj>kcb6F~~?Gf0n;tF+%2t>49n?J#74H=hU z8;L^0NXfowIPRKv6+qRBuwyZ5Wg=`)m|e}&{jX+;II>?<#$fyb1M@dX5T`;6Sd zjXYVwKjJSp(-7387o#*BYB<>y`r9D5Q$ zY#mz;UxV*JW%EO!Oqvo4TX&s6BaX6lZ32sR9ToT-(4eOER=Jvir7)MX_`^-{#-B6jBQ{mnNJc}?^&Zy;SaW8>BXhRaF%M4zF zI9SK4odI`C;bWkS*n-P#5^rZIru7>4S`U{p6v6^USBZ$5MvPlw#o8`4?WhPUTI;*X z2}+~)&M+x2DK10AkL%ze1DP^NRADDo)gDbwt89Slyk^v zWtGoIICV4UH0p9NN2}Dx_F9}!$dKdBeTg|Fv{kuuE!loBqmP}$mC*Lc28{&}vl4_A zPNlGDb|nsq05L~YF!%EI$+2v17d#Hd`ZB1y_IHVvyuM~MMA3D5YTy0M?ckiqZLx%(i1yMv=)L$K?IWt}5kbM@FoIP#X#*N-U$ zb?a}E^O#8MvB4crkQ_w+--+qAP1zS<+f;8eyH{%Z%r|MjkmCA%4?Q!wO>wavnA^TxotNbS)axiq3IC3))Ic>C7*&RKy8 zm?@J0Q${}%r%;dsys$meulM6>(Yk zg5372NQqy?7EuJtxDEA2*qdnda^swk#l-q0-F=x=OJ+V=?Jxwzt{By z=gFV+$~qPVYd~~w2U(#rnIWeFz?pQbF!c0gFE)3Z?LK8!jPGqgTU5POMXfF`xEs-D z59;a^X7o}{aMF!dqr zg+Qf@pvE0NH3*OaKh#Wp0;0&3bLl1Gm0l4i^FeAOBLP?g}eUU2~qucwZWfak?s71V9K+ zPjg@-5Fh}gC4D{mThLmZ&&6}@_sK7S*72^X*3+)}weCJQ$?xKc<$@9|Qjj^_yCf0} z0twEA=!}q+0asT^y_okS7}MSUu_7H5d$lJ7K=N-P9IE~i>V>s52&SUH*I9&?10-oWC%nuTfg-P4<~#?fWRuyQ;M zx+COZpO{E1O<6QHpE7@vP-~i2-!xUx7~dO}!>I4&a>?aoY>rF697to;(7zRaJLqL> zd4k7d8$e18irCGekCAJHK+tWV|9%U&P8EzkW4kkR>NK+q2q9xY!SCit_z4)v6U}HT zIR-P@lo*JnHVkKK9j}K0iSzdArU+d*hU5+#(;kZSNkl=$T*1k_&jPQI%D>1j6Do}X zd5B7jJWO+_WuWgXeyN3%G?nZMhlWj>6v=LRn&qK9!h^gG*%yI`N9L0bV^#EbL)Kq3 zJpT>R4$J>Q(GDX60Ta_dK_f9H0w#ukft{4-#hCxR{!0=5y#AjM-T$L9|M~32%r9gZEnLbM$yMYRec2=0y9Q;*gN@REYPgg|zC$5&{+GZf1~ zMOo3-AcruA3BQ3-pF^;R zg}!2G4ubhIT=*snj%Y%FlwQ~*bYkvqACe(iCWK(Lt+6{-%Rw-)-^wtsvUB(R-ZfnA z2_8EL&rNc{Fs6kV;S;u9A~0bA*d*zoTZ}Egy!WEA_(w5^jA(H?-LWA zUGzrpusRh|lC`Z^B*>_RPlZ-IGXOZ{jN{SAbdizii>N7Ef)Ryemrb5tC(_N`f6lmA z0w)oC^#EgzuTy%kyLz(z;hT8&(`gL+ZvDL->t?6`G{>eMC@&96HyuZvqZ#pm^{Zk5 zl0E#Jm6rw#6I^mKj+N@np1anITVdOk@(6Uoe^lQM)zuDc#c7j@+0bIqUvdRZSLRoe=q4g&)e4ilo1K%e z<84K~`?2RQ2(BD3!N(?XECv5VwBqdPJ~4g#DR2vknvEvq5+d*Aa?5PiR@HQ@dbU~Z zDMeA43{M-Q0+@#2r@9afW(MLUZNK}ma7Bg?q^)l2K;r|+Aj#bZ7n9Z6MoTCWQlqGj zO$r$f$dRtF$0af6{wdW~rx)Z}2OTC6Sa z@^l5>F+lyJi#WuVOnZQ1yj0?OZ6g8TaCFsHNz7U^Yr%9P-3rRc?uy~o>>e1FjpvI^ z0p_7EPocR8+po?HHJgoQx%NzIg5)mnsZieaOT0dNMYi%c-*KeLi;nC3^Lck2ZfBms^#pbOu}i!2 z1narjWG;x+XRX2b+4~EEZeXV~GnpPB%3+9DjT&#M#USS(LC#I(^k`=c4SCuj}jbu^dJXv=nvsoN-0QJ@F-oxSvB3jF=qwi}zT&|XkYuW4q z)2HT7@2o$E{k@{<4%U!3gIuK!alA`i9!w?jjR5Vp<-eY928W+mwBUGJxNa&}-qd@? zFDg`1%1drkpG`G}yGtyp^D#*7A2OM_SK)2JxI1O$W1Cft)X>$^QLo>W_}+9fM!4%N zR1EO6fgsEwbg2B=^^5>VWU`fYQ5}y9%HEqR+!-F`KRkOc*@eCcvZv7Y%c%^f+GCuN z7!JAS?#)ZDx%n|N9K$SC)N1E7Cc_w6EKCFx47Z4FC?_pz9QAj8d7s)Sm@P6-eg9-D42IkxwtafXYA%+b0&}Uo-!au) zBM9m*5KW97w)k>IIC_85Bam5YRLbvC-F)Fw0)A$kE-7+gm<8SA2*5L(MBTV-`zne! zq~ZdPME~-P>Z)AIU-0KG!Kuk_K~lQp8@-D|M3+QE2~uSZgyqN>{e4?MSER0H0+>J1x_TAgNl>ht$StD z`vgzbQBT?sm?;Zx&7cAm?LdosMx+s(Z+Bcn!x)aC*eUa!{>B?64FA1Hu|xC5pP;!< zLo-%N+((P}rBI4pLf##ZLc%3%KM_;&TUldlcq+O|~UtYIBZ3S(J z9`WFMAho37xYtU!s+}k~ybKnwm?=gy#{UfDa||ShOA-r;qyx)fBSUlgEZn$Omlzp& zA;S8Rk4B+?5V#_byJg$&DBHztS}1tk3>RJWab|5dIo&CNus=z6x)!Xp4h|Ua5N7@} zl5~)v9VaU~tpTyRdZ`^5g}I_uUpMwPlff^ zf@T~Ks9h{{rOm6Qzdp+(E4%+Qb9U93#jJU{WM5QUn4X{?;A z4@Vg7qnKDeL~T4C!Qm~awvW=bp)r&M=x7|;{u2ZyvrV-v?Ac!JmtF8mE+T=zW4|G= zVJfar%xBOeI&L~ksu(%{G~0J_M?_;Kv5-+Rs@bGp`?2W+?89`oh$%xY=EgmNeAQM8 z9G;6#_NzU=ZmX6d8es0@-_4|dAzc4AW>VIFI@bPAwcYvteV7h&nU|xGR-EKoIE7acaA?8+hKx%}& z0#DPXs}n)`C!Wj|W=XL#?BWgdkL1X5P`x1(5T{)Zs0WSrk~6; zjnSfY03M+(V&Q_noJwQ8?R$r<<*Y{GsSPmOkn28Q5LJGkJX2CwbkNSxpMYE2m-l+)^7p^4LmB)`r|;ojm(UJd0+~YBU*8X4sqU6$;{5ZzaXcFMbw3irJK)g zL`s+I%zu9l~C4?4|51fHbgk!oqU8Pe1ep@4`f>}Jr;@rJ4`@L zcjN@PeBL1x-1SoG+G#hAzRo#DvvZtmBIThhD)D4p_Cf_P6jU6Yu8-iv9{!g~hQV$i z@xVL=>hE3|rB6#woB&65-6Rm2QUevSpho{%qU+q41FFuHV@JR@^A6GRH@}ADimHTl!m>rZwMS4vtDMrL6!F# zTx;fB<;?bJF#Si+VMDTq@tV6n34juv6fN|fyNvATt!U^1dN$Cp3pm5yH+V=DZIjG{ zOhRE=^L(kEkWuy@Npg+k9gWOF6Nm&h%241oKz!AVya~;w^J#gy76LAAIbL;UYC{hD zKf?JbNMohjQV^D4Z|~Kx4({VA5fi(X=j{zW|E5#1{)=qkU;np%8fX7eKm22_{BNlr zSpOlI_#Yct`FB*t{~zv}|ER`)+Qb+c{%V0S#l3-vEX-wg!|}Y^r6!51z}HDkp0O6_ zorN@MWuPYEhqV@ZJDd%=fZR1++7a6c^OHRHn?G_ z_S3cw{ysS=YD|{aDKSPB!4g2vqrhEB*`tYrjB8ioJGpgSr|!(2vE}X@t<*I0gy=0d zvrTUt{OoD2kvdk zFWRwp>tdjBqNxssl`OJP7L>d~haWVPP!vZ(1rg#-YMN**W%9#Jh}eyV+!N{XMYb7@ zoB(ip@N2SV(U~N;wzvwtRn@H*z=$$@v)S;}7sAiZaWTw1er!~%^Q=NT3!Pw_j>N^u zoSb~|^Qw6F%<2d^fSw5Z2-*sKDbPyHg0b6lzjAqGo30^b60lIhwxr0xnQWWDJo^UF zRqLKouu52JIl1^>WCmmbZn5JN2aJq?7uZTfp#;Q8SHs-d#su2+xoc~pkC7v3SwK)g z)Urw%j)xm5038XGM%Ey``ef;X%5Jj6!4)oTm{+$#UR1?%5_$8gSkg1&sn^LoP|=vHtYY%p;yX`w(RxR z>7p&f;mgp2xHD(8mDeG-|2im)PiynegQzTwMi}!ai?VTaMP&)-$}P-~Hf#&0xsjKI zA~!Leu;AJQQ?&XWdGrZVIYIs>DgL78Q90NqRgB45!$D&IJB-|`UX@KvGK#WI47oNS z@KAak*bTQ%Mi;iRs_lrwSs4sIjB@hE#2vg4^wAc&J4ZJu7HhmZOnF2`Z@rVxvgdvU zFQaz_Tv?x>Rv`AKFKGgF3#`-4bezTc zpHqb$pxkTfFFNOeXAWwPJQYX%18KmJ6bfM}E}wXy)^8f5`DAm_1mfK5n8`+jyQv=G znB=~#NK6;BLYT4Y^$}o!$pHmh0xf1678BR%Lz%KwK>Yaz7K`6x#|}=F!QyXEiImvr zaPNane;_y-h^@VoTh9vywW2!HA7cIf*Z>f0CE+*=+G8{$Ly&>lDv99kU#QYGapcDh zjr3xsP=D8x|3WAK4->NgopaC5@*i{VQ}YTt37HM#D8k}5oe@jgNY{?^ogw9Efm(-D zJI0{&Y6XD%uBzGKH(c(Nu0A0d8nSs2@lgbwo)F%e&nN+7u4#VZoxM$o47LxdD?QB? zz$e%4l+oY*{vr8Th-1q_UB@VWf>yPva`D#W8;Ab}AcpB} zDSq4T#sGrHbU=U-DU0&7zaKCPbq#7iQ|br_F}eh}D}Du%$b%?Wywm>gYVnt}@P7+- zVE^Y8=znLh{udx#>Hi*2{}ku3{)LwCH{X|xiLIHlIRPUBJLkXC>vs&DQ^C*($=WtpOoWWflUFD5&Xn23`ls-(=A=2Z zP}f>f^m07o*rrdTB?iV^JD!K-Z z-3mGbpzlQt@E8()r7H3ms{gKxy?x7oF-rc;@Rq3`j1f9xQV6CEyD?e6id|^Z&bl!LT$M-2B2??+$s%%`Ip|IU#ncx=hj7&vrjGnUI}VCgAtyDp0#?tRMc8E=xG^#j(_I3@q>X=I+$s0C)Mvn$P%IW z7Tk-D$CH{*SP<1j$Hsw*CB~o?WzY?AW+j5nDhzWXxeB{)XaFpYs;oZ&CGfm%jIu@% z|KaP!K*0yQ*GKQ1AYSZr#(a7}#F8bTejI1^#cyb;3MT{sNS<*Ow|$zm{qheeKNix3mtaoD(008H_2D5cTxwFBHc>HQf9<4{mPFA$N}vgwx3 zxofPfcD`!IyCJXqA=VB?Wqwz1t<+fI>#v*NMD$4$0?CuuF$FhaMg!x4ryrktWOo0Q2D`BhtJ0Vgx+Ibt`x5O3LrkO>F>0H>tL zhmM9za44@Ecy<;0&Sft_BtBhjk^tEJ--%oY@zlPBkrH-Nfr+xQaK4%Om~Y$Qbh=T*I-HF+o1iI zlp76LCxm_Bu_KgIpk9G95N(QX`W0+3+5$>R692BF|9^Wr|1(;FmHFSI6__~K{v2=Y ze=kNK`?2F;VLe^vEnuX=E5?+@vK{4RI?*l>C>0K>$a5-%Md}g8f$2=P@|l< z5g1(7lcj7G!E!iVP8mhChu|JBX&N`{IXm?Kd6%Zbd0Q%$DDg#p%HV;Ufp4!wNA2gs6v-6J?D0+RifQ z;FEf#6gp0=;(0i*AZ(Fh&w`6zs!vX}^56&z{mb7K|9=lGW&Ixrmi`4G{V!ci(W}cF zTACO+^YGA1G7|hVG?VYoL$74#Y~XA{!0^vcP{F|LFFN;s6z0FM>2dxgNMU5WwY+;- zPdGTkL`ACW@{Wv|Uf&bZsyuhFFCWZ3UN^Ic+0R}hQsbtnv4aCLCQyk5v;MPBmO*P_ zx6TUOSRgKbO;&^K_i!a`u8xKu^D0Rc66M}d=BZV^U8FkVnD7S97Z0pC8`hNXxrmeM z;5MqSap1^}RKsqoLf_*$j+las!!OB{--jBOaGTAkP=McU5t{cu7U4?EFz3IYN1@<~ z&Q<(u<@a^7&XI2mB6PjOzd8hAeZ8GDn$A8~5>}+~KPf6E{xnt8<%=;Aw_T!kF93#BcaH6^R>;|L(a zlsw3GjBk$$FY0#-FGVaNn$xihx})meliOQC&#(aC^#mOl2VGECntSyuOUN}|im=aS z6-Hi}j(%m-A8&tCd;f@;LaKI);?>M38|$@@4v|IAK-PkMfu~U=kV)5DysivH&Xnrv z4*0MY+Cv-t!6Gz%>yi+`YdcUZjc1a$B(pjWCgwq#eRB@KCivhB$fj zl~t^dr1<+3oYH$7HYDEPd+L-02snaWIsjlumovtTfqv+O7JwUx%C1OL>0Uypuz@N@4jautjdO-N%UDK`f_ABQ50w7ZQ$Yk#k*`1cXqgM88tn6F zanjMPEDmA|w(gBdu3y@!P{}wuvr;A+JNO0z+;1}rDW!|X^~^DateAq zDNehC<;sUL2Yt7LgvXT^|KlJ=o5uFI9fhuMC2CfN>l!w?y`p#0MllxC64^zmoG}-I zPVA3{IC6#+uKGk1ffWTB(-;tEjp|IMCVf>kF9w>vKAt*p8@v^@<@|J|=m!}!w4%e1 zjEA4clW_6$U?(YL+i~#)L4AWx*vGgijBs$Mty`3gB64bP)tP5QF@E*O*Zg2ly_l7| z>D5m4d2su;34F=G*h*|5uoV}<=Dg!~-=XvYi32UiI4>|O5D)G;&hK0#s~MIq-v-?` z|G(?+e+3>aoShU*9EI&{?Coq#Y@G=>=!Na9?HrZu4UA0aMNC{Rj7*foh3Nlv1rQNd z5iv2cGd7`@FtIQ*cP3zCWcjy4n2m{pnT1~2#n9Qq-h^J#=1&j&XE*%Uo=LA}Vf>Gk zf#okN!(|O8xi-%=Hd#2;) zC{9y;UK%r(q8KFCc_+||_9Uum<()3F0X?=CJ!I)zlOPR9eh=_i{E=GU4)p_j%Kc`j z-|sKpr-DO3Z(p1^wpnDf^TXIJMIqD`s3U;~CFaPuo>dCbOVPixQSjGG6Tf{*PLlZG1>9=P92Nq)I5o^w zoV059K2xXSLNy_k5Y~Ty0{|Mh`v1<1N*=|utyS>h^g26rXzf(b&>Vis<+IhwzGaBl zl6p%3=x2RL>(byRUks@5StN6Wz3R?9j*8T{L{KAuMDKWRom5|k*eixCAWxzRdoQZ> zez)}m^8RUXtgHrx6EoGaLkWP+jR-U5?(Pbh%f?Nf8Ilg8N$Rz|s*sADE zL!GBiBn-JNZdTbVM&r(?l_^-;#H896`1e}jSePBQ)R{U-3nZYm_?F28sBCcr$^V27 zI|fXnpHIhUV*u}dkn@AaT`?C~4oEldXI+iyWOf++QI%mQ6}r?~1E=ceOZI`024CQX zd4K%qaio2?qwME0QRJw}*$EMyn0h7?NZhVH8`@&~4X6f9S1{m&NQ=ZI#$c1w~`y=i9=~32MJIoi>gHJ=4M$P{aAy zS!lNI@Wtx_sWjiAZ=kYoA}cC7e}x=A^O0|@N${P=&WUI(&8z{qk8~R|o^FjOsIS&O z&@oZ$OmT2pO?nA5H=ivm5tAF{HQAHu9{I(YTXIG;HK!5z`(-$loa|tWU|tiXiSo@;WfTXkAjV) zGdIY?V7+)pkZVVP0=1`fyCjQp2{SG_vZTwl3cH_L%ay3SiI${GSDwy@t%;Xws&ZX7 zH4(WHt8~%R*CjiuH*{22Em6iBqdscfOa^hH9;R`X#7Ys5lc~kQl5RZ;Hm`P&@{`~= zAnr@MdB*ArY3?(9$|`L*H`#RPE5<-7L`3yKplsP#rr`z5r;`@w!0VJ|#g}pwowUT` z)g(o+{=%AbJy_@D>GY*=!#HD_MP4X*gO zlyA0PkW_b8*3$)J8&Ubg{>^^g^+~Wcs4BVYUUndvybUd}d}fBm1Yb&)ql5qlH^h-I z^HK@}3h6A7ne!an8Z#?7?wi)oWOnnfcf{2yI)a@KKCgVGrK+(*YFOrBDCtoP;-Yar ztnx(*`eTTSrI#nAjD!KieQ`oOuoOekSU>jIRut`Jaxil2I&!0MQQX~GU1I?2B!3hJ z^@cn9q~8AGgX#nf{OH^qx6Q)i%TAlw)9&+=3lLc3QlNHnM3J$4>_$i(NFjHWv7>qS zKY<)gqmbK3Dl$x+5`)dtw_T?}rdk|^urrBjNA%!6S)Ku(uKuL2)I(7jFr+_3L~hBZ z;R!k^{QGjfwPjzA+nCL4@*XOMa{ds>9{SrS!d`IZF=r-m5uUK|@gKECK6B;Lxtc_5*uM zqGkBn8s?{=W|cdY5LFv3Q5(p%{XanB*T@~sgX@I1(o*;c{-}Gv&^`vp8uyQmKvBcH zp=PE|zP!{6Ih$E-fbCqM#_Obl`!9}*Q3@)i0sMKNu0R9zjnao4-qzb4W4?}bADXQs z*xg#1?ISkLHly1(drVW8$vrp))8_=5z#`W2(nFvj3xtvIiV1`If}CaTTXmJLEAwxl z_3Iqkdp{{01emVV{pV59UgHec_U@8(_GiYY1*uHL?)B{*u(e<|O=lLU{J@lZ{%XrPD1tdH( z$+xF)vFAngKu5kbd7Wp|G=;AG=#i)_`M8--pE$ zx8Z6;tRpvf;0&n;E2Wy`4d3$}d+mmczH!`>Z zGXBdX$v*ffpC3qFsd&vBJPCFmF67{HJ5hHE8eLWAw2H-=vZp>RS#lAg64IfQ7$ZZO zXk(604y=}yA({Tb*M0QW(7cq;9%4Z;u#NWslBHj1X)rR3w<7pR0U4gqAeE!zF4iu5 zPyAdFxJ7n~CerWP=!hqZOUwWV@qYZ9sWfHxzVBI<4BD?y5VE55-7AMVWEC$yi14|Z)w|0wP;E-klFts>;Y7C$|c%{D`h-N6tI_rGqTjW zt^n89m5f!so*0Eo9=v^%Nw$~h(LguJKb~kc0(maGpd}4H^u~{On~$pCjdN`~)Jz)I z)F(=M2}m(MCi3A&t<{H5z4SV7h2S_o&4jJ1b>wK?oJe`rvLVgHct%IA%Qc!n%os;W zN`g_vIjwZ&kVmK~)1yS5))@3VT@x`A*FG2-Nk!S&aEajT9kZ~_)2t!GDb@sYL~!`$ z)(Uga<;gbs{%EJrqTn)~H42&ZH_dXJspc(x)>DZDX*6+V3~Vo|&rkaU1GnEvX7Y4-o>ECf7Yh3T^#y*aV@+eIiOh9}m z_dDnGE@xf6#ZzV34Q?%|!)jb!?`%tU z_e`XCj2D_bm0mX0&KK;Z-gemzKBklU;0)Hw%$x;4!DlY4L$k9g{I{K;nx=Wm??r{J zprh19Md3({avtywSJ;!(&CMx4amh=1)v)Hl%vr|t*9|7-Uq^4&7Dx;0V^NzM2;ir= zx(Jbb^>tk`V*=JSO~G7^6463snEt&3jTB!2K8wP$udJkMkpQ;Yt4an;L&r7^u{K4J z`~1y=d~_?!>7mlTBsqZRm&&FgDG6d9xDEi6_uc3+zk7g^SMV9XpVyQW_Thy^K+k5A zpYqzB7G<_cg8ZSstYj2)=+|`Zmefp(hK0L=42mA4;p)^*>8DWh{@Z531uG;r5Y}^j zYNHO%XX5FLX__2dQR1|;NxfzF2tT)R&YUY*^`3X~+{oclVX+6@?w+dY!QI|G>YB80 z`gPPU*jXAxEX85i=&4wE{uy%D58e9ipIwXRJ$%si8i{3`cgJ$ zfBqS?HSty`7rktHL2Zg|qE*PsBEFs?!WdxN@irQB7(0DVPsEJpNGSN^UP^`yY=~bc z6Y=6JP+8j+i}Vk#Q!9ZHAEyQ|i4pIri7C`}j+2mW9gS&x=p?rz;(lv};>ln~KwFiA z?IAU2xUaZs` z9mEOKtzSryuKi$jqw6~aG+daj@*2Q4Jc0gZzA#oMY(ljf7xlk&wXp>3U1~7*>G54E zRHI6qKKH>)=tZ3puQR7bq87pAtdlxrd(gKi&-QFa%TF5S#AKKn^VOSujd)jPa&z?w zt(qq=&OFl&i>cYHvS$TOscJP=4k=!_jYDUq=58M3%7e8)tljI$P`#Vr_Mme>*ut8% zcH&&*KIyM(godP__IN`2#*){;`fn(~>l4MtW1qeIDfTVxaykw#^{p-kznJHan9Tr& z2yO82Krnf~ldsD-8_az>RNu{q1Rzap0II`d!Ytv4B=H3*?4B7BdJlS;HDgaBP|_ZN z&ycr5N2s8?QaJ9e_E+Dn84H-}rl9ciiKw;cLCp*wZeyQXr8e{A1NPd-VH9=pqBHglcoMR9aNv7ZdZC`u>s;RkZ-D8;;Iv5FH9!lBDFD ziZt}g$)J@!qRkvqtZ5I}eHUtYm4$V#gBwlL3or|)&=C|IcPVB|x}G_>Uj&f~BVpFKqBT-YR-D_`{NBz!$o9HJg;p)u`84kqH3*hR)#z``ipjC1Y z$#H3UO^BbzFd@$M^KstkpTiJm4S~o>N9s$q2t3?R*7VStn5zXO0fWwn4B!WKBl^!& zR|Wlp5JqzM`mCg<)ZA7D+K9T(ik#zOdW+SY5&SiMOIE19a>#mzx9A!#M;V#ssg+o< z#&UrwA<-l68Vi+#xI)v~Q8^@`{{yR=?ODk+#ZTjVc92e~jY+2wK3Wn%!Mub~>`{`Y zi8tVobDw?G#5PA<->`Ah2Bduhyn9Upq~zR`litkR^(lGx=-p$QLf8O&<@JPwjcA6- z&*IPkbIG(TkqF@zSaw(E3(V{opsLRSn;|kWenPXb7lIP%7evF2WTVTQs-4wuJ_qtV zKt$otdkp-#*CO^_gG^aG&Y<9&rxf&h z2_lWvZiaW}RC#$U2z19@)-CN@pt*u_LC|L9?BUDhi=5ya%%apTd$UA7^c5QGQj@xy z_w(}P;G@<5bl7wE?cZRcl=TOS`If(|EpwM>9cZYb4Q(cmbGGt3aD;Ln2I)cI+XC>q zbAt=xX@}7igZyff3kWqs)gg)CFK1wdWxnhA0YjD-qdyX~k``M6vr16&!5eu1kjTfi3tx#kFs;9fDlGUwgb)$$&LyswtY|9q4yg%L>2z%(bWjiQ-!40_Z5QN$cvxa| zBA6;-)XuHl?bvKOhyY9mCw<=OwOlpAl0q~ccOy|GR0jTru}hh@s$ zZn#9%qozt3$TkeLOTJgY-Acw|v_}(*G1w))ysx3j{rj*um0=!2I4c}Rpw47Hce}$u zZkW1ZfC32gRNA7^?!$R$@XD+cWUn+bML9Qt(1<)ajPN<8st#57MBy_?0H*_lC7D;e z(OPEb7qW>6MO(y^L*Wh(;y_d>#K1Ni0x0d|o`hAVqC%KiKFuE@-4T;cA>gR(NcgD# zyhGCkg|SxlEcq&}bDgO&CVdAygL|S^<09F$0fw*d?c)}p_5Z)8`@JRrZA1H?A?VYi&&p<2z;fd~A}yTQ z7ZUEfQw2G|+S4C)ZzhyIrOJ_Px# z^4`=0byGnc%^_Xy|vVouoaeF%% zD{i!(_)l_|ijBCnJ>)&}Sf5TCg9rlzcI{Y5z5c(FKPbWZYhMEiWY-EkI>KecN&dujdEG#v%a z0JxQ+*%V8UMaS1&W!w+u*_-7j1+ja?%zElnzqu9Kwf*TumAYV`I=Z~Zu{_G`F=#j_ zrEG4<*oZz>y977p;$!to&Cy)oKzW}sCbJ^i%62piz-#k#rNEFmN}a~JWzokf_#IV| z>V1$`xY35h@lqZd82z0=W=hH3Nd$AEPor-C(RWU?!758%T3smDE9j(kLwnJYgXwhd zfX{_l)*!lhRNhty|C^(om$h7)(UAJ66FX0*1OUAU;zs#IeVQneH|k5X^u6;JHK+m8 zFSa0~@=V3sv-RK)?24=?N3@vKYZg@}cLp2xI2Vg<(vDYAwmqWK20@rbm+>?cOSk z-CXh~QC;uPwPRZd5A?!4Zx9lXa=3{i(LhlEx=1AOHzMRh9v)F6-=1+;Ueh~h(G7a{ zz1FKWD%RAt{)~FO8ie=y3WZcBtM2uDF;Fm$x^b2=6scYfw7@MnUET*Sn5*a~y|M{^Z85;_7 zpK1I!2TD5dD}rxl{Z+K$VA?PFcO?z;#K*divw*9}+f8rgWz22bO&^7=C8Z!#Pay7T z*Mz`Vo4_&H3osnSC)N=fZ{>wzSv16OSALCoAy=#;d*mHiT)A6*I=?DLpaML`HuaR6 zgtx35C=~F#ES++7sCZj@)pfz<-_|)^r~dvAq}dA>dj+!otw;=l%@oe!3-`qfg=|_K zv%;Z7=T5jx^SoJrvnWmVL1S;nC=OLhQ7i#_RZ$A^TKiE{aCC?!UJ}^%@_m9PH9+UY zT9W$&2qao3O4NT8oW!m1{ra%pcN68Hj?-t*z? zbjlgh8{Xl}UbsH#yk1gfoU-+8PbQ7)&uOYb&oQL`3cg~>^Fmf+#?^uee+sA6ZIG$D zsE?L}WoRy@juC+sEaC4z(Q}&t@Ej}T1#2WbWI#^(70U080^WMr zxB^MIc%jLM<`8;Oy(wr`qDojskiyJNy8){dhrI20lZP75j~+3rU`4P2J9W%AFl_}L_teM^#cXdBD<-VQ}O2jZ& zWHe-Z4qG|of68It;eVv6m6%rg7`kjBd5B(GjKYw)1T^6WAqLP*1INF9o_||lA#PukrvbF*MFxGO zF9DVKsvAO*R239Zgyfr9q$&%(JKKwq5T2jQ3trrHO0eCH)fl+Y)C{#!SD^p%jU+Q7 zjtWSEQUiFofYaHn;gspj#v#e%pLIq88@jzZ__eL)AZ?Y&NwrCI&bKDOv z|C2sJ@@~|{YFf~a5H7T8;*vfZG-%OO`-YclI43n9K{MLH=0m-YR_6OSoJ#$j)LW`( zk{VO%lZA4^n5dvsO#8@l!NC~MKdSa2Iw^P9!$SsD+|A*f%=|D13$i)NVadye`<+*d zI!E6V#e3M@r?aaj8JxjlH$9Ysty0NGqdsNxbSn9XWyMRDhl`-rNf0?~i= ztLVTxcRfnCs9!ePvqQF9PzhGHwJK=+(XOj&SM?3402V&ujaZ|NpKCVL6Bnmcs?^6Y zN7G<#)PXxJa_~S?1GIdoPwq!y4r^=$GL@Ql8WoqGdyd}lG3~eoHrFuG7@|!M`@WS|CdD#k7SZIr zqDHHN{tJ*@um7+&te(0XiI4%?1DY>TvD%OO_Rq6`9h$#68I#(Z$|djnP51thXpuCP z(zhF8Vp+$bdE1{j2`=j1SYuf%6M{lnyZd264M;eL)T>5GpvwBffyA{afUI@rQj0`Cd*!Hxbs6w0Une8)b}$+pVhAQX8q>@~H3v7oR&dU{t$-V~;U{ty z@MdD6%m~#DJ(0|_hVBU8w=21fP7MzJy(05C?L(k)z36C#~crqBk&`!iXW&>yVwQ`dOuoJ)zqk`uqhj}#?%TQ^~f`@5IP$z;8{A76vGC;Jpv6=bhg@n}g} zpBpFf`iBy0&cuQZ8SqYkDi@25x;+7oTmB8huga~?#U634DOyHG!l{7*wbhtKXX?|F zBAAH)M_N55ID_X!q;?wJ(een8I3ag{kRyDLzl&1E+%gma5uZ9_L~$pBimPM}S-sO;wR?=g>lA}W|#y!f?&kQv;fr(Faz-Olpu)#bl4 z&e}hq%T}@9d;GKhIAsg3TIURfRj}r`O#)^KbLiadhsy=N^sd`~Pvsc5{Jw&&teIN_ znopT_%z}ShRU=glE0T}G}EB_DiO+NOZYTUy{gD|35^s>)lCEPb zYu}xgxI^mZPBjybTj*~Sfbm|s!MP1wm zs$l*RnJgJNN4Dh6O{^Kl+SM!ZZ6-9Ozr(((GbGKNgEM3MNboUYDgj z^#1G^CSPpKgvZ8S>1ARZq$U~bJ$FsLH6O2>p)RD0S~TZGDF20*VjA8JsA3j``K;cc zBBibofx7EHF4GjNFjp6LP}pxc&ifkQ;DFY~Ux1xu2F3TZSYc4aCh65?BEm8ju;8O$N&COcRY zlT2O9ZIE$D@r|&)*ZO{Ott<)G!G0$nqLg`f@jl)4&TvBjtH1%J7s;9|m)e5?-O?%s zn!*}l7}o=?=vIuK@0Y-t9sO!6;!89sV&IR((Vq-Wv>*q8345Fd^sG>jbxjPtRmFNd z=HaR}&NG;a{G+!L;T@(Qgp(Nd1T<1h%hVpuBm2jiyJiaFOr(N@VY3zT)T~vC zv8$eOKH9U=JwF>bYzc^yB)>1Z4dv}dm9p?tGq?RLW0BfO@#s_Hm(okq@n1hN_Fr#t zDWODyG;sY!*21#smc-U9lOO4>G8U)Z;1LKkK<>r#R>>?74 z=D4GRbdW!&c*=c=cV=sY*JgM+)6r;hf>w5^zPm78S3kAV51jy3HFN5T*RnetYNVp5 zM};vF;(3Q8xx%?TT#OSRl_849W$przvawLa9+wl8MkZU**^&M4kfjDQ1=?w(Wx&Y; z4-eCyWSco2X|oF}_FTwEc?AQd=_qv8k42!u;18!0?kuJw9bDF^vU=T3Xw0%%VxnIRe{32{2+!~ zby~5Y&7t&fQ;0;v-3JRSwqY}SiUDYR4P)uge$hS@utjH}oKhy*A zf+a^A>Idbfi$3U<1_|7-Q8F;*>&o7y&Q2zSu$fKonXkgJJYPQ{Er*H9lp<8}_vZ@T zDF_aqd!fA6QNf=AVO*5boC;;l(`6txaT1nf?KFEwJ=>{V)+D*AKFr+1^K zEY2Fy_GHS|C`QfDUlv|s!ExG@S`>#@HT5nnjf7S)VbwdR0>aOoaX5dns5P;+hd<4GE&&^n2 zd3y33=RpwOeF{NMl{PD_7A@TEEaSXTh8Gh%^$~QHZZz9tc7rvpmj2wHRl5e634Z2iDws5&vJa*CQz6n8yA#vRC-y)i}`;&<;cVof^NXE zmtt{Voi3z6E!^+%9mH3|pIH0I&E~TuLw?Lmj(y}3nJ?ZpL`0JkIt)8K?s%C;;};X3 z)ljr?%zJbW-pB(dYSw^EKOkg|Trru5Luj0^0im?QAgxAf1^w3(nr2c!l=~2R@<9YE zKR&-&)5}^T?fiiYxnq_b!5D6n(ZtfsAI9`*AW*hNf~iBq9YJ~iUXu2Fj#i%Vwj`Ir z?Jdk-8JJCM;bu2duVX$~jA9^zv&~w?{9RBYM4K<+B zm@hPi1UcRRJiq*hLG{Cy#veP=1ogTmIpPU0mPFZQVNYPwo@nR~r6HB95o%XYdth)W zI?Pv!TmQxHw}Osjc!qcZLY#<3nBUF~o89fcg+!iUqo<}boYEyltu}+(P7eBEr?nC;^u$=u(&1ieXaH+Kl9#7X4!SkbkC+5|#(Zv2W;Sn+2#0ebq#|}{sA2aQqB2KGg7%m)GD?C( zj(0EB9;|ox7tCi4rZzEwLN?W=bF%dtV1{F;U_2CJc6q>})QTQoofsuC80PjOae2y4 z<=!JJJTnuG$nKU1%_@HONC=bz#ijZ4C@D6&s&JlGK2j<2JH?8);O+|05_gn}w6@=m z0P8O&I0+Z`8QS);>y06k2(R_ zC1EQcUcKA@-24b_isa!vs4S&tqLF&b_g(VTx5*2Ip8DG)fmC{CA{u2rK)~KvoG7o_ zX^!Jl?oEi(So|O~ymW^iWB5B44Y^e!i1xj594g{BiqG>bLRk*1PK3h_l9=|z`%#(j zxY;eN$H8`sgL{6i27i&>frZ20{+bd{7XSSrMM;~;qt3meNkm9)S;X@|>>fXfcByp@ z0eIir5rwg^!hds)_Mgg2n2=q4-(Jhhc9AFy&S@SW6&kEg(Pg4l#|S22JmT)Z{2%$+ zuau9M-I0#;hV6MON(-in9Waaq-$n!NkYJJ_RB*_fyj>Ug?Gqhwd{^nYsp=q#$L0W( zIvFcR3HMtf0=3pukV*DI63i%RGdyH(!RNE=UF~Lw{%_lKixttTG#j%qVt+nCXs5^` zd>2OYZHhU~do>|SW3QsU)Dfc2Y}PNFsZE0NeWwVm{F^Ziy)3Er>(a!qv46dOsC70MEZSdpd>#IlkNcc5!x2I zq*l*{83|Tc#A)CAb>u_j7hu5fW*~mK7_JHt18hRSK^h)xi(Otq)1TNPxF*1iBv47d zdu@aTN)JoK06obtAOS$%@cH|rvytu2;%&GAAS zxpOk;DkT8t`LlbKIVHUc=9YO;w-eaSs*lj&94a*r@t5Y1{Q)}+i4%)z-c>Tq(4-K1 zFQd&5secq1^F;dj+G*ANEj=7YMuC{pOOC)7+DLiX71>@=U$Btw%dO)qfkR;)XnltP^C8+K2b|Fyz1h<|S%S z?x?g;tUXL4GN7-Pw_Wmid99EAbulC%H?(s1Kj2>!-P%yNYxj>&U}NH_ix7FO?nA%L zRVC5Hh!z)@F(TG$+e*L=@1!H&B@l`(CW~{w{}Y&INJKZFn_|>uTIvUD*}Q~*F+#Xf zr&nK-o`;abD1-v7QC2`S{AEh(+;(l=YWjS_EsDPP6*JA6#jVP2_KaqfvV z#3mP+Sza&1ONc==BWi2=ym6iI7^S6YKlqD!*FZA>n#72PDF<f>S_^Wnj+@s@VOw(&;10<4B4aH~)wj zv`!xqYPVznw6@RPslNVG%*~cf5`Nj4A#wVAkR%qNh+aA4?Av0C(0hnS157vbt`CPi zCLPou_IfyRKcT$m(~1NkUUkJF*UxpPRH{LyH>z$tXrwN|wo8Ntg|o#m5K#3}(w3D~ zr2@%o#YT>`m!ddB+zL#uVQz?`fHz5NezX2gmV`GmJRvOl2=cY-V979(xRIhtyuF%v z2q*sXAHPksQih(&L-!9RVDP^|ZThy2zn)XYP{1|i3>Lo+{xFTO=gzakDmBt=AS1`3L9jnPkh9W3vp+4e(cgf!K({&@xxkNywfo zR}_}qG;__X)V#K4sm0adg~22QwuvAta8l0ZCo<UVh@(~GSDTLCYAV(G}k!ljk z&GYuRJ8Rwhrb5nC*i4KsR5nIYlKFJ|3jk}<{YI`&8qT|Tko*apCs1j@Or=FHa}mf| zYZm@Soj(5Risfda-$?^E6@<6Z_IbpmFmM;2N^ql zt0x>~D*TK6RQDivpt(W7ctN)ZZqE80JIH>h$LXicBZ*x)l}R|gqE<^oUCH1vMiW5ouZZAqYpNSSsmOrH4I+*l{OF^R z{6@Duf?7zmz-+6u(-EoXK8=f*(VTdpmMhk%Z+Fkde@!%h9$pRsMVnT8o-RrI1}tP8EbYHfA0(m>JC43nFsqy%V;_t4x4`H+}mn}=n-0#M|o5`_1Hf!}w6_^(@6U z*VVfEKA5`8rnTPrC)J3QStl9>Ddqlho{9|>%59qW9!oWj7O-TLyG+Vv*vU3P`gHlE zZxcoQE&u&FhBpE@ob%ADu-oON6QW@p=Q_6Qq*w*{Kmr}605WYDGN0*kW`~_eq3y{s zR+p;zvPUHHS0e zhTlq)Fc$(kg*)5#EHra~<5G@U$p`C9D8yS=tBV|MzPFxhSD}ZH^R8-_qw!UxD<+)l zm3+k%41$b7Z=9N}vnN`&48t)Y1{jO2OCs6w{ zj88gb6$_gP{GDDj53Q08;22J**VNf&9BJ4(E*hU2STN6T=;nueBN*U~!slzY*@pb# zpYH7cOZQg8MURw81pl3p1-96(IzA@nWh4Y=k#*_a4|+gANi@r}%4k0LSNc-b*c2Po z0MRC{!(S=<%SwPUHmrIZSp@oIP) z@k`pAzrvqrG-4CV?zP*id_zcZXs9KLhUJrS#{gLDSV;J{WPoAV|6uCjrE~H23Qt@) z0y^uTY|;OupcJgqo58RH^eH~)YXDert(Pi6MuAlCGq6C)*=$v|GZBdcl-diCg4+Kr zp@SpPdPAUl$Ypg%?0y52Z^FlhgHhl_WSsO71%1&4{kOWO#yTqERg$XV95f!T8%GoN zX(^o^oc4cR=ON1lS3ypKHY^wZtd+~*gF7QLxVFl`MnHB3^Yr4_?Khy46L*h zL@MGF62poT*b6!-F4cFxhGaW@qC3|1h_+9)*I@tz&6IlBeamltmo}MLCq??h}p&j-Fmc8|1!RF$}lR1ov=!r3{%e@0I0IHFYDq0tm0b<(MwWG){Zy-(l zK=D_)ec0R=*;MrU9}A^Vx?3C@#nW5T$Z%-VhKe^rf?;2KNo<+L@bSB%)otaQgE9iY zrxCRmn>NK7f&bd;0>qj93kEs72F%T$WFSdm$j8W^&0PbePz2dXbR!r9^-Fm=ig-dW zl*TNa#rHtmnAdc#+?@MZWeXZw0ryxV6bBi+pRnTyHRU|A#$%^#{NTLMmkSxFQ2i~l zObq+p+Ck={DH+FIknd^Q&#r+>k9-&EO!RWDW>NEw#8ya;Kl4P+3H?8&ZC#w@*40)S z3T|+A|6VRtc&knknf^~;Jww!!$kC@V1X%XyXeY%V)HCHCtO@OX5WA`i*H-K{_v#o3#8tDBKb-4Aw4vcU4Te61HN(9i5e#ud*Z#27i8+plU#~*l75BHy-4h-oW6AskvY@fP!-HJmQq}=-I|ArznUt~=V0@loG*Fw& z+@E7-SN!4=fd0egP^GH#&akbR{c~GDJ!TGBGZq>rrv^_gY7D`e&E36ch#ZuF z{_l5#)F|EKTD?GHB3-`J6G244Kh^ddJ3-d22cczTuLnKZ1n>KXAlyOU@-}TC#vp|v z@q+3M(ynpRiZHO+5W)5sWBwcuyR)?CwkCjqB5ZA&n@(8F_DIGL(5h5qhmb*pyLrFV zu-N@>4{)Q+`$Zk%WsdtT!)cm(hcs(r+@Z$7+|(jq`VYv^`1jv*t}HM=Hg*w3KNH0n zkGj)Z!U@MX*!n1UPOqiv8`df?flwFlf@IHkHA8{YJc|!n=d7Szt@_#UfRwGIKt`iSk6E81%$Y*xN=JXi4aqVi>#GO-K5Jga`(LK%NAos9^7qmrDGJIbW8k zB-C01RPX4-t8l^vWW9kB$88tZ+Iu3dfNBh zi-9^!-__`tcJS8iY}SMGnPPfW0-6;{X<^nl;#cD`pFX8NIrIg541jIQrh=HpCi(K} zG3cX|`bsmkU?fZ{m^G6ChSbnU?1K1HicEKe>q5-pkAr1vaZaQ!R>D$I35KsAw>p=D zx8?qM4PGyx1es`?w&S4F(W3&SbM$!6XURp1S zHzTGZXgek;BUMp0v{^<#$L8fyiz?@!48b0It$Sr?|AmQ9Vm^|gae-@eM0|zq-*5%3 z#u9^I&%J-o9@**;5pu4IXA=MSUYMNwm0%yj!8MrJFUUdrrp<<(Wr(zi^Kz8onMWL^ zX>LRv_M{9%osQpdCjxU^YGC`R`ggfnpK!oAg?~#a4bXBKWPeJURRycF34#$0Vwk$6 zgLS=5MP3Cklo=8|Jt5v&Id>g7uyuP|A~9CSuxP#xAVn;t7yj_x;pSv4HeRtLI^XXm z=@$Zhz_O}=6dwMA8RnXr3pV>n;5fF+Za}T~F4q zSAGZ)*Rw-=p13CLFo#OO{t>{P%C(fFbj=p!Us3i~Zm`+nK@D*yi?6vzzD=X|Y6Bsf z|6pnaoYJHCs)%CcAgbzEDdF@eXn^bGR%xV8nq6=n!0%606k`EQ?FKNgE*xNp+1Y-3 z#r>+UR7`G?`Da)@T`QA3s*yVs?NI%Py(eo?-bA=D5!#2Gl2$mOQRLAjb;e>9m{@nT z@Ts2PLM4k*rznT)T}OZ5HnYd#Ht$$U$d7fA6I|?dG_y2XP? z2^d$m&#yMvHDjkS4dLcA(wDM|i z>%`YF=&HZAXUHm>ro{sBA2JyCl+{l9p;okf@ILT<#kJnz#|x57t|EiRfs%Qdg7T`D z`#TZUX;z;T{3q?OpF0cA2f!6-uGM5*ufAGp!sSiTDbx9hH3M0$k zals^h-7Jzc3$@Z8Po!UrPiWR@r^7nomtUTD8BZDstf=#L16-E*jwrQkfbFPf7UyzQ zh_^)2r`OJJ*`VTRTD#xD>O59DfEel>+#opVA_UY(RC2Dnq}iqBs6^n&BI)@mKY=fp z`H7}luEedN#@xjL55C%AHc`zlUnOhH9=O9Eih~+%Y z0a)AEdXQ%Zrol6mh_)@BAat{ z55XwKr&Q7taK%6MQfbLYUCZs;d53GF*a_Cx)c}mC9gi1HG39WAs7QExQYe|kRwM}o zM4QF0F$@ukT-&Gyk)!gr4xqj=l%RS_hxe7&mr$YgTo*(MqOKIN$ z0ldrn;;w9#HcwMD6q8JtNcy8FVc}apDG68!L9-@?x*5t0N)z~I!3={3Dp#6Mh3FC@ z&YR+;eufg)Ud5qz$9KaVQs+Ua3x6;;iz8fWHU>y(uXXxFgQfx)a2r#i(Q$+M^7dd` zxRQ0aV?>?Gyc^dsxrS5fvQ{Yu@wsqoE-FTK&wm(_)zW$O#(#t3CGYuEJW~$+++W1#8V(DBYJucrtOuzKtkT!n83^ z!m_s>R!x>zZ))zsVzkOjE168A5U-i_##zTkNR$rtA>&3zl>j0j>6ao67Ux~a)WClB z{nlKHWaAWS$0cZfKmPIxX@5ol8qxb10!=%IOgW)=;Cutital5mMlbnnhCP=S>Nf0T z98u(u{~g#bnT0z$)At|2;^R0Iv*%I#4!%#;DZ6Z!z>pP|oal&C=uR;Y@M4CnZznMO zXj{08W_C3!T!@e!E7u1qwq@42EJReb1~&+SG39N}w6!yPzsn4s6kGubEM93E-sMf`A$h4(7!iAq-)y65+`8h@Y&(18;Z%_L(%qFm# z=B6`=x1S0lcNH?9Zyp=Mi>psG$H*VF@V9zgMlEXVU=*xbrKi1{#zIeQl3b*`=Om-E zUq*I6_?eA&^cPS^>huY%2@GyQSDpDAO`83cAYbDRJKa``IfxeL?28-F(i+{RZ$sz} z$P_j%h5}%76se0B15>XfO=!%TeXTc2`G9Gcpt21jWji?dtPqGM?$MY0bkej{b}XxF z?*WqdUkgAe34}S6&Qi%jI{UzIk*;jHg2+oYOQzRFh5ls;Fl$1LRj&Lf72CK|{wU=3H8dzwpNwZm9BB7ds_ zhYtUE>*!Pr_Ew(D0LB8EyoMYssX-F-@R8JVN;mGeXDHYOAd3E}ov@Gu_2K(OtC(2V zL9QzAz4;0y&`O`x%MsPi$Jsl5v9M-xN8QtdwYG&}6vi@ATCPtiNz=PdPe2F320ddoTp;{xvD$Vml5tH)?EikQ zi5HbW7~|ab+ZQcw;Z=Xt7FIfABG%ZXm;)*atV9e*meD1{ok3b+#r-NfOGG9o+VSfd zp`qiqHGVmQAzOpD!9)S%T;f(p(9pHlk<6mp557;8%WH5N_UHe{c@0nCJF|At0spqd zo1v-uihaAElWXZ-8eq)P?wQjj0cKP~rU}U(_IE~_e}Riw&5$#G#m6gwJX-XnbRI8F zKml4P@#HU*sK?(@Fm-WFz~M$i6g>vRMgnHsq`U=oY8Zm6hxm~I5HL|&2=8FwvxvJO z?t#Rif;|1>Yqx_KMbfa+H>_1`V!+6koD)g3g?Ar)!b+hA%GTk$GdpErAqt1$=9^zE zy_k}t?Zv^Je0x_?NP7kbR@o4vua~pX9gf6HV_8)LWDuf4mrYZ**lozM_P|lrdIA$xWXL zj`SFH4oc9XVJZ;LiH{&ZdmL&8D7xev8x~UlIY7q0FjQn!a5mfL%(W-~ zYwAqcDLI2g6G2Fg}b=e9|>XPE;MR|PPy!EQy2NOCvM*Ndx2M7#R{dSI; zLEEgq)hvP^z{O#wAsDW;+aF^LuMcEo8!(JK4?!nMD-D}=D$qg>LHeF^CKtN^-u%|W z{0`@_U`8f2i79P9nn#I6a%2^@M*|!TOeQp5i+{yfociAagTmWRAy^IsJ$`_hNTs_c z<81H9uI4li6%&>r#`Z&wA%z^>?G+MZG#blCbK)l~R27#%lGm~5ulWA3PaCbJSMC18dlV$gcAHEp&VVdElRpgL$Qk^XN5V#8!@YM`Cs!5r#%Jm zoEu3Aukt$t_~L!Gwiu~T)HURWd&_Tv!TpC_@qTc}%0}M$!1@#aH(zzaY%nV};ppX8 zVCz?cf#E9uv4=3_8JEzE$d{h}pnCXg%S22X%PatrMiS$xG|N^zNT`ONCaMSNloDI4nt0LJ?vpG7TkU4Op@N(p3mj4s!u;|$bCD zfyqUp0+`a4nMGN`Eru~;{NgSR*V=32!OcP0H~5)GU~)L+E=2qB7p0jB(`)*i;mXGp zecMeCuJ83@+>h(T?VBE-RELnT6fdfqw7Gg5K7IV*QSW2AzN5Fi3Tca?K%7P4TH=arPP1()z4Cvhzy6M zoW>%N(8&5LatWBBd#xztHT?`BUjPBosEAGV(03|X$PoV_kM>(@W?rhMx zRK(>QG+-!+?V|M{XZ5KVv0A=b1+T^!HKDVtvaKyxu4Ox*?fVxX5C##m+c>9rw-{jK zRWIX*EHnU|FLGizSgVensrHlB8jTpM6vua*sC+n%?j&ZPj1tBv+NYX_1(Bzz%W2}4 z*X6EX{O{%w7di7OXS|vr=SoMkR0i~KKeO|S72#ThKOM&1_pLkSOtW(i2`OL0z3a+R z11Zf>lwYx%wo(!TPxxzKvFqFi>TEn4Yn=u>LzlyIuIZAgxi_RoOh@xP%gpHLuey_v zNkmDk&=1flYLG^v-DJr8_oN#V$yoBjUI`?4!flS#Neturw%>|GccMQEL} zVUGV1&@kp3b!B71f!7qk)9xxSeB)UYQtD?nL!a!4XIg@0Ar z&OkUs7euDPczXtyMqvYRR!tp$slUeB|DOJTTgqPp+DfEcuC9YCFa}5d9U=@l{lF`` z94DMK*e^Vw@b0B>P-ED*;cC+Cx)uftBu^-329!K0hIit9amN{Ec#rNj$LRfZc5>Ig zAee`xYJUKA$XVUn>QD0R{%9G=1@K5TNE)f*42wkR;IZ>$!Z;&lo#~xw&B`bfZzIhF zFhl;S7=&-vw5(2_EdiiwGs3uhd3}b=L5C`a7RGYQKIGgZ3!w8bvp`6o0zG$~n>sJ% z*9X;`=x5-+?4fBNduZ6d{lmPZG_8r)M>i9FcOTzpwA+ES{Mox=MJb31)S_wjzbAJq zZV))cQwP%JryjeR?arN1mmtq=AIu5)*A;+~tw}7)*(UA`7TxVJWGS@`EH>05)}}Sz z@uA=Ur?2A}ehv`+XDr-jcS*F%mF96aMU=q7+#Eqv;vQ~zC4O*9-3}h3kT|`H%JSnx zQm_uUC08nRDyUgs4vGYQzuv7e#-lM0v9ifI-Zc8L+6CCRpbEnr0_;UF&xOU-JD?6X z%Ej3P{SG+hA1VWDS*4u2a?hSLy_3Ba^i*{bB{Qs53<_E_oj#abJm>63IVVZ9R*lBn zLD(p(@$+Rm{NE@Dqjc$o;QQ(JjhK8A2#GR_BL(li|FN;ONE>Oh!1GB}7xy@QUdk}8 zA~@@qtY@dpL4-AgrmOkv;kta9oH=_vKCBpa}m>iWquU$l@( z{D&4d9~2cZOo)qw@-yIdNbtJSMj$Q6I%sS zP5ae0gCf2Y_n2n}sCU^>3)tT7prcfk1@hJ7b{+LnHwk{8%Kp*;0-uNfN*Fr(wR?o< zB~@+e-k@;=OCC%zW{$;% zu+(=*gPIO$v)zUUBioYHfSrc&ZS@Vmi0IRGcC7rlxIzp?uAL#H^+FnO;-biF?k)%1 ziow`%dZtxxdAAv=e)NDvj)0cigu{Lx`qiB7JbrH{s#W_jv?KRaPEv^yq}qHyFuTdv z+`uB+A_!~%Y3lb*4?{b&r%?kUyC)+nCNV`|QaATZ>!IHhz<0@K#!hRFtRoG*pM|7( zw5pGyb7*ASveJJ%Yw=RZK|NP((F<+TVX#!){<6#gk2uWPY zV?|xVjs`0|639M`>&5T7RzftT;_YOy=aAtHii3?2ETK?2#Ea0GR!Mv@I;5+>`Lz8g zB-n5j2b%6x+n_(dE~qBwOY*{6WR^8``P&VOV1<2Xa&r4i1eg6F>)#u>gGBN}??+74 zWDb~CtH(T!&b20B|L1J}%Lf~v#~`>&dwI!};!B}F!609vRl-arZ!s~jBFW|GNwC8w zqFI?BXPy_5EZ@rdld1DcK3aK;^tDN&BuAf0%aWmd zW+JHlj_p6SVFq<=mYDl*KZ-l%#=pXB4dBg-!n0sy8Z4C~ww?y;LR zFG3q7p|kzYfGm1qd}6_&S+%A}ol6nO9$ zyGP472vXorO_r_ znAd^4myq5VjRdHTy-U6+ool*S<(DCW;;3$knaPbihCa|8X%f=Y9QpTF4@Hs|Q1`iO z$|44!v2lZHTUJGQe=n%yz^LYRa#POFjpr5-%-TK*MH#ab?@!$P-<-__7@XW3E56s- zq07xMoRrxi2z5F0=4=W-DCf}pw1Q}h9+z9IXhk8xBjrZ_%5-!PH(8^^z(--^-tjAu zca6`-X)bmzct$=?kQ#i`-;Zo;GqWsFZ;++iMN`aa9=JR{*TX9$ZqGNyfj z(IFL@?^l0dI4btn8=_xT$h!P3yaiG^)2@ie7`~<|?2?3@EgI&Z{>~%Gn2^j37{~!pFu@?iodi!DcohFhI{9^eRu00bgL>$Q@(z4E^Alk;94D@Gf z?11w21hjaWhgu>=7}aQdllb9|R>ht`7jQfh5~O_V&YNzTda*8~F*t-YSC1j7P*Dtq zKGf^G|DDAr@YzQY!^*knJ#7eH_oj6EL0?1a)22E6PtkA}CDcInF!g_E>H5w-qz?xL@5{hZljXg zE}nCxC}ZMTP$dZfXaF;j&MTfULkA$x^?tY2FVfbrHR;{u6`WxDRHMnmW!h^zjc7EW z{Molu>?6x;Im~gU)JnsTV;pj|Y+KRD0C;5zUGo8k)x0Od4qfGp0)vzkEG923yC58U z%cZ+j5;?I%C3S&?h6QDt3}W{0YxfTwF;GDD;W1H}dl;s)>a1C z1!uKovO&t?c~{1zVqa`UK7m30)2D2xX~l%7=h+K|k%-6ZU_}^L=(_*%e|M}Co(uz8 zX@U;Ik{7BXN9{mL*Dj(T0_g*qZ?qi_!c%&t?`eIXwfRIxAgUh`hdYrcFC3g!2y!%4L{HWo_ z{wm3IBg6RD&E<^3Y0?4Sw^M2`aa4vC{Bp}0^w(~k>BJ*ne}M-Le-jDU;S9l@$PTmV zdPIUxKN{B%>9i~5%6%^?SHEVoum?k=VRlVMzT$NntT#sU+y13><)J}sU}DB(7hI1J z^-Jx)0_nCRK&UqpQu9{J?cc&EBoZQiJP?6)%0@5Ii-))nty;QSxgy9mrM~U)^Z{9b z$6;?~%znLvca{LlG;!^l8QVu#s%Rt7AEC&QRG4c;>?( zH3U8aKS1EI<}X!wQy>4Fj=QTl$!%Syz-h020exrCmS&`6|O1pX>(5b zYyt1%l&SR~ijVl;W-!Yj9y%gg-liR3eJwFiH3^*IO-nC+CC!# z8Fvj{0E>we10Shiy*a-- zF9AO`_>lEOn*Y{%JrDVOgj|H(*Ks~)@<6pEjSw4yB+#TatXzpnWk?^bd${=hLUzo>LA{rU#|e!i|{>E0IOJa zx~TLDn>$!gA#Y^*n*=i^Dc$vho2?6>!T5;H!^v1OV8=fki!btF*3p*P*>rz{J1;n? z)>WuZe9PLKt{j?MNKYPKH{?AN=#4;Cc_Mr8kCbg`wD$wrYxZvwbzD$J%ywR2q1GaY z91vDehp=X71vpIMtj=3N&&P`CD_qwHt;nENE*VN0r$<&yAFdCN9xkAlPu5U@QS7_2 zVj3DeN{2cKp+m>-w=m7{S^h3&ixDmeQvJa}!Hgv2IjoDE&R23^0s0 z+l*6mja`>H_Aw5Eb=B_=#e`X1bYt?xP~MhYNV2l~lZ+LqLTxYr7m&KfL|)^$a@>=I zSeY~X81|v>&Rm@N_f=2%OPpwZ-+xkKov*6Bks+Fz`RUTiOWb%wv50TP&H~@hxnxTO!HcqHIcNYw24p3Dr!3@@R*w~vk(u|U`;4gt73-v}g)w0oLxE0HP z0o|y8&?Eb_@lwMUEZ_oaq{) za&lSq^-6Sc94>vD(gm|+5no%b-4dxvV}4De4MqOb~$Rb+fEMl7p5uM;^wfUFPhmvz1%|^ zhcu|D3XR+Pt|Zj(H+G1*Mor|prJC<9na5`Ia>FDaFwxD>|{n&rtd%KyYvYGdV`1MC3iIcC8(MK z;c+pe{(xG1@@#oe8y0Vzr?`InDN`bnfiAoO-4N{tvGT$-I<@1LND0b^31-p619N{N z+SN30Ok^WPNc!VrnXS4&kX2c}0q_}4+J~P0(XB5Y)JN_s+d@FeUhuNz5MeavUsWG< z+UV7=R(eH?exu^Df;K17MZ5v5Aa5J7ITmcXGedo?j=cvgum9qp?M-bxo(=w6-C~S! zXCz}i;XBk0_7t9jRZAmT*nJcoMlsNOpf8zcsJe6On?^WcICBYdf(Ic^e|lJYer0X^sfr0n475h0^Q5*K?C z{Ip3I*lL!5O!H|C3Ys`QIWj7uKP;Om#0Ep%X&Um@k1|Vx$D!^A{~%_%eNyYau(D(m zx0AS=Wp2_kuQce#$IjYpPsSA0YBlbAVLOaC| zRp8cU<9-hY6BNGJ%w+f{6G)gL6GgIEA7e-Bcwz`!KzLWNpiVJbIg~U_J=Ra2BR4Gz zki!jgPwXN*0==$`isJK#Xvgn`#K@foC3qoQ?cHC^Ka5L|N7qDaQvG-wW+U|w+a5VW;6B^{UM$Y^#t74>jHnN{X8~w>h80r6UN1oAR+d z#-wM}nuh$3CQS>B-LG7lRC|G*XGKRx#f%_%d8L^pZgO47J2_>an+8O}csk5tg{iZS zq#l;HfDD_`vk_vq&+`JlfGdm)gg&-8mz1`y#c}XLhid3N^ezcW%IIV+m#y4;VmaCt zOIT0Qzrhta{J$=SgQ0-e`cWJ;w2}H@~MC55x-$^X88W znze`EeWX2d3!e0zCB?S6+n-c zfiQWN1V2V`tRgJIWy-xP2#ulz7H9vaf*mNHWPDp;Z{_!wRQ5R11o&})s2&VqGK#@6 z79=MEGrWnds3pzgqW>Bqy}L9)7>tTBd5RbhC%0tji?jS+*=6wo6mk8BdtzicYbvm=){*nh<0odxL=>ryydZ5r1!A=O+ zQ&HdDZ!xpaewPqKtDa3O>E>oPk)Y1l;-wu8_>Ae>l9u|8oW#3YY*v{!auE=#uG~`Y z+f=xbgJ-vXA}9$4vZb(0RsD%0S#eEI;GSzdwyvXExvyY*2Ls4KPWk4$4O?gGyY?1nMi+b4#(Eg+0WS2GelEY z?HRAu+tMBF%~D5C9q>>HkX4A(`N=`iwmSz$B)X;eV`!vuxK3yB{$WQTxHC{!JClG{ z-&$0{)5y(NXeb3g$Z?R!K%a5?X`t==xjx%YxGy(6o_fRS9bf~dL!8pTX>@V(0P~F2 zA1Vv}2OOx9Ot4vRvU7gvi=y{q2@JZmYvjmUksAoR?JI|Rjo&nFv`Y5(H zD*a}iIi@oP>;os|@Fc|nY=70}Gh=Dh; zdY^b+S(65cg(96nX0arU(>wEw#0dK32?3(Z@r0I7j8O9vh%KM zD=;^Mbd3GWKM$N}1$*rv7b^v4mo0wA&QfEd%KZSFgGr_p$xt&4`^^JvXR&1(VB(ws zazkInnUykvjSQifo-~Di2qfyO#9gTH*GH!GuL+D39!^b3Veuk$U)RI05>z&__ls=r z5fO;FI`e964DCCtmF#xFGbwOVt%9Rm+*3~VIIFZ;=1Bz)gOte?ztqW@H|q?KVAxzn zGHKAY3&WbPhjjy8FfGy8%JDeNk?NpLzn$pobu~-RhcKGhR5MkY2eRA?qzl&NksXip z0yrIJk9ZG#Dq%K0KBU%?mlR#Cg~9u*oetFukfL=o??4NHjLVlpff&&vM=u=1 zW4KqIn;o{^spLpTOkT+%Q-j3}d?iF$>P8Ryu#^=YOp6)FE$~d-Ga(a%kF_U#!P3+l z#Sqi5`^CYTTTryD9$n(Oav_1Ac>`;i?aI6 zU_`}1EGjo~Qx0e)@IxP|=)yyXL9bG#fuFQpKZ5LmLARz=;qq=~B=aan>j(8Xc@St3 zbYV2|!92Ws62#YY(+&5(VJ3sP9lcG0@+!0O0M!T5`FxzW)*k+7Q&K!*kwA(g2ruV` z(Bj;&%hb+A;Hm2uSuX}LH77;vTc|BwiQ5G^45$LLxbl3yW<~3;`zx9OzXT!nq{~ef zLO5C!4%%U1p4DCiOn*KoyLZBbX#I?5F1TNc7I{sqn~GIDMH1zwzlm&#%-i8*aZo#B z_Uf(td<$3p1p8beO+@8851gP>w;H>&h4;M@^Ua{H zd6Z#H0EJ`R2GgI*E#({DY1YCZlM5@gvf(>fHB{V^HD}b-Cqs*46^{LNuoQn4GYpDD z3a}0lMNwl{M!j#CW=S%-MY{;@@#U=K{rEo&xu6`0 z@Gk<#K#ETbjD{@$=rOM_KWi@{j$Cv)4Y|zS9$P+?TXx9q{wPB~r8}ia{ktK>z`EhA z-GQ!NfwckMK<)eq+ID#pS}}Grha|!aIQHfjPWWh749a=wF<+A#)7uS?=CJA|I8qVE zOT^umk}gc(js^xrKc;V5)@(nnR#mtG$(WC|(rek{R`$Gc&ySjODBjJsr@}>?*Yk^(CDOJ2Ir^1k z{MFuJRUe=Ra4+C7My#ddD+d zjWq@X{Pw438d(IEiNGKeb6J$HMwW;T=_Ul7CBy)^LHf->4pCgMDfi}0t&RcEWcCrn zb+e^Qm7zQx@mJ<(1zY3dFC4ndgVMo@1wu^8Yx+IqWQ;vIi9O6RcOiw~VLPpsQM%kZ z@DK3g+D#$WnX@^jrB{1rmhoq;+DcHSqR z+Tj7HFND=n=2tuugpj{bcjPg?JLQdcT}YUh{|+&9XjKilMT8BPc7nDPINwyt0GDA;L zb~NULoS$Yrul-)1i5YxewN9I~<|DtMcEqc5sD1*dL#jGNUX=~d=Kh={Mw(>`4%C-aK} z_K(oOiM|Y;&yk(Ngz2{bO~%^j8~j;51@Lpd$c{YXjyNrTWa`GxBF~Zki`EN06lP0} z8xbF#E+p>oj!_`(;}3Y$wKK3iev!-AFDtuTNYyC%t&Ynit1dIdAtQ#PL4)kVCpjRJ zMBuMzf(V6W(?@~kFVYB!Gx}PDyb-S706V8pL2zVJF&v7VC%Vf7-%rJvOc-Ucx&S(w zUicU&ZBA_1!nD+?e@zD5Q{_zW=rC))jv4^4YF2?%$nzd49=Gbku#YIqgpSD#4D!Ap zayJf{C(0%4?Ij)~H5|vF@@{#3hd8n(xs49k{&h(@s$4gd(vY&MvEgzofcXJFEZ!Fx z5^!}^j+SUGt|;e@Zm?l24?)Dm19IOEawP#e%39Ia0PCa{YWR4F+A@Mq(EAaWk4uW` zm7;HAHU^bKsW;Q@4Eo(Benx%7lMT_|m5FL&&YuP@#0`Ry?h7A}pqMMA+7tID@+a+2SsG# zCYT-rlGJA(@NbQMnK1?tKRd(HWCFO@Hm#Oe9+Lb}Gv{S3w0?awsQ{7tio^xtLnzt$ zGx1RheM%s3#RZ^DngL+=x=RYgj+0fjMThKUSm1!=PE)=?YY1Z_-dpXk_Uq(t=YZ)W z=J~Fb0uQC&RVi%O^F8^ zkBhN&tvqJpYmVotrorrQvHfBG5nuiLsN8|rapqg-Yc2#WJWkcTYRh@Sr-pb zXpgrQHej|&qnk86o>AfM7l5|&pV6w|AqJ+(PfU+{FkLLRH&!2roSmZ^kz-bLt3b*o z?@`%MTcUWQCJen(n8z44QEUK&+D;H3B5#if(VNv<#QG{sgF~y#8m$nVt2y-&o0FQQ zPe%UMbGQC7U*qw4A1#~|z~7bjl0ievV>luYW8P1c4Wf6El#hnytB}8pPVB5$dXl2S zcyxAbQSthDE+>`hwry;TCsX^>`KU4kn!Qn`;U!&zbG9j$;^8FJVIJ{mR6TGFF}5U# z8=Po{3Qx9A@K7P&pEXHW)eg}m2CXEmqh(~J7fZ>W4k&c5=MxJ`fW4Fbr!8MD16z>V zLET;rp?MNaS5U5P%amJv_2g`)sfU#yEi&TNdoC;o!Q(yUE;M+F->Lo{osq553_Ur-m3%0#WwT zSrkQewExL{l*9`#9?lT4MS$Mq;IrM&ErHOIBks{%;O88v8zSX44E74RQGh5Sfv%)r~?Jb zUFk0E+U0>2Hh~_$RbLu7NR3?0alVk~``hir0wijwbZ+55-2qKvRLCKkc^*g#Qlt3{SIt8Y?JtU1{ zdcEctQEUKvio(%cs6}^1V9&3VGGsC$n=6hqdIVeSt*y(5}#fhkzZaqqBdzrXy zIy@Qj?M$R0=Vn#}wCBSfPOd&?<@DSmn!bx8DlgdFtvbYrOKc2lw7LJN3CF*E7|?H0 z?`h+7ZgE`2S zHL6~E>p)qzr=Iq<7~)%M%8kE90$j8F>2>bdrxeH+U4?tGE!t{*EKvv~j3J&Kfh>5ESV{Qfl!rr+D-Je{(gA47+LK+}Zc@E7k zgI)$s`y^!{WVtkfy6`BfKX*uIy?b5hOlRD)trS*jd?tp_kTHGCvR#t*gkg97oF+W| zm@y{2228E%WL&{!kIeUQGqb|Z*~Nj~5R@X6h$-9NSGX4lvkLoD z7d-xWw`hEo5sNyw0X7!;f@=(s83D8tk|i8OTqhhMfw91gOPh9qBOF`jP_%=)1iamf zD-!)iOq5g39D*%pVx7dP-tiF6(B1DuV8;-`fHa7pxZZk#qS;@gheR3B$2ahQ^f7qq z%%bhQCt4JYjq|thWeof0^veq2AF7-p8>Fyv^8it70inw+`e=Ma#aQ_-oi+ zsYG~#Y7ty3g4cch)-H>~b>xj5IN=8)IKD+AQJaGz#{UpgQRIH$H2;OcAh?-SDnwqc z%cv4ArV1D|d(1+KVs6+9B!Bl^fCO_lCN<4DXSB#GRB2Ib>IEmOu%}z6AF`p*AN6QI z^%_Y=ho)C2t0)o~0g!pKO{LcRokrS5hw6Z2{LnOmGx|F?-zTfO3nbOse?dFX&XDd1 zW98@SyYgeE$@zN2Q^N85&=2Qfl`T0tYMF^^sl+>-6q|YZO!ghc*;~s&ko`Ng5 zSIkLR$LB~sC5>6eYRb_QGqS#uDk-F1b)H}3(JU|)hvK`5XhbFLJP@+VxNCI>c!$JP z;i)M`hy;n{&w=YFk6Hoh5ucK`qmLa(NQuxaE(u(UBO=4h!1_=q zI)`7ptp}%*^cnVKr}C=ZU}+bkpkX~D)#>PyH!S>@XT;ZB7)_32&?^s)QCY?U4SdWE z;m9^NcpA)09LcU)bu1w;Wk$;nCc>D4B)WTk{+et4L8R?BQ>c?|SZ7>xVG=w{e4b=cMw|>dJ1wauhxLsnz=X{;;S8Y zIcvgR*&1-9JTPCSP>iNiFGpw!JG86^+!gGOVgodq(x$@R%*MCIj3J%kU^1s5vuCe& z*c>2p^e1fUng=iovEQ(Od6Sn^@%xXB@j*e>r|EGIHk)n}Jl3)Z8w7_TVl-)HBUjhe z!i*<8i1eK|ST=^C>sy1^C!0gzwR15TfDg({%C)?X$);} zk=`N`j;9TtiD%|XE{+x2F{mo;MZ1)CW@DU( zFJbUEH{}LLLR!2Zqoo7*_%Xj_;;5G!{9v5EXDg^*VwI6V30s~pC6-*_m()>@J-Q0f zm$nCHP%&e_3|6I51adrKwH|hWH1h-cFN1<~CKDU{SkvM(;f{u15J`33UUfGenb948=Ea*4|4MD7258on zDG(88#jwMH+JaiBI6Bfcz10jU$pg3tW(|d0A8b$PY`aH*g)sMp1eO0CIK`pujD0Fm zY((U0^!1pQv(c$lHvUBjgW;`N6VKCj44CxvuU#9Ww3F^%Gw2n}SZF#?PvJr=1ObVQ z)+R}j&+hVZ2b>rP{=z(3HjRsx^rqHGFYmhkqYd*K|4WdZ?}k>HfEBAg=aFph4Z#}^dqEoEKSY$Ws^-T>mM}Wd+w^!Pa|VNIHtKrzhD0si=kspkK*^v)nys6 zE@TTmX987E%$hVf?`~kS@f)@)S7V3cN7M!CEkSz3Tr*kV%WVoKjjbGYs|wd89R^NFDRb?E{9@gLFb2WFkmt*cNnbuZ0@KWmN|T zH#!0Qnz-MR19-hC;L>E-5*CE`E1sadG zTOoY-?7fm^=hiwe3bs>a0keYs_%N&B_o5&>zZ$ZN#;qy9K0XIqUE;et!`Fv*<2Otj zM_tQ_+kN5<98r?iXS`tf~aw;e~Lh z_-fw**+`4Ql-T*@&q}~?n&hBr#DXjP^9rh4#Y+xTIai|xComhU^mm*tzF$VR%E$H+ z=o4E>OqP^StQ$|;XMtdqZ;9#^9lU&EIyZ9g_Yg0hrC4g-ZV!YWOQGGDw}fqNhSNj9 zP1Alf%c#1H?4K|`ze&GGasFA`5^j?Q@v!_*StYHW9NutPMo3R|Ch48pNT{e&AVBsh z#mVEVrFZyJCOd`=mh~n#L&kZ0dSSz9Vovf$8gmQr`d_wg@`(9q!0ABEi6c^xedd7y zf$lPuhOp1I+r{nv76V%K%hZy;mVYa=5LaMuqUGrP8Z4pLiPni;OnJEkHw)*0J6h%z zMM&N10?Xa4r7MsOdFvUD1lAsMQthF>tn2{vvwq+(tBQkAUfI**cZA@$?N^BBF+D8b zH9c$gS3<;eh|FbH4TZL_BfI+4c{JZW6I{&co$xts%Xk`S3LAe&sIJaU{iSwCO9201 z9y*n&A3w&(|H|btOO){YGDPuN?VvtW>Y@kCK83B@-TAYdYol>peHX~GlfWd7NZHP!;qMV z&=N_n&_A=)U=+qcxW`Lb!IPoL%)7ieT@__~Q6@$$TFjznq)d(Xl@9Y#;%Jbg%~_ZD zOY!LoGsw4bcW>>u^6ZsdJuP%4UC<5j7m0`vY;rM#z!+~Ga zJJ7ilqtQZgMvS$l{i8VGa>E6ZzitDLSIsc9F>CkkcBxklf_0)4HZnp=^ixm+7$6n6 zxia&KoQZc+x`2ROfy^kc#qVKG?5UpYl+;+e%~s?fc8J=VP7OFoHMo~BYWfl5pcHNL zp|-*GDT6vM+%+f3A#KN}3X^=Z5keTQCzjuOFDuZMu{{>*?LkH$vKV~)bUA_-s!+xy z_GDc#p^YdKO>?}`kUsZLOF(#U_u4ycImi{vk<>%?+(~Y0@h&5{yth-U>6$9$%cL}o zcIiU?VP;O}UQLksy+ZhA=4>G$uF`7@(*W}A~^vO-^ zS-Q#-+H4E#n;U^m=U`t6uox3470DtT z;}j*=aWm`u-b?`%<$mYIgk8VC@~+;@bO1eYr9kwtJ2o(m!cWj&X_NV+>Z4sZJ?2!Psgf*ClW=W6DkFrfY}QTvVglo+C~b4}ChrW#mH_&ge%Y!<8e5l_7F5wm z`qo>6!UtsD_RCuzKN?MF8v*KCp)LZB1EDfxvy@=MIFsNro78Gk^*HHZb*pd)mEFL@e17Kh#Sj6^({IvUJwQ}*W;gn(jKwth zR>e`%MA%oL%#qNAU#uT&fbBZ>__Nk^et)enm}PMr!U49q;~`G=r${G_NQ8>Rq=2v{ zy(Q5+iuS1PuBc)e$b?9~ItdohV5^8oFaxlMI@?^Yqy`Xrgeue)tUNr(#e8C@+^Iw= z0j)Lb9eOq#k%ZKx_oQ zzAEbqgdC~(_PHy@znl=54z9o`wozFaVL@~A#lz*g`Pz>$9@Nn znQ{<2e3{Ob-CP{v9z2Imh@s}93e%09wMBrHeW)=3f+}S+?V_e5;6dC-o07PG@SJZo z5eyGFHP_e5zNIjiQ5XzJVv=0!7H4FjkJfSe};~u-DkI|9_(0aj8%JkhiHr|gImd;bEb7{Si z4>uprXnPH#lNgeCx7#s7=w(U6uP{!lOjOk3m>xw685Osvsr;Opk5u(4=5*Y5^%D$Q z(viEU0?B{l*|WkKsksi3u!e*-P@(n4`dx912&m9WI#lVCu9#{fr=2dQJlZ;jy{?6h zPUGG9^Shy6vjCh*>+>3-jY; z=1FnsoCF(bfMA?h&&jXoaj`89>6sdgfa?3PV#ou{4!Cdim|`|zwo zi05itY}BThoNE$Uje9#dE6>w$0juA)1Oa70r0GAGQ)z^PPMcl z2STa^ag(&V?`|>P?q~Hcq5BNp{L1;_jh@5|VnQVkdI{o^m`U(ip#1BEGY|;-))Zt) z+b#afzq%=6^jFMXUtox|+gwbTC(R&W2LX~xz=a=~Jt&3DlEP9mvg|9!)UZZaYXX5y zivftwsjuKv)I6&K+)#+E6hs4F|hMN9J>WgFLmz}L*E;3Qy{~l!Z&#J0gfZzp+2MU-tpGhh z!oMkyIipa7%Sd2iz4EqkliaUv!n3_s1QK z_kW%Y3($)(4i=@aC>@^->VoT|O5R7|PUWDYT5O-+)Mp^V2OAf)ql{(PGcF9rR=2a! zWYR#)G{U$e>?F*cjDpG#xs{Ev8tg$~I+P1m)kxnOp%{L}jtFQ(08nS2Q&o0t)ohQ- zycH7ZbTnVIRG;@D&BI({A9yzCc&<66o~%1#cLdbeen@9VTvLrLuh7?0$1bryzkrzC z6tRrLY&#p(95H{v#_V-DJo416h9&^+jl_l9Gd$r=cm(y^5z*V{y_ml-L) zpvyMFxibk^N?foQ(BTgMQYOJ+P(+Qb9978_R#HAOS8J&j*SHN=XBvN0qC$-nwyjBZ z1{i9vw_kGI;;>yy6D3fVmftJ%`8TS%PKCbrJv3*B^-u~_4V||~gzpR5*=Bc= zH^nEBmN-7I$4@x)e`!WnS_7F>SSb%K*_4M>{y`Imy<1~y=ZAIcgTQ5XeEzZdXC#-dZ%*SRW@a>XAY=pcx|- z&3s27`~1Tl0;x&Yh#V*+OlMMYi`BY_RIr+8g9+2qoE@c6sg%uQ@L#0dcMGI;{~U$! zQ6#TS>Oj083PaliDOWtMdG$qRtGg*5Z!HROzfnrR2i}3^QXL2FY^QUNZhzTV=ob~X zmDwWpan@|c3V_{nv5^{XG(AE^@O3zB+m4)E`SR&F( zKfUfXTTMW?Y?V#tIzX*m>5SPb`ucw8>9WT)i^}PxAep0Cloa20l;s_wKa4)*s3!;f zW+EdqLx;gz%BB+kIfHbOZnGb@PABPHK7E_@JP+oayL;y!97%k?286Y1cH0i0xkoJ- zW5_!hcNVZ@PBgT4u~iADCf5#`4>p%Z60UCt?>lm26^r`GEn-xrfL|8t)L0C5t%G;1 zm0K6Ux&zNL$G&uO-vZo&lj)IgcjZMR1>p4(L~2;sfrN7d7Tg#JU!D2{{2r|oJtm~f zcCRQDpXLQ1wnf1Q*p>d4=9*6=+3^&0Y>%!?m%lVvCWkOO1ovW`lgBEy#L-z(D$S0U2h!}0o!bJcD8*Lf}j1P zl^7kSqSD1bdW84&@S4#C)t%^8@VHkM^{7j7`xuE~9L;#_BVTS5hJiKwL$ zsv)Hkb{p4_%I+qU)6H$%LuHha+W2UO?~Ko|e|y11^{BA1JkL0DgW>sPRUY_c1*Ifr$_uyEw`PCS%%k*# zo!|$Z%wU7tfcnZ^V(=CV($xg77adwGsz@&i=lg|d zmSkp8O@`ky+|>g?0B6*kDok|m0SJ%tFEr<-5HCiIIh!&8A$?JTwZRiIY?A#2iL9By zl&Z*_L_3lo#N|7ommTv82bqtsjZr=iZV!=eC8ENH^dSW+A(RUtODFTM-o;|Hgd%YY ziNf5Mzcqxf8dh|mfqWnAqvFh=$2BtLC2I!cbFG2?ek z&I{T=(}KAlfkL?+r1Qmh)Oie?(S=uOFx0;aD%meUG7|O`Ke*f7AYb@8K&*c8TfddK zT*|C=0V8YpP`T|`Ve5TR91-U!J_)~W~guc0IES8kd{78@sc|4EP=`hFlpKtF^{8sRBL&~ z{t(6kgigHii>m%YBvvz#2ArQWnu#2)FmGVB>Y5@E>*HZ=xnpJ&Vy?K}(jSlJ{LWh^ z^K-LNKU8S@a|zL$aZ8DOzoC$1m;aP_|DSQ zWU2z$OM}O2jtx0V&(Ni?uGn{{kskTgYeh%=<`YFrz1i`@Zk1KmYE&@@`?!T%TQ@hr~cn>e^RUuna$u5?o2DP5TvoKP8Y$A#vrCLN1 z5g*sNJMqGSV1quH;E8$`HfGbyNl2v3|@|ta!>jCp`)CoR+d?#!qjPblzyu`dPFW=G_F-kd~{olEEI8d z+1!D-X(%1Wa|8g@{h=f`nF8${8Q%%``V&N_4)tx`vaagobBE>x()A+?u+9-Z*f30x zYh6LHo&p_R``$@J{&K>>;XW}4l7X4$M93Q!g_WhEN&+q`GDh($w{4jdNl|zTKTGs| zZG3`qA8!M}t}nOOTo&C?zr+y(i4xuObk#q0kioN@pCeFt?7QQFWB*4_pal-)2&j8n z6+o7@yjMa3@#r%iv|8kCWbNDy#s?l=xxkW&OBFv!hLJpWClqK)cZ#i&vd@{Dz2cE@ zLlpatcv5CPf&N$}-*I)TM6o2b&J}Y?$!mPeW`-k`VS~@bedU%Sn4#cud=X0P&|26; zP$!ovE?}sniV5;M_m7woi#y(np#S`RI+ zp9&F(eHi?(g83$?F^m{fTY!MN5cEw*@u%-?Y=6Z$RcDHE-A!x+=V4&>lp;F=&ZN8RjUlmm&SqSTp6SIX9tHvP$a*ig5e{}H z2;y5OR(D)m>m41_^T`kx5{=QYwZ8h-W>5@d+L5@RDMt6-PYpLqy5GyPM_g|#^OQo# zE5~rCoOoeJW4;P>!B_}A*0#Pe$`mS+;bJJFjQ+*cA^vyg%h6Ky_1skP=UEcR(;T8` zLNse+y6Vi(eDOD$?R+ygl9?B%=GZ8z830E&M4>leZ1m45kjS= zByzFQVZXdlOH1GoU0;?v{6!Utmk5#`ATnc$hvO~OKQ-r|$S?fWw6-%%=hXK@GToR9 zO;i@3lMC>o#&i-P_8ure=jSRG$s(nt{AitO5{L~T}12jO8<{R!sCmLkY(hf0u>bR z(c9AKIXAeM4QpEe{dI%2`JZen0kVZB?nTVROjW6*-Ax!>b3-jll=O}<%gt5wc7{*A z^0lF?9wx!d&WMQow-%}5JO9wa-%iNaS8H3&D&)zZlaV8;>f4=(uyB=O1q1e4;Sieo zwUnftvHHO?N$~0+=Tr} z0>0eMUpIM)e=}sL(5#H^v&qwJ;Aog@xqZm-?Se?Db=T`RTl7Th^{*9oHX zLBo)rlM42@O3yQTy_SLPu4T~hqConMvK;BY7rv{9lI$D%;`rpCkmFSu51G?s0%!ZK z@{07Zz@PXxcE=|$zAa^Gj5$G4@WD%tHo1=0D5Ql2_Xe)JnnoT&24^pA+3E3LHR57U zh@w=0IqSMZx5SAnVxF}%js=_LnbM9d=}$-w)!aYf3NvFr%wI{=z+| z(Bm0ZtAXWnrXV9+raMRf*Vmre?thKeVakN@S7I=x^I_sCR1somLF!l2u1P{EB6Z)4 za|u}sq@eRb9DY7U9Pb;pe-SS=K2DgVk2wC#C%?utB3>uX6ZClv^pG}4*^zZS;Gd9Q zA4e544)Q%SydrkYwn$VDk)E7@W7@>`U4M!#4xE(>L17@~{3w8^!ArW!EoRgQT8me> z@MD5CvS`sq+1pdu`Q+Ux;m4?Y;hA*7JxP_%L%?~=9JABEnk;&!)HoV6)&-XYdvf^0 zRm!S850dAdz?2S@tE^aChrDCRU!r~uHU(G2`S7(`=WsJ+#^W$pKw2whmOD@=j`qw~ zGE|9WJX@TB|1oSWGIz(eit=XV^j7yTWO%gN3$qFsw4M-vZ#B0HXVo`Z&xWMP6SgRM zh%vYVL6~TD2;7kqyxuEV)ilKqP4$pR7=-&pKvV8yHp|VuH?X{CVP~t`rw&d6pjl^5 zq`8*V!Ak4_Q}S-gPk;}59}jK8@*njjakJ^PD!}(cwhiA0nHNVL2*t=IY(L1f@UfO2 z*xl&a7-BgBrb*Tbj8J|#zp{T4^c1-{{sm7 zr{rQxS#!oQXw?Wo!s4%u)uxplez+odnwIa_V*7@kC?g;FxrSpm4yti=i@3Y^=?jzv z(9&eS_{EZI)1Le0VcQ;O7YbnTxP4fla1PTYwz`tt&KUg1oWk8$Lj-5Pt*ZMg`7Z(R z);y)~ScR)iMVPU~sC?*iBv1H}2ET?2dMCh)1+JAcd2Q`LJamepD1Wg)(~|cqInJN^ zcW8#2n^=iv5k#+iJ`{5g-V=MjQ0DlXYsa@fQc_s=l(z-P@{5M?fsd*|vHqb9efh_j zcuEw`pg8q;E=jb04va8nF19wo8JbEjM_c0(+fqmOj3n?ka2v=oPGq{B5d&RpZg+R5 zmT9j&+vFrfrFhQ{&VQ&iBn9Lj%d8$018b}uWG~#?a%?VKqnGIqTj&%ke0inRPlW;D zEcM-c{NnVH44snlejjCA5xwN`e>UH;08*n#mlNLt&^qcgr5BEo6Z;Ju;#^th*$4(Ik4`Tqz1C_A+@`iAPTTg6|q*N0tw z;M^|Ngn^vP+2rlimM-aeg#nKVfgMbcLCwc-SBZrDN~2P2LuZyK_L1C0TxufZi=z{l zPibL3t3p#zxv#$;nWaw_c*Dn2Kg^eyHv$7FII!v&H*t~9T%T>n7~0%lpR+c%!V$53 zcZh8Q4@sm+Ca~7PP#O$!zZ}6_IB{pARK&;cP$CBS*ZS9nPExv6b^i5o4 zQjU9nMGs}Nw>0P55&nE(E8Ob|^(E3f9`$;B7v8|=nUwJ#By^gMOr%+3?uZ-ErS{lw zme!v(G$QB+L++>hWa)Hxa5Jcv&?vnI*sqe307imbH7k)zBDX*CSFaS4uwGG<^ZCf7 z^Ept~3REe_ZOTO3ai)77=fyT=?-ODN)gVbq$FVo?Db%b*6se0|8C-%b-SDb3Jc~r)&wR13V1SPI7fP?_bN3wWV?xNwID;q z{k60S1;axofb-7@9cr+^TLNU~sK2qj`{f@_lSmTYb%{Bz-b~N=fQ>Ypk}UD_%AR}R z&r-$84Dl4_M>8yMx)e8UZLJaKkEc606_GS-X*8?SISOP+=kWQU$G!q!n0LW%wq zrxy^y$*IZBh`GF(1-4ZE^ipEUzZ7=rvF^{alyW1lV06^0meM9M(V7f@4$N%cM!ij7 zNPwI?r=?#KFA0We=sq0j*zfS!tx#;1Y1kF(;n*7n)q5j>>YX<}ga*on=7k|G_g`=z zOsk|zzP;_07V9nBqgMv;3f*bEe{{&2|Dz$|I7rs%4An^E?f6IO29!)g>&UE+BF5v5~=-P)Eg_j834}$YXURre`%2Nr; z^_1|)zdfov{`A}JbNFzAhG*q-)j0c59TvemRs#*LtOjRg>Q+!hOgt%vh zrJz?&l6D#>BOr~`g3yJl!YDa36IN$O?o}P{NOosm?qRt%Um+UNh`(3EZG5{UDGg)B zm91~&LFnk9i#c6P?qmX$(fYomINMuP3ijHt|?R0S%6bGVs=`is;e52pOxGb zTgV}9Es_$t)jbPo&320O?CQph6ZekzI}sXuU4le4ohi97qbnlQL zHUqf>cs=p%Jb#%@t~&;EctvsH1J7TCMexlvNcJWkUhOim)-FH6 zHpuzAM5C~AIrU|{MjNZ>Fe4=kSd}Z1#y=*jL%>tS$c+JvwB=-xMrt>gF9=Ww;kyNW zS77hj;A1)dn)mxacqBLO!}yjirin7u(qvu5aSzQKgg#$ zRg@r&83M(sLzU~n30fg6bBw6bx6uJ$438Lxh=dW3d>{4U<@+Jt)DKajf5ySI<+D## zD`eznq(3oca;A#*tDNLd5VmLlc#tJlH!54^(tjwfEcl97^lyKdfbfB-RphyTsc$b+<*w}%G;it?OCOY`ImU_I_cg!ucs& z-S`j3DS{K5AzMN=9=~yyU9nn<9MIjqit!nv&eJ`VMq9U5N}rd|LU`@YVc_t6*)S=u)A;0qwa!IE_+> z2mmq7wB_?0&1Gf409l*DT>+PHlGQ`3%_SY_flV+?Np&Ktx5e;o+j>a5^s-Gkq7-*B9Ky)=U6z!GBNaGPTDO?8+}5!cQ+%EDV60v{$(iA z$^EI6$``|xIgMdgO^M;gcHBv+aJ{4ls~D72i&&8O1;^SGEt`)XJVXW4hh1$~#O&ix zHh5$s$ki=uda-~Eo{;Y+G6N)!#CU5lsYj9pvE7a)$k%_rYcMjf*)WOvV-d>ReBlm{GE{n~U% zVJbUa`n#pF)X&BQu8*lQ?s?@H(0yvifjxj*-<#&S)ndSKj-fM;sHPax*EMIh-tK!F zRS+P{MgJHj*5gflonPQlac%F0_ivvJ<2$cD2HpH;Yk7FNMFFElOzAk8dk2t+F>*i5 zcWj89_^wzccm7cxrH(5ZS!=lPlj7El1eJxZ)*ZddRW1*&4ZD&!D@VZFQEN(j>(D#v zh$Ht$=(eZ(aF$XCt^ZwWW|CLB_q9u{=XyeR6fR~SC@iGnEPw7H0K|0nw#JLnKe1J@ zSBF+d`RiA@D*?w-K<;S^GX;XRC@}y8eTasZVB^Y6TM*(!#rc{urlnzsH#f5WKv3tJ z!KO6TM%Q7{D8!y=UaOP4Ix8or;VELMz23c^oje%gP@Rrrr|s)2nSawro7xL+mpY#) zhVn`aa6@j^y5VTV`1bI;27H@?G1E^;U!eW{5lFqQHIs%$NcDA0jAQJ>8|I8+D^cO6 z&s^iBO%oV3NvZvMy)SHJmwcHR8RM{sbrtxE9=1SF!9wt{`Cn4E(sQ@st_ zlt^+)$B|21yAPPr_frr*WU!4o#cjl7LBJ`?7F|6+_y8c;U{z4LD?>*fyu2ond!Ll` zX1)B!l$05x*21rDR*%0)xC2!QUsQ91+ z=l}$D!yL^aXhi&nwE}&js78~KmVuZuyi2MP;LUvOm_y}U7VOcT8 z(dg;hAF&P$e`MuDqMoSe)V(aQhFj=Wm8%O++`cSvKosdD%V#Beu%NNFWKX=DO5HU; z1bysJu|Ijz(7&tK7b{9vnThSy@?#k`{yN?};5pNI{Wt}?(mnCj!fMH=jO}$7eBkGjF2V%viST(Q{aYSPP)#;65%|O=H|Ud@QJ!jOe(3^6 zzwF;Ih%#sJ%Ko_(=@w9APTT1eZjxd3R)|wf?i6wa)<6~liNjIZSiC)RF8q8dK3~d* z9sGOzUtU(P?thnaCR$UEr)xg4s=Bymi_@uBG~lB(h4t~g+(L25hJ^B9%>aoDnBr8! zqo)SAkxZ0LUW!<8vMdk0famDxO!p{;M@8~y??C_1jmNT{>wXAAY!SNe>_tE9kFiiZ z;Wrz6tbq7IRxC1goch)=Jc;^(?%dbKAN|Od$PF+5fODwBV0Whi7ORj62MoVNA}qBa zOm`govB%-Tc`6$S6l0zBxmty20 zbik}f3HYN>&vQLp(ttH;IKDHPx=qV>>$S?IMG}F_aSWOFB_3Xc9*$@@88V1zd zduoS%ca!)EEpI|)GNMHF_Oi_3#pgA%C?PhX3b4!6TFFRU2SiIJG=19OU$p z%DfBApS%z}F@4EFq$Sj`n=JQ}1}KF8<;AzfzZUcdDgh$4pPJw{XpGu3=sSiWdC7TN zl4Z%Sv)4v*pp)>HXku$U9I~kmdxmInC~XI@+g6nPV-Z^M=SQBwE~iP$F>y--z7~|H zZ?+m?6=^u|h~}8E?plPHG@PKWEqZbw8 zKH5;eJYur@3>(v=w6O6$2(^flz+t%*vvilnVI&Unq+rn2j0SG{O3{DT0d6Djw$zhG zp;CiQ@USiuEoml9KZ#VmdEcnFpt-g}|19%;Ey|=E6t!%TiDj9>dVxR6krk6;aRD^q z2zRW&h{Dks8CjBb5%`@j$M5wM|D4x z=&sWNCPAIJiJEXmJ)R$jt-wR_GF#FO#W8f4G zSLk-K5KNAY7u{&x%+AeB>6W|+nWQRgCEdS5J?`~>UgRJqPmqMhWBX7(yTuVf4WYd* zo1HIpQ+hpUO9fjT{B}NVREQ~DK-!t7eV>=h7cg* zd7Fq1fMPQ-`>O&r)y@eHbu&Qbk;FkAqM{`?p6-EiNmaZqDuI`_Vd&~OSYmE@rpG)Q zS5xIV9og(^xQKZ)DnQ36p>JeC*ALAcO$vM7t^yB;P9|~=kax;)*%tAsAhr+ttVT%m zoQsQkW@e3tmI@Y^-d6y~8Zx3`uP6m}J+?noImo0#fOC4HXMrzrY6^nVXFSqWcF0r1Dgy=e#mUp z=q}{}?wK++!xG(MGV1TLb%b#qUO1fsxnuT;xu?8{5QO-5hLd(qO2o-XEIeSoH|nW& z>EZDPWj}5v(R>(jS$R}#|0rlTR_AMSL@$!%C#7Rs525Bn)y`I9@aJ_Bv!ONRO>6jW zc8=eAFy!dH1p;@V#kdIQgoe+SBDnT|L>{s3Fc^9L19$|oHH#yRZuRM>;X-Wcfq#_i z+3!&;sYg@%a+ShEK4qp})=M>`+lNu8s?KxKVXw{Q6)&aU7!#ceG_L3(aj5i!l;s#2 zYv+qk8DrYt-VZCAzwtQqd*TQ(o~^81XVXqAdJbO*pGyl{Cpg0JPCK$AjLon5Hr{Wn zBhpvR@YM?^F;4jWOo2HQc$F`19?oQ#m3l~g}sYF$^~i3kE?(Vx$l)lu^s@xLdj_Z1^{6-eQ9uyw6Q&)mL;|Q@S@1mu3G!Yi_LUsP?c+1d-ua5ARZz3R79aFpBx9ev0A4o z@dDN08HRmvk{t{-2$OmOOWk^XU*E-k+Y<3O*ikqIk-`(-G|k}g^%Tk3IJbF~mVFj8 zUX2R^Dy&S{UG!L@tKrBm#IT9o547d_H5bAdhK~}0?4GHciV~{1oshUEgGk!4+U_u5 zmXErDS0U1>5{Oiw14eMlJR$Mn(d_7U*l8yNmnuV4u&0nLB}z&2s5urtaQo=-3Hp#& zu^C?7*bnH*NA59zxDTNKU})IU^#moZ<}?F&oy2YXJ2`DhmZP&UHuQUR53dywCOUAE zbCv*-BJW3j>C&dEI%cFzAvuzK)H;lpgUy`sD_1H_gsYVbC=+^tr2XraG>9kFxpY$R zYZdFlcnE$z{5W#QdVvv2ai$g&N954LV^TT8!vjHwU9*jm%ps3!R-W@s4*9zWjCDtD zB~;@SpbvABlw>j+u~`n-P!WQ{ok==l5*z^e*0Iuh4`f_yol`5{UcLWu|lAN_@iJ< z1`;Vm&pkdb7hQ2(hMLr#>*t1rnV$LSw0B}1D%2(0HD6%>ISTJ!BOC$Q?`-*~tCKdM zLXv($#dn7gUP;5p+gerr&3h6y^@WHZ2>E3(vzX}`-e7b$FL#DXYED-KQzB1e-(cGS zfYQno;BVr~?*~3j07LFHCc0?+woiJS$ROm|^aGQ}cF zn{a$V1I=GQ=Sk-vkKELBAlsziyKW-Y&Bp=$vS1<%n-_1{Z(X>RpY1VAK_0;1OMed9BIR`M zoH0KgOJv$?t4kcwLrRY3GWq`FrbPp46S{9dhDmNDGx|0v)OCn2)wYJywSCz@3ReAy<}3omcorxCX9WfH5{Mr1RMyU~%|J}yEP*CHXgd~fz z$->gD97OZ{a9$l{UHCup*>GrRaC}T3@HAqLd{b08r5n+d_0yL4QvBZ3CfxW2I18WN ztHUnsHy-Nn7r=&lHcYggnWaiw`IwD*WhvBPL_$L!5RzEqS0;BpgQ>@oAiKXjm-Hr< zyiNh_MzO>VJ>v}l@-B7%`PK9>1mr$75Wp8MVYkzdkIwR~@RR0AnrcpEg1~V~5Yw`x z!uo4C!P=U#i?o;wLP=QxvlV4Xw|W@MUqH_89VtHfh`~Ce!76;_$^cMXv*u&k61#z( zr6*9l>OOgE6i52|+Rx*4r!mTd&8V#xpS~jTuECJk)K&UV4}3Q7^z#pETvTV9VmjLT z4fDm_{uR=F4BCg+i?aTsDgD+iqESuFXi4meSH2_G1qf@=kxuy>-op=D{u3^YFzWAP zt`04Xr^_fsP_eHr-GxO3;Q0&bIkwejf*t48y^;ugZZ98Put|(=pDR==W=@l}&W$C^Ys@@n)t_7%7;&eUIDGKbojzbqXr$Bd`Bkog-YiX-b z1yjBKkgPsYbw~z8qnP>u(Y6W{A^ES@)uIYoQA(I@$r~#)@UcIrV0{QRLoYZO=S}7~ z9z6El%M;t$;Sk*qr?%Ei{FWi9sNJlJaIA{5n#9=XcG2c7=|z({q}Nmcly6xp^OeBn z7BH3JsP`^5B7RT`>(7ttT{~K-fi)SQ-sA_X=d3x(+g0)GA@7U zyNZD@h5XJ}0YFjEX88y8xNJPlHAQw=2dz9+GN zd#AuH9PhyLMv4ibB_Dfv3BI?`5BaT;pDRbWgF1TA2)TM;kT z<;UA(fV&3ekH9QGuxc_0x}?fBLKY{P>Wqq5DR^x9(^z8bKkw@x|1og)*(K_Y@Rc%C z0%?h?;lNh1KV3{%l+-SJDoM~&SD7GV-`in}0u4+NVbGIfehp)gC8ST2brq`$xLl<5l0%hy0l|k-Ga*u_XcfIknu}!j6GLPlD~1J3hAL&TE9<6y}kjJR`dSG zYa{1EVYz15=#EMOR6VppgUI&xi5Jbzlj5CF$l7J215m+n*B{Z?YppU&?5qsSjtU;y zJdyee+<(Zhzcj?HmskLf1~9!OvrUfnl6`k^Tf7+R`jiTIZ}_^oOcN5{J|5Ua^J#&4 zfO5h`?}O4&AVGh>=N|gb^~xK{zdMd@H2Z|;TWC`afT#m)KUEaADQCW2hv=qSXoB*b zQZ$HoEb+4N!C#nNabCp5V;F&^)krytz1wSR>$?cBaGw9SCQ}sj)qf{dGRW+8$0q3Z z%seLHGWgI{YA-I1;RXkLvjMPtdMBZ$2?&_%?sl;Mj=uE%D_3PQI;;=9>KdntbmYt0Rza>wh%qD*qA} zrk~~f8)xwnUVG6Bk*X6LrZi{{>eyS+?Sizml2Bpk47dl( zXG?kp>52*{jpRyU{22*QfrBhNMjzG%m@zovJnV(0dhYgbeJesKq2y7@_l^h{)Zky0 zO%8qjCp^n5_)~N)fgb0aSNVdzPGRvDU5?6C5Xr!-xejmiVS-8oTb?kGwX}1U%FrJ7 zdXg>$S3l&J$+kCwO2{!`UDztC&h|1@3c>Fh6EJ*vY=3-yozmAIgIhKM2|mna5aeev zFXFo4K>69BSpB;qDo$fvqE9-VR>NxApzH`{t;ZjH)1A^Nik*lp9oj++2OB^E=%_p& z_`rS}GV%c!henoVJ^*6X4lvWy?$>e~qMHVwt{Wrq_41K9l1z(;P1jQ1nir@B{O_b4 zkQxu8V)zYm`2Qkskv56DB>K5~YZor0tJ2aZJQ@mC9x0wel6;cvyp^%&DRGn4h7#9< z?;AZ_{bNz{^Fn_==mwnC`Wl9S=I5`pVl)I zzVGXXuQRKBDEx<{$#JCFi9g>xZ_Q7ijG``te4K3)EHt&sMS8a=yOs;9jX2h$FCGxI zJE#UewW>0g6h?$VGyMWulmm_;o?eVra$h}$>-~~nG zv)DeL3Ay@0%L`z~^)s(HD$9HB`bg&QYjC|eI?*-ywV)j{sd8^0G;xs*E)|F5lIvtz6q@0t6xfTt~Cb{#%?P&v1;G zc1rqQoU5ZK?*tl;1bk!+QX^FS;OY6vyz4dno=7IeaOKEbHuK29)>j2|lLQ6nY(H6% zB&Uy2R2zLvke7FP$O$(uuB2<^h4A*;#Y?dUn!!PuX+>O>{4ixX{Q`hpoaPQUys^iX zc01;t_wk;*CqqG^Z0Dz+VUf5T@_R@#%VE@yUjZ#YJT8Ul)eQ5a<;;IkFNrXQbGxPG zE*nkI6(84231>)Ff*oSYwjh4~{4P(lKk=u!KwE67B2KDE(j7mN)XOQr8(uzUgDmb5 zG?;q^_)Ivc0S=xPo?@#9UqwR{X1jkv-v*ycL<&`aJIIBI$|*nZHx|7}yAF|Bl}R7o zf^o9nVr2uh?UNMih7?&?4uOAUWV6MinJ9XrS>!E#vjR)8%L=vNw0KSg@k7b=JPH=Q z&(U?=L4U90`cBCW>~p#+j|-8Weph9Ngi}yvyx_}42vc#u02xcQ+s15@w`Y;_WP@@E zun;r(W2oqJrf9k4iWML{_SiHzgoFemC7Y@U8OSq zP1^E3oU_<)#OD>s9rC{pt!m(9W%;$b-h3|H5lexfLan=Z&Y=2rv8nE|O<&VMR#@YH z!;trmK!dAt=UyB^K}jSXw(8~_x{@c){wNTIiYrD90p2)mzy(L2RtfM}y@h$swf_ty zt<*1HrAdlYpn4omaZsYpLu*F~Yx6)$bS(^ejhf@K7tZCn8g;EXY zT)w2Bnv|148sJ*L>O&Uo<&eg(*;>n!cOJDn9lKqpW8NR)-pJ zMgEC+yjg*mC$!h9C0@`I!YzKwrz9aqOtIa*24UAY-5qYGA|#*NrougPhUtn$zH#{p z^;8QMzuvg`sAQwqGkPk6paDQo4MKigYaCvb^i3Qy9Y|Y!>q{!nSmT|Xju~qZPay{j zZw(Z9h71$@XXa=)J1~wJzAa4iL@2z*UY3RCyzKHg0P_!^d$_noDU=YxcFALFl`VB3iQ z)}I1u&H<{sFT)>OUP}uy9x<*_^+d1sx>Mob=+*>hECL7hIg~5NG1CMw#qKt6jo@cf zYFzKV@SXY3R)RT=7<>d-tlk@uKBKp45o_>?(PEG*<9dMoo)ZeIe>y`(^3HaYz*=(aWw!D>unHi+s3wCrppW zCNT926ktCcvV?+OX;XcGSs26>I#-V#zImjU2&S2i`1>i3=5m@&+-*%rp>pE zrFAmEFd?jO`aGFO-&frZq>{axfMlFEg}2kI@tTNP8CHvE6|6qdyb4iY)oVxyjRzkz zE{(!MonlRX0hWdL*92&^3#~3pDdvI4g&rV_;uDg$Rd^AZu%wfGFaE-Z`};yz z=OQ#xftfeVJ{QiOR0qxjo~;h+&?7a`6TkPWqj|oCL%(9!%?By{y&`&vH=GqIR!FxSP1 z&!~*5$C4d=w8$c}UW!VfLrHy@)&wb)9Ot|q;Dkx7&Cgp`X7-}7plb7QG})k99Arp3Mv^@0P#(E3P4o)G39a7B!kP3Agph z&@uD+{ik()c&OCstVbHDgc+wkVr@16DL85TPfL8o*u|wet_7O(HIVPrLD+%flTY2~ zP>linXBMiT!SQw>GS}b)CD?RW=>4$sg3%PND8H;Ot^oKAc;>j4!g=QB61e$mBvt!( z6MblyraFPi)%4Hdb9M-*?CCq6l}TBu-v#qr}s>-{@yrTUX>nN-4^3Hb?*p`u40jD|6|~Cr(>yMj;7d z?R(|wN3nVum}fqFT8B@5;No^x=Q9vL?2|34yzq{@jWF!*xCZ1f7t5%Z?hgr+(Jbl(=Y*={LtnFs5!59qoT3b(^qg0u`;)lNouaX)bm~|WU_9wF2c3} zjs`g`V0K8}pInp=MEWQkxIMJ#g0_Adl$E;%A}k`7VP3%2nvT{*9l+{4t$O9&jlLFZ zbqcvuSrEz(Py-rXZ@{zY4@q^t$N~gdFN)|WyRbjN!fHcLjg#hQ!R={~Mqvtn;1}oh zPhQqmgJNqK+*cR4oxJQ3~&PY@9AM<(kW=6ck{C2)$%ycTz__U|0FO z{*+A3RK~_`u+S_c?!AA&vO7af&LG@^D2#U})-mU3iboyUYMEMVF7tKPi)`Ie(SmvZ z6IqWuiH@7R2j$Q5J!lTk;a;pfadWqHug)iIev>B=M-%W^^w2e_bELL7Gp)h^mt`t9 z=w@##cm70ky48*e=cFwbRKn5NS55mnh-_38#U*y0cU+H7wuQnjPsQyzJJsQ2io&xx z8`D8J@QEzSR_Hp9SDw>3x1eJ-o0aJwiuy$19Pq}2MzI+MbyH!TOKdTBm6z8r#Y`q& zvDmK8ZLd?_Cpjr=^7VUhspgfvioK#v*T&Yhqj=TDxCa2$0iZOcg#fMUUxy61Ed!gt zc7l)`O#zeS(R1Oi3s;BS~cAwBty5bsot8;=VdEXlVF7UM;T`<+PVY6&F54RQ(Wk zHo$_111w>yL9(|zcyj%->|LHG2A)?|n|6}S6_ncTeqR^-#F-ItHW}}ayVZB_pzRoQ zyhdlj&45iCCwV+4eC{aS{uD4qu^CKOw&F8_p7g5Q9j{~aA(f`xGFW^gSSviysyez3%QhP5%hdAvl(R1 z8s@K8>4ABY{9%u9<*yCYTmc8FD5}-EL`rM?S;9^0(fh1t{krpni}7pB;)hIj!=EpucQf~7v1-}xrxUfmoy;F>6VYe+DAwrv}C|L2_aed*jz zKlDRCt&#a@W@cq2BWr$Rtj))*()W!UFAK(J_picx&+H<1}aOp}j{ zkna(p57c)EdgSUF$C8c0pwJKtzqiKQ!6W|n^=jjylT^&MEu1@}^HGwjN+N`QBbk!#YG$`Kd z<{4#vy1(gxwgJH{Tzs|Ik`GJEvKDlhXJJq>;Tv2F{kZ&BGnV6Fo&E8-NALj|ZW^#- zln$BZ#gMcBImzq?-+ErRvAzVYoGij5iSA}~f^qJ)%w$2|i-B_Q$3PUBA+HkjV1+VH zckNzlN4(KB4o$wkl)C8s9pCG)A2S2cOnmM4*`C6*pSgI$g7>&Y>}!DP>Z?WHCUHh^gdXK4xNMBPvUpIbxEv$Q4iRc#f&!t~4M)~3eSTvCGDDdo@Y}oiZ!V9x` za&^e-A5E?%ycjg&T}`R>B3a64e=4R%&J>}k3&zVJ_ZomM$zn0SwF#O(XM1Bx zw#By8&@dXTZXDA_YxSN`%{vhjI&HncGh_V_8Z2g|40aw=#pkgH<9IY$DaQPs^^Sz0 z#ZA>ZJ1@ZsfLvf#|4d!*^I;>@rO%$(`^a-NZX7q^PM7|3dOC~~NHoPJGBxoO1((2% z9?>+bz3O6Gj1u#)5kFwR5p9vduPS=3wrhMk)6ws*H1EuP-&4g*?)L~0MZkjh22D3` zW{n)*``R0_&>xHe;vb1nx9W8aVp0tX%P}6QBGf!E3JpDXo3dX&Hl%S`BxfFFTQkhF zr~@Tv3NQ3o7;FW3?29)9Kzgx;Ch|M!gWRi*pxdbA*0sAZ8+2gyrtdD9-(H>BCz+5m zJ!`4S?2|I?&gvVBaTx?-8&~Y+0Y`3IOg!>WvC6PmOu`x8;zH==*&ZU-{N*LZQf0P# zqg=K++t|gTKN0RIpMlDOKyet@kl7F#4G~3K%uvluxmOg8FW^=3l=9m5c~PF_9E30w zTgBzghQO}xGYw*30BB-fqko6b+S_h-FMP?iZI*ScSrBnQ)ODMTid}l% zCMZOd@lFuQztWi#0#3KZVExeU7egmBfaSTgv?vd3DFf z7ZbEyF$Ped>DDH}jtCpw|9pM-zF_L`b)P=q*4 z@RLpZcN$N&pjeN+98wkir*jUE}o3-*irS*i-(vXgZBN|n;CKCt@=9-sXYc{k&zKp z&%}B#M>IX8GH3}C)|-jgm&75dW0fKe#5tXSfY*9w8k7g_a+#>kFw2JY0e5@9G_=Hc zbJ}v5+(1C%BLH61YfM%nDiGq}O$~XT>Mw461HRWQ$9adL-if6LQC}i@u=^##gRF?H zkP7`hl9DcEqdPGEuxxbeMYVkFs*|cL=3J_665Km`EOgP0{7NWhc5^=k8z}@pHJi%>Wc7Bt z3m8Z!MEmYE7lqg`0*yT@M6RGkb-86bXLdabt$-E#1PA!_1DBLitKj%m5O!OBwe&YB zMuy)*_lvB{k%OFQU}FfzOzPh4?CR^R@Awod7pjB5N}Y3raIZ1YoGKw`WMRka0%9d= z3yjyUE2K+LXJ%gCCJwvsgpZ*ONyRT7Y}UQhD`I70m7xy(I{Qg_@qwPti>orGf&ab8 zu<|xyKhZjvTeRDD683sqh?7&`*A6o%e+q8np@T<2wy#1jBQ)5CIcpV{Rwe)^>|rgK znik((w1ZYTFe-mP0)vEd#>w>bQXDbI^zf^#OTH_7L(~3}Kqv@`Fg4X2+sutJbDY_gfZ@ukDnK7~Hw219a4UQZHNx(HmPFS^lmh+MX_SH}a_ zWr@tY?#$VqK)t9dsw>1JG;oy#W9V%vcjYf>IUj{)5VJh2dE>7@!W935OQ z@7%l_0-qG4GEUnR>o5Od%=WNKH!u7}!9IULRMsjkyv>b@%yos#soH6S8Zz@WYCK;4y_r z+3#O2)y#XTeG~R2bSsG8_tHJ2lrxEUJ9e3k$?V$VS_l|Z%?4?@g}>i5yVLuG+^hb4 zmHg|!9Kj|R9hpT>tRm(Lzh3UT>!-iVACE25q}+%;h#9vFXt}8Krfk`cWDL zItPM_8U}Z&5Mq4eFmm;mlZ2esd7drpd>_L8i9o`WjlHVkl;6rxWZZUP@>!ju4i)L+ zDMvsD!bs42{fb!nZYALz@@#pQa2MWb52SED=;X)C&1BOt*hOIFRZLZjSqzVEA*Tl; zVMiG)1Q9D37Jm?(fSoM%TYtw>0DOIcGdr666XFc$i2!6aa;Rra^Py4qL{{P#je7FY z(VxFIR2A zQzG=jV{WoJOpWaRvukYJ;P%8~T`#7%f-t)!^@f9&Vd-eDeim+AV>2SKKlWMNRPt5UOfHMRfJG zaV}JyFs<6PFUKV&^$JhO&3Vmz*WWe3yvl7JjO;cCVkjQAsYiF`Q}sLNJbVDlhp&2s zShm*>qoHqw2Ej#8{7^&4n(95f_073Zs|*5qfrUWALO4 zpQExAc2XW+!REy=HO@3$5UUCX{M>kSzLyPBpU0AG((VoT<13?i;5qKcNVAbIjWa{? z-8KkT7kA>$$oa$dn`tNCL7kS9UCOaeQ@~1$#BGLm_wSQZO{i`V z3WLa_B(DmDo(q~%^4lKnZ6oWGT_A#6SafZn6~C}=7>L}?59>nLkoVn6pBE=0h=SLH z5|&SQ8!>!OPupWM>qQ|}$`u)mC^Q~mLe*;Z9|=IuXM|w9I>j{>WGeT^hly)b9J?25 z7Kz%kNuWel&IF5TF+9rf_j{-TtSIT+fixCFQnvB64_Mp zDh~wtG;%A5PZu!2GW?#a)5{U)fE&l21POXcneY;>u@n-01o$e z2_cAzkK=x+j|v+i0gy>PIolB18CHK4NKetsngqu}f&6~%bodITgOurM46jej`T7ij zc|1lak5rSDpep@g6byw0NErOvYIrR_r*c%OuFt&q?4TSM83m)!?!0^8n-AN>VSP2m z7we6%9@@eWALsFPpwhqtCp&;nt&^`Hu9gT+Qj4;h`C8~0us7uMt`F%rc3e!&vaU!` z^m%HK>FR6I=#!4aQEQ&YWc#?l%*ktP3$HTs+rDnfDm~zJCUYCiGX8myq)q{Mbeln6 zI2DIXXEWY*27Z>t_wUdZ#+9YB3}%3kGaonCxq@3-hXK~)PU!``E2fsb{&5J#IwQBP zEKKX(_%>;aGXQt5WQhmU3Ku;qn>-(kYlFMM?lwJ+Mmaia>9(Rk#lm(%Ju2Z7>`N0t z*AbmD&w0$8Dt3g3&Vqg|9O#SS({5s7EOm2orvi;c`IwsDTYoje4*-iLC# z^a(2hqe)lKq!Yt_&gHrr=f{D9Z~kqIxa{+gkNm54zAjX#v--vps$j#0*b@0jRu>(- zqS8043DkNLUP%PunW3f4;e0w=5_dE3M1JPWUtko_`OxweFRYH*^T9bKdlHh%R$U(g ztS4>Peh#OI_LkS2#@P|75k~SFz^9RZ4H*9&3}``M#21Yd1e8AAom1cRe7!{`G0*S) zyV?CieACKr-Mi!K*TI5P6n4@AK$;ci=gu{JS=IBANnKqb`*qTbS^Ljt<%zSbkBZ#V zn%wW+V39EZv`N^b852w}w!Gucvcj!RFEps`{SUt&(?yiu2H4-FvhoOp93gF^Oz{Yc z-q<+QZ_GSV0o+N^#wo}~Xml?}^BSuhV z0)q^NX_`B#HUBD?XP-Gl8%(L2>h=sb4#wyJoz2p7vth`CqEsJZ@y~;WFh#M)>=ND4 zo4DrrKkYZG4y!TV!3a1OEYaQb{AD_P8>`G1eqF_C7)e0{&oYJ>T}0zC0JvM@Mm(vv z^~cSdSPz)!pub&_G?lb2_07)w20OMku$tTZP|It%F|UU(!(^_U3H_8>?)^g$@&F9r zm$-rAz!=N{=qW67VH>*fgk{2LE)%pTGB1VoN`Qz(S5!WDD;*s=aOgX`3pb8QK(_l2 zxPJq}Y_WP!s1@ahy_)|{#4iF8zqn6d)j#VgEUQiD z7z~2hisXsjQAw6e-;47ybm2>w{Y)9J>u{SsYNea)P)}kf_WVSFB5^YF&|2%O_bS$7 zB(cB~?=e57Y5=q>$VxIrv)I>*f<-+SmyOY7G8fA}b>SV<;V4x{3$|K(JsM%*F7`oG zKTX->C#qTA1yXT%tGJx_N!%0G(Eln1q|8D`6x{^eI&NoH!{U3?R^V-KtT`3~ohu%) zS-|c0=qA?Q;+sT(k6{c(I@N4iAE7I49Ba_+N>(KQ8pQ+xY@b%KZa8~YfSuC*R>5(^ zD0}7;qk+q0vASy45|ZCJt0q}mGDNo-wHp9oO#+pIw#1t{aSOCq_1%L}NGn3V)^x z+`VkY-y)+^nDEa)!Ypd_$+QxcC3!>%oW9%RDwwq4L$h*>s~M4FQb39P=UGG)ak2s$ z$Ky{uXxYl6obMlRE9!!VNH@sxCg7kabF> z@}jJ#6wKF9X=6w3%B+(M2Q-_+=sU>lV}~$b`0lG22Aspqa*v$|0ACUt(-URs#!syj z$kodrWz!rFOrriCgWq{eVzLNO&OZuUFO-s>9Bo-wCX6Pr2$k#D1#l1=HrI5zBP=9dO)r1Dz|zTG9D+ zt}l8q3c#?TP2q`GEr$R@Zmd7(nuw1UsUn&VLm8jUlsSS_zoD#v!LupVv?N6r z?1opdV7ei+HeL`@ZgFtjQr^7gI*xz?A|?J4wTugBOJf2Z;RN-cn_~qXI8P5F%xjI$ z5vQ6OP?jqdNdaUjV1+x+HB-P%Q4CS6j1A6m-MPBJf=+|nd{CP85flFKEIg2)!T-AG z!%a;Le{KY*uAs$23NYA5M>V+ZMm_WR#~Yd{Pb3@GbfE!c?qD7 z-;H@L3#pm9p>;iE>#&3|n8h+3@+OF`v1Ovh*|CV74`0N2=Lap1-d*e;4U#8h!?q#m z#qq66efUC!xO>MOle*OtYBnv>f|n7$YK4mGk~pGM4_*E8>29vHxkkRz8!kwS7uFHe z8U~$EBb<&iY(+-IE@XF|{xZMUPE{t-a15huB{B`<7GH8*M&nBaXozcuV$LF#h5Hc< z-^(3c@3%F3g5WHsq0fQ6K5kP+PQKLlBv}VH)K3W1DZ=qX;g=#*js-`f`vu|Nq=TCp z{*!c5b=((fH6_5MJw~PkU@J5mS6g+Gs>#)@JJLyl`GGG0~f^Ot#bm1|0D4u}KTNKcegkmZM&T)7+XI--?!!10r zD19OA!Pw*nzPFti9prZHi)@_S@L0Z1BN>es$LqHYXNLRIinST;Z62D~r9%r2pTmM| z*uSo7t^G720eI_sIC-oU*uVrH!S+D?BAm3op7*LJ8KA(@k7>Bdtjy?~hZn~gohMOh z%qWK#L#){Czw_O)>@ST3-s=3FKLJKhdwbpGX?+H=3Z6~fvKT|YI$L8U(# zeai`YjeU~e#j5qOqySu}2K}X+xqQ7VhWfTMdHg|Ka!`Z29E?Ql2zLjTMGk{YJK+~` zxzHdXV3ao>Jc6T1NuR4(t&k1S_lBTQZzE(&zWYlK$c=Q~FXwE1mco5uWSj^3V&{V& z><9dHJ{+BgI`KsG2>_x}xG5oxS2(>793jNxN9sr0lDJq6T33Iqhl%QyXT7<+dX3OF zA`iWjrtVhIDncrclCyHJxn^Fi?hS$(NpAq+?6TAT2*Y!l9eo!MH^Jtuxkw9>$BOHn zLck~v9g!MOqB{cf#)fdRNUkDIEE zetW%5;DPo|{WD7m)(oIyAAQVd$YGv~gtj(~`gcD0f~ND10iC=Y4)drSvJplA#w?#I zbr0FtM6j50;?KuGT^x`2Zzo*M^jsv>GsH`(4!?29(?ckuwK>YAUl>B8Mjl|e1e8Kc zL?uv$<6IwG^x~@{B>}DhS6i6Z?LA1Y&6LMQ2H`>O{7JF%ozNjGZ8fPK(mD^*XcW6* z!^HfAnaOMUt{!jD!_iL(xjK|f#9!2~phD~M)UioCR>BZV*HBCYHloyaFAa=SC`>$H zEXngQsP@(|_$$^i>MP9f<(a;KhqVUWmR_-YE%e0bk?iFnJCWS1E`Lx6MQxqtle7g3 zHKWw!nND0fr)cx5%0Fn9EI2JhcP)l$qiwPdHVb-!$}4JK#kv4hxIm1hNjdw@q1R^~ zpwPBaE}oXDBL_dR$}Ttd>la@zUn^yCerG40s(4R&)kRl6H|auh-hFruCc5*3qyx*XldN%gfW&;X*Upxq1izs6m-xt6oAV7ijP?ALZqZj3;K`Kp_q!TkJ8jlC)n7l4et{e2-)4Usv#=^H8tJI+*G|gH znle7nI&hm6ww8{V60`rBi(N&rg}4H;{|ref+gf0_X-{>`HtL z<2TjFNwS&biQ1nnLV~O~2GS7m&5Mz8o$fpZ6QA`fl#{AWb~+2p7-dWyLfm8=xc|(3 z27R+T0Gh;!vZD3Nega*=B;F9eFsmLZq04&e?47&)%pJkG2S^#JFK}05FOun|ur0YI zpKk=d7c=(;A>PMp%2DpT?*_kY&~!?(IwHifXC7m)F^*weupC<(SGyegyyROV8Yn9> zw!9m{YUY>vMuj9f{u^Sy#r>~SlM)qguH~*}81-9axz;g#UF7OBJ-ua+%*yl;h>9jY z|H@_OOwi?C!QX5?0J-UW74NT^QRd?qEMGj&B+p0w!$=-5Q3J2?%RjfShqjOk1II$; z-(ZKK8}h-1KlfXUL1m_5Q;H&)Bfe3o{NVbS))s84*c}HDM6wT!;7W44lYNu?G>nYY zvzp;!xYb1Yhzw62olE71FH1sD52z(djc*e5Fv;&${Ja@LsA%$0C;O_ISUOQHUn92e z*us-1UCPiR#tdV%J(ls0MUijAs3n1$FNMmX1XV|g>CqW??|dMEe3r=0 zwGYLEcCmKNZ2hdo`~}VTVJ>>UC+-82S0i(MNVs)bYm zU{V|Ddj$Fov*+nF2Nb~~H6wzZBSXWdnAlG<${G%E4Aum3$J$r^=s{YyAK69e&w46r zv5;{?hoe@%>4rP-yej;k20OP0LHv zc|FS%jxuL<<~BoQ;+K;}MOl7H?-MOpVM-~ysyn1#<|%or|3$&DD~(@3Zyb*Z2^U5g zi$$_-+IH@MQ0v&y*__CqWH6VW3p3g{m=mVRZoNe^5+f=QKDGw8T%OHVj25`_JCThC z_Ls{!maq7(aQl39DD4-Z&g+ znzl*1DHB5oDOjlI*a?-rt@*_&8|GJ6O|h(D>pfs1=D+_yx@k+Rkb=1fnBKO~LU`*E zhwjNZ@BvIyA5SAb_~l`#cZ#;ch+=_knVY!-SkfD;8Hm!Ewgb%4%_W`hSh~YYMDkv^ znzfWmv;B4=P^Q7&vm1nb~mT?AH8fQJ~T>xb5IeqquTK_>7g5njEEUT z5esY!n8OBI=FpqXp2ZS$JAec9atx1*!0QOdK=YXJG~&p@;|Qk4ev zDWk{*Zh*^kP6ok2SXDD1FrDNp7Yk%}k`y09s0ykQ!0c&D<5krM9uQHa1k)f6uE1M7qU})hE6xq zchI62nW!5?Ea}0wODv0Xry{T_rT9#WY#!xLNoY=yK)H`QRd^RcTvL$u1U#vY7V;@8 ztb+>#gB{EgJs*6umTkc;(qg*Q)M_mrxxM66X^TEYX1Hp)*$N@0X8>uXc`xVYbR3tZ z3A3+Kj$ord$8xnKtqnvkNk(Z?(Q`nl+&zhY)eO*`aQunm?$xM-3m$7Qdv&r=)l=!? z(N}67AzJhITp&7J9SN4dwpD-RB{O{V;B;N!4*~>P8-Xv$ExP_(DepMNW1-~rE&^5~ z%UL~w|Gl4RrTtE^nE|itDDwcl|4JF5QH4z$UZLujo0_b5R|L3pkFR8P_XfC*9(ZTK z8b86H>w-zywBZk(YS&xJ1hqB40-)bAU^Vvw<8w+ebLbekx;RBgHAdum;&XU)7Q=Ak z(f;xU-?LMwNgMCMAEjHTAp->Fcsv!+rqG=c8qsXMTQMl3ih%nW($Y)TcJ_@7+7S35 zPpkOlc2wKIFJPW8OvP3pI{?-@)BuIEF2DvAgB0Pr1r7i->7 z0>M|pIIId2>Nqp6wus+((a_gSKB^Z!kX{Xm`7$%QsQhCn({CeETFszogROVEvoq;3 zV#dw=ZqeYKBMiT@Pa^3s-ta!?(d zBCJaTjsC(2Xvkv@s#{kO5Eb0Q_b#TzMLc1Y!jl9GnZF+g+&{$jg@c7WGV#*{znv{f z9Ew30B2<<@9oACmvdDAA^ysXBpX@XI@(!(@JkEU3h&o#Vc$1FCQpZ-gdxAwa0GtzY z^A0qkj!1dlL;d|C6n(IJBA>!1p)?bn<>?`UaPwZC1vGXQDX$hYmp&Ywlpva8q_EEn zMaY&6k!CnA3!k(@y8X&Ty0W7O7}HO9#-+?)sj!ZgxZTdyN@Ea%d{~_GV#p5*dp#}+ zg%hBpIn9oFpK68PG(XA1eARSai4M;s|C1FHQC~gQfg=_y+WeB;oxb^Mo2&bsQS!rT z?)#490xYs8PL^h5%$cE#U_%x6nyXI9y4)b)D&*J`hLov4`25ao6~@z>_FF8Pqv$SX zwmVX-w3b{v<`zFq7t}0w5hUhz`YcM?B{qcI4g1J24c}O;S29ojUc)`Y*{(?%RlC`N zv7`xz3!nU+ceu94EU&4p>D2A5KNV*v*eG~Bw-iSN($;(5FGpDe?j?FvIP_&L(cS(K zkt0E-f-3|9|JiO9A8KyBr!3F^(rQ)J=+9Vj7Ic?Q&V4XQM6wqz4GXJ{oPB~&peqmaPA zuUB@%!143z#cuBO{bgfK63Z7wid37hLA zq4s|{n*G=keWtDikjrTLbbnt8*$H#Y7K}-asUkbTjHUy}sXe|l8NX3y2)nAyvstuv ziaB)u^yPhXqkM4S-IFNEdZI!iTLETuJGY@iB5#01GAme#Iu;^QTlA_o05UILy2NFKCw8j zdmu$QEFV9!i5+#15pt;?yX7Sy^f6*IejKwc;M8KA(Sgq8%>Bde|5Ib5L}p0;<=8}2 zr)8KGg@Dj3+d-MSFEO(B#N)JPH5bRI%RaMvV)WE#Y-L{CV!oC=@QX*SNd`H^t-o_OwxVitdvI?38D?TjOzu4>xT5(r+mH0X zcpZi{SedYAYHE(xOQNt^F8Vv}ICD#wV??DJ8rod@8cfrvY}T@fNl83$HjYe;%2I2* zM#=NaPFiGosug(2=>1umV#>2M%l-?j-sbuFwh)80qtrc=;cM8M?}@GFawF33`$zr? z+-%P_HmNA$@#|vw<7@bA0{4eI1x<~c@@)86kwGZo8qiZqcBTMksMTJH9ac%I4I4yr zpv6;R`a70F)D2+D@UZ|?*of$No4JlH(gVdlHG#!68a-iIBs_-?FiqApQ5;~tNah)vpi#RRmp=Vu7oS6z%ZJ4-Y z6py}yzwFXJDG#`3r3UL947)gjD=cB-pH?-lYa0l}%dv^Ci_7iUQBNyUgR|_|HiczF zYi!k%@JXms`L!u1%IG^m>6qqv9W{54&uRk0T(?5(qFENOvk+|)&zI*apy<0c%H>i| zA>$|)GGiQUt{rK6=V@wX`Vs4Br#c9AJ#DEzT7Et8DQqVV=uUp%1;WH{+x-GMj zlYWsP?`b?C>8*K1rcT<^?NcJerVzTv@Q_`u+e;Mz8?M=URNOJ|Q3nA1B|-__R($Ca z36s~!;CcfEf(+Gx%tkWSgz<7{w1au1VWFlmdchY>-U;m6SVo@%22zvB0KP9#SEumq z^(o6^Bc)Z0|2(fO=|?yBUA8!GH?h;6U!1SNomCYa?U%U-j6CO;yq$4fP9rm{=jQf0 z%q|`1^kLEa3~cVc0KZU8OE`SK-H}9SyT_+bS?~s2&dj%G@_~fBHW?g<*w9XM!Cf>M zl1N~P#;phw|7?Jj1oIUQK!bN6p$deNV3o8h@+L}dKW>5G>>f*OEAJ}od^NG6uy#eG zhIWx^eE@(g6@L-nf;gb=4RTvvJKUMMhEQ3$B5gmqW?n7FmnGm_y5lAZUnEV`3469A z9NmeXa7cKos(bW^0A@Hjr5&0pE5%+*#wTyAEL{lqw~B2uq``JnS>Nd3RrON35JBnD zY2;%$11>P}MtK%yKG+tzgJd^PkS74B$T*BVujUY*9+4q?6L*IE{(BnM|r@!hYDjOzs9)0D6OrM-6E2=05riw!#pxVCvQQUkFtVGf->|K*vx7dr0 zf`H|2EVE=k-mYog)j&nNlI!`akBETxk`>)=d)eq3Hg3!(u-c*$5zUsLoz|t%&)$Kc z>a?4|v4DpBi5c!+7^b($afIQLRC-|!dA_t|4L(XQm3v^WxRDa_94kZRZq@oR42%Gz zO9=~QVr%T=>}X3J$xoiI?#>&Y`$HdOc#!SG@ z$wbG*!OY62LoaM+>uh4{>_ouE_-~35y|AO5{r_=+xTBqm{lBe+mFY!HTrG@Dl*EPT zmFZO+4Q!q44IE8ujXda8JnT*A|7#*2AHA%Jv4z3^aZ82&EORDcW@GuU6qHPy>|7j; zOq~9mBK9x6|2!B7m^qmLYgCN!A7uM)$V9-z@IO)3e~|OPqZ|ZGj2!=g{|;Ahv@=pR zan`0+5D}wSF>!aMm$Wf3GZEJLH>PCgY~XA{!0_McD*Oxc@%>*#{7*Ihr-)(}*3Kr5 z^kUZkW{a2@*%_PA%b3`jIhzwOGcj@S@%>Nvq5PBdU5CkL2K|P6IWCdI7;99sNnuMT z*}njc0Vqav^?_x0O396l^K4?97_$yswAO^o5ET*mJ+CnefN&v%Y(g|K?Qr|X7wF)B zPm-|^E+(e?d*U7izr5TYjcLIB0t{c{`ASy`ay?1Xxt_>l%s;L zAdN;KAd>H)C(0&5tp|)*_Y0V^)NPDEQ%Hh-B;6?zacZad3ntag zh(WtA!rN{;c8&#YbrSqOh8y6%uio<$)!=?r$f$*uu%jVjj_JhXLIGb|Yf#`+x#$SO zL9-x$MIyH=DrM)9u=^&n8VK4J5i)K*?!VTWKie%c@@OfHXgi1dq^Zj&1Tft=Y;M_= z`GFww;hz*~9hS4R!v_A$OOwH2rcj>Ks{>CdKv=qgb_k@03C_Df%$G{+pAkFQAjvjS zxB&!)zsdz5Y)sYKlOL0EBH&&9*s#lWx)1UqFmMcrp2(V5%PZuZLmXyRrkfHO(g<)r zl!5|VHOI@ucSWe5+(E0pt?M4Ye6#cs`secONZXDkD*Kb4^ZNU-)oH}X`cAWODI`H^_cs;F+?xA=;+xVR^oEyI#JCCYyd|`s~tJdthD=7UONN;whiHE=w37*4cGCVgTL{~+(UHI${`KppiCUst;Aglz)s-MiP86AJmT6!RWqbe<<2<|pVr}3ErmD*k9vYGoNYAcfkAP6CE(i5fWa^l6?PC%q z(eZ|T?1mowDui)KkrX1VQ>xv5;}y)D3zE-_9oM9Xaj#4fMkrl^C==gtx4N{QAE=vY zxg;MdP>{;#9}A@2UjA;j_a9M{XV*kT+^ZXq=*M^#?N()2Oro-yFYx4J>nA_mX-OMR zEK$5Tng*8EA?FZgq6YjDHX@ML6otmpXB9mIU0!)|YK)mZ`lcEpG~BoO$mdbiH;yLUb0IMmWCv01z*<|2pJuE2Ivzl71We*#?N$T9QEs>F5GD8I%3|X zBNt`G+jk-+{3le-z6!sHoc$uam)v`O8kD5fx>Ai6wJrA)sZYaCMMT~r_>IarU%a13 z$uxq_$G@rT&0f<@v~Q1q6&9iRmRzACn>$OE1Y$b6>x~A<6-s3ORQTFWfu04@4{Cma zF7RJm7NpRi2aOHkahX0#m|!K|eAEig9{{R28_5f)Txw{q!{)EB|Nc&^Zz<$ClXrR$ zhuU!U#@-#tB#|rEm5BeT)5x>9{vF#e57>TSWk3fkUFmSgjq1vx^(CByjv@C`W1dY z*bMWyi(1YGM1BO4uYnd8v4MN3Jtqm%{3C=)%P_^UNqfV`&Lg(8udek$J975wS6DX*g(W%hnL5V~%UKsP}*Rh_9C&t26Ns|Dw!kNyKYCBj(k&y2Dy<|o9lP<2=y#1>|A2!2)~KjQ5;ZFC*QnfrJY29&H_l5wG%Wn4|O zGCsfkS#VmZ%{|cUcSgpT)H@%L4zO5?2gZBA5nVSUYr#;wgLo-$ov-My;i5kqu9jA# zJZulD37CUC-G}u&>5}?{FZ;qbt@?mVw6sI>%WL1tK8kaq%`b@*Sg;l;l=N@UIR=l< zK>^)iq!6$VH2C~9yO+1p2VO7g3D``@c1hJ^|6bMba`)~0s!TYveZVm`h3@aVh1s2R z{LIKG0oV0-+BN-scobKCr*{#)rPv`>5ff9{rPLR~52yn<4VroWrkq3WF(SJG&#f5| z@UL*-%mByNM45?V&WYa2w9IJ+pn${~@@#La^CEzhjpfwk@cMxO+D_T?R)l)v?ob6$ zbO{#3j|e*&{n#C&cGFO@bNLb#!@ge_Cj-@mif|7~vcM*%r;~WJ02PL)hrfdPqA;+D z`Z%<}Yyr1*#uPPkme6J)HmUM9NHQ+}`g%E6szu9t&(A2Dt_*MKlL;cyvONukvaMf! zf$FYR64sgLAln*>fi|=@&(y8mLe3?m@MLOE*{Vr1les1FhKP;MyEw4#)N~!A-y)+u zMlX$fK12jp)g&a;@gOs}eMvFAq5_ssWWsHWRBu zn*e#HLNwB>Kb$Py>augvfH%~t(i;{Ie2NqVA}IL!!#vDey$~L`LjiS{+LQk8!R$*) ze4GT}EvCHVQ3xIfOGO3pk*g$OKPv&rxYwaaDx z*5kD;Q?}!HiuSL5EA3u_)!~ud2L_A$4MOVBWvcYkgXC$pnrTyzAS)TJ>rg@zuxaN% z@~9BQ$)PMbu!7+8TwzA6Dg^&XKmyY=$wHbbT-B-pF=IYRWK8=7NtXvH?qNX{AO0##>SGJ1LAN=$zq|3e zNYXZU_lk@pPC5M-rjwlc8OyTd(`Cb-(VpV^z;p{&5U>oM?Gm6v=pn6}NCy0Mf4wUN zf&H)H4v*}bK+lmyA?XT3oQK00oZNzaPZ_?Sr3IDLw^&)b;%d+rx= z3*jGOCFRt1yD3w7Xs|^5%|6^UT)5!Cvx41h=kY8Wlm*)!K)YWPdoRHVNm{{Rs~Z+U zcjFOdPG;o`F0jz669)8jHinQ-_-&~r4{^|MzHmkeDJ!1Hm{DW~4)cvBGhS_6mpb6) z5$p3~AQVo$eSAl?D`<@4NTYlU(EV?%=hp;)^A?aL_()Faq%QW>#$Gb!MkP z_@7PM%mp43W|(picmf({Wpo24)+Zch)D&VVd|P#IATPEN z{8tAzu=NB;`#|o(TO3e71DF-B7LQ)gJaNt|ibuITr;S7Y?|359|E4(qZ+PN=6R!VX z@kB-j0w$*aCYNIWA9BY3RObJYGqNyqF#K=M7zdD`!2X$gW19&=Vfhdr+#pR%Uc%;9 zZD+J7%&XlV*o(78MD8E1nn|wEozi!kAXXY2qHPjqU_xLIxYL4tj82B3fqpVoqy(R*z03s@Y)gM9Q=YZLC`S5 zb`sQpMq_fZ*>%b)yyL9CA(dDuj*64Yj_2HQW=?G*pWk)AwUL5xSLNr5xd^7$Fjjru zd4KJkxowt{pMN-hW?VrbXRTtXe$da`VWPj>&rDs&TZzj-;i1uRMFE2OQSgZ8@4J1k z59DUu)5XK5LI-bzq|=G%euRO(V|n7mGCHOGYUG&ZPjLGpYPnibooSS-Y4>c|y(jC! zKhP+)9VLK`Ve+PCAR3`fp1Et7+9x?`sOB9FLx@Ac9NT1yI+ynXCl560ae)7+oKD5p zg~X|~qdS-mpCfjsvN4O6Y)@O0_Rp#dCX)%|HAY7R3c<$^7F_*nEW8@pwsWy!rBER1 zjT6mdqk^d9Yi5|5M5^l&cK7Rga}T-dlq&Cy*b`~}qYA7Py@<_U@k8%BMTNXaPKkyG zcE<5_GhjgGU7LULWht+_!~c{hh#!7mJ~_+GhG~swr-YilTfvens7MaEkEG%wII0yu z{#UnyB0X3?HQt~i_lULnF{G=XhVcsV%dO}#LTk1X$k=KsEn9?@1ghy74v3;0JR>0y zPYq^*k?U4#T%Mj7mpQ%#D9fV|R5@3nEKdjYD3WCDJRELzZn}ukF2Be4D+NAIHsv6$ z9Fk%^&HM{bFF+`awG4Cbqj8q+ie`wM3Fw-A^_&`LNh}Ec|(rmrKJ-k?-eMJr+xM zuksF^opbqgAOI=G1T;|k@%+wYfOI9hRxYUJm385!Y`w4t94dg*Em0UrfJF;54F6Z& zA?2UkQZXIGPIir}hAh^%(DE0d881oMgC;`uFX8N?k==4O>0s*N+cbsoJA6x^eIqQT zpwo|oQ4gKVWDGluC1s=vW{W-s0Xq_5gBX~I_ctiVMJ2R->B&(fd%CcgxdJ&k>b4*p zo{j2;2SW;1yJyy`njDiE2v5psyW;Tmm}A!905p_)U;xOhSsoy`L;1%-0*lpXL3Kv6 zT%EOk?w0U5yX*IPYEpAs;7v_2HfwRAs1`}!5S%Pe)Al@Vls|O6m&jMXO~x6Hl}Ob8 zqVAo7Jn6c1-?D9^%eLKR+wQX6r7q)Nwr$(CZQHha`u)~A@$ERV_Svyd>^KoCuks=@ zFJ{b$e8w}!^Bd!m?M!??4k=5^s`^j<#Sg6Y!#vlF&C=*^D!R(WoQG$6IjJ1o$YQl4jwy~{N z!qrZ)o#0`+_Tfs^gVs6@9uF1-N9JK*6^=MSUSwf3k^>_g53d2FJ*eO!_uKM2MnL1z zTf3Sg-|oqv2kayP(OMmNt1HSatOJ})&qR|276R3Wbc4Exbfl-uX^D~cs_8ie5I_h5 z(`SWfXWU&QH{oGvb@yjhS4y{t&hODutl~4Hra=#X$2*C_XJq4QPud5gPx@)?F0S$? zSt^NJsusGoJqrWdl;w*&DBtA`8-AzCQ_4JB1y|~F1>CfJ;4jRRaGmbd#aCvOs*|b-@{ng#C)XzB zJEa?XlwMJF73N#8#fp{s}S=u&_io8qbSQ@vANS77YXh$VVOSpzv`_z6+ z2Xb_|0;umc#gnLY>>x6re%xN0ZfTv-)F+2z)p{!#1eM=qW|srkVKPljn2N-mw3pDN9MP!YnHIBQB%gdza-9fuu!|k* z*;&bwZ9@WXd)~9QUWQiq8v?RDa=9|I#^RuLZ8{qCvxY@RsI0S z5V=tc9DGByx|MD%*?~1&?BRwXQmm8!LycR*2S9Q2Dk}N?`1dp(1=v!RX#a4`kql{w$fg)luRDeFl>f< zNJd&4yBrvaV)6hP@|+Ur~fy(=z?OTJa;use^t zvD}(^QkW)OPB!hGk-^Et{ket=DW9SO@P_!fF>=P9zWwaV`I3bvSfyafrMHV6c`Ww# zN>nGU_d}GLrqsu;&?<#fgBEA?@vqYNcW%J%rj5@->==Dc(h80aU+d|4=Nk_pl%un) zJW0O7!aR*@1}4?UFV`scOjOOSkEMq5Vgb>{2p*jneJ^jH8)sPUirFV|SU^^MAySst zERfq^qA;gWP6_Ve8ZsjwW>q-iI7Q)-ocVSevpZgdy-ZG&=Vhh9h8H4hZ!G9pTVKl_(ob_eX{`9Y#@@q z22<3?hY?jsJ=9-;FDqjyi))X&g;m$glkuk8RV|}wmh>Uv5?7Zd{7#2E5hs4ZLUxH$ z(BX&X>5CM~YA((co*ke4f~DU6Oj-1tO<4C0iB(TE+;`-N4t8a;ZAoI#dcA0@%PyZ( z{B$l(*L4dGPRc@*>S302UiR>H2po)k2;~Wg%7V93&*DOcS>LDjlFHK{wzf}jDU#e? zrM_aG_3Z%RdVR^%#PJLM-H@MSGmWEOx55-1{dBOHYd7=mb8URg;zIrc?>MZdSQK@KBN?u|~RKxS`N2~;5rk0G83A5dc`NK_tJ z*jCe1>p4e^$Qc4~Ok)UP%538R=#C`OB>!hVdWw2Ve$Jz8>EW@w6CGi5*ggWVa29_k zYY7|{DNNyM08>vTs9{BMt4(cFO821P4-tJglSQL63cuUXkMNx!u_S$(f~G7HU$Rf3 zcuIR3o;gY`n=t9#^K`GE95>LcnC*F;dX9Xq`syUy^C)Kxwrwn?a_mTePJ)OQ1 z#CUNV(h2PLYH?vTFbFiNzua7cUb*e0fg#r+G&IhkOFrwZ-2qYJwW74f;sLQIjTo3} zLK)Pyo5*a#pC0x%Ve3?E%~vd7WkYWg4G~!|61daglN?*w8bYDhjH!puaqkJE-(s69 zwN1OGQjj{{Puw66ucruYktqtLUD02jO)#QTh%IqyvWY+Ocipj?{&=@0Dbk~)_1I0M zdFY1yPez(hKHmn z$V|I~uEYA04v|kPG?c3_hnn~$l3O=R84Ph{w)9e#Hw9Hp-tQ>JS3>jfU?`{ovT`}J z4};Jk%!xQ=ct^JCVeEU^78GQFj#R!nY2J^-z zMw>_bkTS8Mv-773|H)%vdo&_XlDU0(Alf5d0U5oVYK%$FR`c?u2-S_-Nnf z)si&3@OoGu{B&8F6%Zr!&hWEKRk3A9CD3A`SrBELG6}XD8cz!dq++`9D@04Ow|z^( zLagWS#Af&7>IHAYkw+yr_;2{p7j`J4?UD-LhtReHzU`zokHO~qwxns_%Um)&Q~W+1nxhDB|9Hp}mhXP}Db1iwv9CPz~- zv1y}bT>GzU5REj(3dtY>e~olC;QpfYe%Rb5#HcS(eW}A`x$*npb$@j$K(wj^5oP(@ z0Tx{Hl-CqoysXY39&UxDjA`|G?BTC9@3`Q9{^c$V%|kH8vxaI=44qbrwSi`awxe@l z0AXHa5ozlyHjGzGRYDHG5l*2*8rsPwEKD4P^t15-ki&X4>i;RumpKI zs?9_NSf_Yj{4dVS_LR>m2rItNe47`ayoR6d67lxSF>BciNnP&Pu0L!J6fkF8AFXR6 z6~wpBrl#JBp%jtNKi*bpiAM!g<-53@pQymZ^#hAT=(!EVdsp=ELy1qe&Fr#luikNR zFZ=AadRF};qZJKuE^NO#+q}h(2o6=96Rz{h0 z@RH*oS}~Z)rlk~pLO-dKi{>Tb@PA*prg9EZm{s*-NrW&btd4)E583*A31$U)zO)e>qeP5JYSBj|z?EQ0NKj3NA z$A5tZ9Xr&kty_H0nn7ZicE_sFdtssrlEE7rg3TV8u5-#gh0}fzXNx_RoqWa^X7=5X zdA<8W(zDngg&NGuI#UYG36U2}pdWylfOOeZK(?onLTpT0F4uNzTC$y&CM&i@ByhCD z-raicj2QU{N~k1I*cHEW-wgo(@%Q@HONvf~cM@ z*g%G3%K$9;h8U`-(ea+xwU_Usbv#1#pTE25EZC4|k|z65JDdEV#|tego4j>T$v1BB zgxzW}j-=g7Cm(1_Q-JEG@^+WYkCbK+!|>OH_~1&Pa#>irCiw1b9u5GhbZ8gk>q;@4 zdmOU-uRN=NOq~A@y$x)CU;A&?+wlLBXZ6p@{4YH#7A_9fe|uK)Nw07Fz{0s=iXYN) zlwN~8Z)7_iLT(7jV6|`_Hx;SY3Zb7Hge?}ZJ~-X!sr5ZV+@38{H66ennVS-oShXTR zJKUS413y`Xxk%Ne=mGUJf1v#lua*`$4#62-pFx?Z5W;Rz>nE8mZ}rbwo&_h;fBI${ zBi4xq$+v$Djf@mZ z1YnXIs2BRvuGY9@$caTDR@(9=c&aKYz@<<7W_dVl@r5@fU&V}6SGY4!;h))d_Ky>^?rM~BeI(Ehw4 zq3-+hb`A9aHW=JF)ULyPqQVS&g0@RNc+2$snRtDpnBzG%t=O+t7sAZsg?G&;jPc{( zoY*_yknk^oXBJd^|ArgZVh(X{z=*?Dd^0^1mASJ=l1u1%D~Qn^v4-wg8Ub5ob;M4I zf}Z^Ru9)lDIogQ^Hy}xq?>1V-z!>cb{0K;ONV6k4`r7Y~MQIK{?a8$(k6K)qMM>d| zE|o*WPY{|95-+~+K4k4QeE4+(63?2&;&)(!COdAanxqIaxLE528Ox`Y82`nMz(FNc1Hbayj24@ybLWU62U5mO|H|JJb}O zm=XjzPsBMgH7HM|nbGXt{177|AJ6%PT_&7gWG@G%>u;K5Gh?eJtGj;(bzZ3y6|g<5YH=)xboE39?}Rd4iq`rS z9qMs^9%z&+r4`|&N?T0_+@rC?JV;=mAjaE&Y9H~_s$GkYdyDF0rGIBhuY)D}g=~^c zbhX=qkpPoH5T8JwtivlA&1Z7iXY`U#0QDVdGq~CTGRpPwYB`$^|8@t%Vfj1~0LMk8 zQW%0jp8o9honAA)t0Lp|CT>~vJc?Z`N&=bpmFPHsuE%Rh{uY(d^8ux=AClkMR}$Xv zAUGcR1Ik^vHf@(V*gP`sYw8s^P7HChE?WJg{Qv;UHnepboV203Jp`$TEp%pNJ(o6J zLETP;9#dQ`6x8y{Ec`JTfvDcyMCn27?gKo*K5VIRb;e<}_X9(M98%q!tYq)wT@Y?p z>^}0>Df5lO-lTLklV!u%w9;nAmE10BaCwC&Ptv_{rJ*9!tB;H-GBF~L#tkws6A~9S zr3I@jw~Npecg!-BoE^`oZtN*7WvCO=lUi~Rr#i?{($Dm^A;-Yi8CG4YiLr~Utc~`= z*onM9WX|^Sqzk`A^pnE=_WIaq$8!vKb@#*v+KkfbQA4Y(4lnexm&<%a;g0h%p2Ntt zBehfV`-D8|HS^Ogz(J~;~k}=)FOuYI4@gJDzwp0%ia(NZ=>Q1+a+w{L}cmt z(aVN%1;&I#U{WrRxQc-5Uz4svw}#gbCv%-}xIijUwfeM$#KQY4P}U3YbB)U{`8HN_ zDMWZpc}uDAsaL8(Q4yFHfa(S^R@>b?u?D8Gl%!-1>F^IUf_@TQ9jQk zKutddSP7o#Gk)7&G3SAZXQ2e^io;I(d6SN-;l#^t+_G@6U#8!U^Y9yJ^OrduC~2n7 z2vw(BIyZ!p(jHu2*6^|x!Fw|1$7g9A5nQv=Y_ zl&u~iM6#3?#FTF|rgN(he6)w%jCQ;Wy%~ibSezm03%+`6J1f_A3q#5jR;!9n;pk@3 z$wj}6dvuwlLm1FX+7JXutO{tT1zpZJuTa&b*Y`^VP*j);#wq;-8vSiRxfoGHtjyF6 zAV;2{x5kaCA2ei>5>tRFf>ikQ(v*CBEk{6U0}_2chpBI{TL9OyDJ=Ho21T12n8h~= z=lzaI`;dt@E-eMacWLlsud4;B4(Ru>YizYyi{DoH!5|Vuoh@7MBzuAQZ<`QUd84e{OPG)j9C??uwo0`VcNDfI{h14dyMe!N)|k)|(%%DG>n@4e_qT8z!v zF}%%!zt~MwMS^!^-MHf9LxHFYzYn#V>30~WB?N4 z7KJ8#$nwmX zwgU2=h8S`4YISk+;w6th+#;**4o#cfTSzLl#CP{?Eb1ONH@BO^&)kdo4>$!}P-b<~o4O0~G04ZKbuv(;XPxCeay>`rwF901i?+B>!MtPYMRZ zKT`t!cdw+n9VFDyBmkHDy5XSzmu}TvW@3DX8l!}fbAeXYqrfE04@uO?u`qZkM(=PBC6qpmXAn2 zAYNXyGb@D%40HaVUtkxDa9D@Mhc-BD*8y4*4GKOM#sQ3do`J@LNE?p%vDHeoALojy z`0nehX)-#0>=!*>qcQwVt2ZM?-%3%}G1Go3))#)WhpB zqUdlOWxrM@oEpMnO)Pnk-kG>IC4RUU<;nuorW_d;(YXW?>JePrN|4>A-TqkW@6&YM z%opOyUg~pT%DgU}ke3P_d!tF8TaFq%FNtC+oU@A3wSU85C6-W5h1)oeRvYmyMBMDrX-?oPSUcZKn? z&iEV%fp__EUX=O#yxK8zS z2^*&55x+^DCx~&h-C@*U@Y8GkP}Jqi6lJ25(2ggRp++Lo(dd${7l*S^3TjIrGxF04 zO>tO8r7s`F{eS1$S0a|5ZS>gm2Lif36&n{598ycawHB?(0;^X~;zIYLv5PpXJ+qmk z=DGJMAr&oHmsx9jaaX<(G5W;nWlMG-nFVL>D`SCN=}h{JKwWB8*(i7NO84V*YZ0m7 z+}BLS9r{tJVFby@no*c2N}G3%}O(Kh|BH z*j^3#bGGsj1Nbuann}xJJE965n`J~6?XUI4j22Njonzp`Q&z!NDjPb?*y!NfAHrAN zY^04Kc(RrC>^h8lovC8PeMH{K-}0@b!ywzNsa?>!e>AAe1?#TW!C@My7jqsiiXPkt zuKSz4RvzvNZARBEuyZz>6-bM%Rrh~1lwz}n%JROjaC=j=H$YJXZ|bWivcQ+1d5DzY z*ihLRd37dnE^K$&bqJA_SNay9wf1=8LJO^QN$~K4zhR26)n)sWzSm@_iZwK?4YptJ z-nr9Bns-k-xWDm=^$^kd?4mNyd!T)9G$D&~Jq^l;1zvI_+DJCxRt{rWR-tO*PdeU( z-`QI4m_4f)nHo7P^D?MY|81`vM4MBww^&l;hu zS9WmB+M_JbFhV1CEqAX^INQndTIjT#GoCcX1wYCe0zw z(?_u3J6utpK=UTE861|>#;|=dhl5wF0pFl}uMuQ?QxuTAHD4MhRZPzdo5FsAsF?V2 zDsDcK!m!z3FV6_JzrB|TLNcUu*Uyi>UwRl)%6%a-Vli++_bm-;9*uRg;yuJ@h>hoHeB0iejY1aTQJ&o0hKZ>^NB{78h?=EGQGf zXv%v>!9QjK*Uek5goC{b^cyQsYe%TQ}Lek zoccXX9P4fC!9P0iv6`TAJQwTRWBhb6T9hV=wz5e2{sAN@u>x?aI>6_5Gt|fXdF);Z z_cPz-1`KS`L{~AnRX|G7b>g$q{g%3>te$ecr~X)D*5k|;e8}aRBPvkzVf$fl=fbr& zkqPCoO7GEyaUMlOm(Q;jBLx;3?Zdu1(Ue_nB+*jCHirV4aczl#sI_)Gm`R5s_)=b>Bi;|FW zI@1PMSj&nZ6@C~uQL6uyJMvE~;y;UdAzNF!zo`x;&i{yY{O>gQaQyc(Oa6zZ9@f7> zkbk8=7{vZhXF~p2nE$0$!pzFd{BN)15Js)!1v4^RdgAAfxGrL5?4N3nQZC7q*@k+9 z@9Sozbe(AJEjy0>3ZrabHV`3tQdB+_UViF31neIvG@E#6e(Z?kfuZGCXkxC}Wn=y+ zFi+FPh%{!N*v~j-@pki;B;{4~hN361bcb&@6@6S@g@5pzHHy-qP#kJ{LT_ozh;Fde zEr5b3kNW!5Ds-W;+Q=R}3)uX@0(N)3=$nlPOnzsY!nEszPf${IY~o@cysWEVb#rYH z`ME1daD8*(M@p=c;8D2>>%S1%B+g!$bGhLRc*Pn%KoFfR^3ZD(=3Ri!2KE91+-*~G z`q)GP&4Xa;CXLIlF(}KsLYiUtG8yK@Cn-F19$x&(vFN_TI1rA0TMe0tUVnTeWPO)` z7}iH6OQA26=((4fpU+xW0>_wmroi*hxKN?HYgbC_S>0a!D?%lPYp_kXw=1Swyg#k* z$c$x}RI5DFij_wkK~P8w%hDVmpm8pnT-d3js#sNLgF4h|$M@Jko$dGYnMHFHMc(|m zeFJeF&x58mk(?N4%w<5^r_f7?b_KxgUm7}zhuOMno(4!`uWCFM3vmba2k=Bb)f>;X z%O7@u#*^2z4EFr$dJn1UhWEMc5PwwV$0>+z2JyG>B)+u%j%3En_K^*LYu773*CTK( z^zp1Oq1@bBBN`qS&LW4bbl5z!VQT7bWLr`pS6kv+9GB*8AoI9g#Khq*DZj_uToIVI z5C#Ve>wS2T9d|)$t05F?oEUdkTEDloX%$k3)jUAQ8vnN10QAhC^96gIJ6rV+4GGiH zk^X26vpD%PFur9+h~KX?;c)T58mTiw@JpxT&wci4hDGs6mg5-X?H-JiJMjsHlXG-t zX=_TuZ;+(xfGL@MFGZt#$ot?&m@Y$?9@r^z=Zd=fw36q&|% z1KXMxA(L$(#5dja{+pRApJ=TaRBx+Bwim136AHPBDSFP*!D~rx1a)pKqwI_%ihf|C z)xPO*C4!O;k9La6E?`XUvRfZDpGG#h**KoXbcUzw?IZ71c+bCNcD)u8j4|p&>I`Hl z%C9uTbDr4nJ?my%cz=OZLQ0mhM{{-S@sFrJY-W+8;>)}>4KvzR;&qU{FLxVRT&Pm* zMf(vuRLxa0mqFqfcrrufraI^B@RLLWIF&F~9U5@<&)5yjrac5WKl}6As_Taxq@q;9FfL#fgX-G z3sOUWp!{X!fGp7Ax1eL~!E8AsHo>&u2zI_)LuNcy+n2mk+}iW zVZK8vB>F?Z3LPHBt&f<`b*76XWN>p_)RIr5tfqB=sXLg%OtK?fwV5-hV#)?pi5fDA zGI*J?nqNsnRn#IyCJL%7Mx`I}63^h8UBW-gCv1dIBQFuAxarpi?q9>7qLukts8&Q; zs&)YRMamhv*+BqeyUd=74~Th_2Az!(%AWc^&1C{k*Lqa#!XoGRpSQ-=u0-;(F{Wl* znDJA9A3{WyrV7I1Q9Ag8+5Gytbx7<#N^q&-C+VJSJ=Y-?Prw_z1dL^wwaQBF&8?`i zp1&+v_Yv(4?t$H1p6nDvwE~!F>2?bKOhj7bxY^nKp%>tnlX87m$U=H*9WG93Ha}#7 z=Cq|i9RBO-E%_H<^B-raw{skuq--bcg)RotC8#=>3 z%@ExAd1Xk2KHr(J%CN{dm%QO!PR!e>)uCHLLXNh)s|dVkxI5jWP#SMZxG&itts^MK zH{UXFZl*sF|4Ixqap(ThQ{G41JJ0kT?l+5U$ZHrguSVH|{G`WPNpR<0=pA8<7O_}` zVTj}4N4Z3%zN$a>XsGK4O71K%0g!*ab#LOmg?ezjdIqz+JY)iW3Jb%eFTp$Q1DbD5 z2c7syn|Pi1OF{#!S1|WTNYA8TLhQ8@3I*1rD3)Y67V-?nBf?gDf%hL|7O;|tK5 zj&3!2s!oz-AbejP59-K(nKKQK2O(sqZh%|jBthZx)#*9>9TWMzHz3z5426W%RaEW5 zdjj$7-sAD|Yt%~NXK>posEa?8+3z2MHB22eMN<;WW2^62bHa!h--jPAqBQxy3MRB9 z`Ko;2Y80)mEA$^^d1j1k>sv`LKRL)?uW%H%?_UNE#CR9ZOFv%x%k9BcXGl2Dcth9S zNu4Z>m*fEl6bOwWQBWjNNs}T&Bzvim)_xFFM9*#Z8^lU0K+pBFQ6CVnHnOVANOI?a zEOl0G|CKiWM-%?f#haLgqm%RB-0=UM>}6(TWsoCc`HyI@{4YyoXPbYEy5x%Z@nA#D z``;g%md9^FfUJ35HDLY}L9MN%{Z1dASlvLz@v)$9GZY$=?hOm z4M!6YzDeWUyAzTo1W{LVD!|amp|o91H@RT~Ws%WUF}<&LNaMT}WVt+HCDWT& zG>>JjIGHBgtgduM8Yszy%m*E8v93%A9c~Fr0cOg*TOK(2Vzji+WLm_Rt;lsNR{FDc zAp*>>a13KC{KG1prDWDr7~b3xBxaSY(LcF$NhTtWe-&W-f3N@7!1=$H>wmsK|1)sT z#L3R`?|tYt?%D$i{#59MdgpTMV{>7CzkOl~QQkvznC84>u^xJf%W}3oe{)&HvUKf%sP387lwAKAD2!B>_QmQfJ* zu`KU&&_eGlgy%jS3a=WRrToh4&Z{P1wXZZO5GZ}H(JbyqHHxSs#qPb%@-TsZvfiWl zlKoVPM~Nn}hXj1O!d&T^oaIwMANk#{_UCtHyC#dG+TL2|;f+4K7G8o8G#T3COI>&L zXNTeVue`hsz9l>VADbF2Xc^+@kO5TbTnH-$qdt^;%oQxHSk|@`w^i7~#EzDgxf*c% zbEg>WnmGh{Zwih*?P;vH;DK6;M*a?>iFvF})zT>yuT=dkJ9o+h?p?dFl8jUXb%Cm2 zD}GOsy9TPCy6J~L<}lV#5nCA@JvZi-8i;?*KX4kc9s5=?r;La6uiN_Hq$BC89yEWQ ztIBu3?_^w{Ujw*~E%UI>8g)axp%gWa@=5JY&(R6OpM}$UhE?QwJtAmT(wA}iQm#it z`LpFK|I7xucIt<8Q!mquego+(M+a%*NFpQ0me(kr6(83Zj!-XpN&jM84!OJMlYJ>X zUywT(M~{_#>-Xueaa2AhF7K{6gtJ4JevZ-{j~fka@lclUva2hZ$WHSIzBxHCgCE;7w9O->^w@Uq%DY;WdUjZU!)gHiPHQ z?K3?=##CGoHc1k*E)>k63pT#mbY5Q!@_e_yt2laSn(=-9y#HZf;hyiB&w*smlQk5jVykd~hISp-fI&n1 zUbF((hU;!_DKb1g=6Nc6_5kwzWNg6QoN2n^XBNzX`o-kjGp*OC0|M(hj_79$ z83nXbGqohSWr`+Cj7Clq9SlN39ef=O!0CmdhoKMsfc9)Hm`IqhuuYRMm&PZ>gP}v6f2A~~nkwO#?kJF)&r3eGn zYm~Zr(z%kV*#KDj8)~4a+oAar%puj!w|dvI7txk^>P90&)%lG5fG%q5UV%S;_$C)A zT8Ce}7K}i0E1B?qwUfU7wr~ot@%i;6@>Si_>(V4cW1|Z14c5l&wbW00vLXOxck){i z*aas$!-mHHI($Bb%mDolw%~e)g>mH1ivk2uRoq?yCW(#l0m^}+G`o!>KZ)Hu&=(RA zZ`9^`vtay95=ouh#4YkuH2Cv%Zdfy$zK#ru1TvzJ1m<}wF4F-yz|qMYJQv306hxz{ zhKL=~z2y@G!BP zSZSS=uoX{*TrEd9dgI_LS!K7yI$$_N4!*}`HKbDo;DA(*OT6OIK^`7v>_ibrRs2@5YGBwZ#imJS>Qljd%x5kCAnM!k}=OPaW;V1%H zb~Bwt*WQ+$=cs>l(p75_Vj>U50$l>Fmt?T47yq$3scBq@e*mMuz|RfN`T4f8pp}~w z)D_@9uB36yfHIfWB;bq!oBw^XDG?=v*S1fFWOlTOS-;{Z7*XY(1IIN_oRqhIDC)D5 zB>&!q4u;HAahf-Pi$p&NH_8vLhv1=uNvj3syWd$Fgof*?P??&3p5@16bDqEV2aAkS zg!QO_)NG2S@B>Xj|7ho9)6ALqQXCe+()_*HeE;eQc zWtU&h|3G#AMX&h}Rr;SQgF)TG_%Dj)@8|z53~FDzTTBxq`*~^p9)yioDVb`VoJYYZ`TcBLm_}*2_y$vLdpg*P8ex+&gaWy>s(17G z;c`f|(-CQdgd6;m)VYG{yFOVGy8%a-Ae?SPqjHE+n2_ICjgnDE5)i|t#Or1ihv^7I z!DMQLRQLy178~%h;~o|ZZ-T_@n{#2}RkTo&Pc`YzVH4t;3 zS!FoT_z)x!mQd8^hMKYs9r(szh5>AjMWClyxwjqBS{$d?k1_A2bTl=2e`S`qkXqvm zZ%dqNotbExY#Vf7Mf>i_YqiSHS_xix@aUJ@%eOF+ExhpMUkved2P0{ej(Dzb zqSeanh3`@E%kOF}pgc0t#GJMKt&h6#>21XD^b7KWzSQ-)^kntsKecnccUa3?M=B>f z-d-NPLhWM&&&>*3;m?5pUA$`w6hYVRteaI?r&EA8SGF0( zG%Y02Rb)Nn1icxVW?_5l8*@~5wQ=Hf6LQ4LIyiKxL`YT#ftTL0K6?B$7{o6V>>(%1 z=kRz~vTnlVYq$sArm?B^kk=tO>@_K-DS1j+?L!yUchE>DxM5EQGwf~|<6*ua7$TDJ z4*2of5bTB1bl7+cQt(bP5*wxS{rF{B*tf4+fBW595AY_g!m8U~4$#B+^#szU(EA7T zdoOeh>+`n>sMhEnC28a5TD8i&2AY|XL9Es-DI3(F#ke%-;mP-fdC!?X<(#)ljU0lq zc{J+zv)HcKKQT_|(3Bw?FQk$<%Qz=T1m7FwK}`fRlm;mMY^6lfx&122k7yC0cD8kyzXV#m@l(GxB1#UyehS5>suso-=Z%dVC0Yq;^1P->iYN| zN!eBjd(@LsQqV86mp1O&wN#&&r_G{ky3j|?3oAD(UcpFfO+6#!%%Yf{>Z;Cjj;wzvBXoOAOLbNn^BAunP&e@_`c91W&U49|aOagc-A zwC3;w$@3R+^=BS@G57^-W<-FYxS!*gaJEm^3*#;QLYhpk*Qn5cxeBbgbsf6NDmKA?j^&@AFHQ{-=jo6*L z?-$NSSNQ9q%Lvwup!bG*h=H6f{ABk1*7rV+Fu%1b7QXcuDN0F61lP*N=x zw9EHkjuQaMyTX~dQ)tFCn2-|VI7{lND?A3ZVbh14qna7M?&>xKI@?DE3|C>znXy)2 z3sdTaZoATAtAui$q}-sk*x8pX=Nc++V{MKTn)JcQucF8hiX+1k^G z7LxnuSZch!s0;tlWm!OnlWJ;j^VBI$n^q5iEgnYcrIYMBxtzs%)ZV>Uj?f?)$CJ-x z6fSSy%`Tm*wEpn!6NrUTS>B+wyWipIhNQq7b_+#Uc?30tBC?ADQj7PyG_-Q6<`8Zd zX9|0537?(8oX&bvyQX%_pLEE7Z%N}~eAo3p+L#L3q9^3HD>OgwKq)xzugV;H{lfjE zv8C>^o$-VXIb;3P;BL`f3*OG{0k-{o;Z;NCZ`aW|7`eYF__gJ3`Q2k? z_yR7zXum27f{C14FUN%J_4#x6$UqU}LUNaMaMuTQkEaS(gC)`hfx}SL;qT@Iz_m9CINyT{TU_Fi z3?Q#g{j}Q;h8>@s8}HEV3F$^|qiPKwq7B2#Hrv?k+8yFNZOuhCOC-cvyzx&uBI43K@UiQ!%6b->nc3XB#k zj8*|Vku4_sXOBSolf0vF=_T`6hI5k6K59=H-5at)9NY3JskwG^G4|2C&s3cYB(lmy zr^Ym>i&-R{XhqxqDGmm)*W4>2QyumD<(9ppD}T5G4=pNPIl8{=Pb^3iu|E8!vQU;D zj)6q(fuVUZO_m9c50Dyh1B*{C%;#NhtEUCrnkAw4`uo&?nnn4mp_Ex6R6#8_QND`x zo;H>{9-~Q`bSJ*A<+(FC9v0p;_YL{GI;^aH235chponfnhiWh5i=Y*n`EC$c`YT5! zW+xuN$FIYNsCit045|rTN1Fut9->^O3-&_iW>^$R^hdhcfsajdq*{9pkkhCXK4Y*)d+Ya6lcAI>Rm%=JImp=c!K=vM zY1=>QlVpCJhPmjVG>-3-QY$pALGHSXQrk|ao?N|YTxdlV@8?FE+(5v-!EfIaQmQT8 zLFi7kg?}7fT=CJk|7E#Pg#|TSYCD~5mw?{Gg?~@V`sE)iP3k>&FBWwdtKoE zl5_VL_MOw1m{NoZMCaerZdZ1!pb)Ba^bm0}J-vq3@5Uuju*gE0IOc^Ptk*6key2bA zflG@?Daoiy{=iY0QVyW=wBzsEL84uwrNbc7K`}txktOIPCMJV-R8Aru;eNl6&n5hU7zlSc8YVHRv z7-8@C@6#9s%a-She(y0x89Wk%OVM$;bLLQm;j`mL_#-*I>?^7yiEDI2Kq|UEsm_7} z5%#hdA>wnr{o_%6Ip%y|P^~(yBVtVMtNg&0hfEs5aMC1|_PmblFg(zjSMhX`ChHUJ z!@Tcd?UI2QEkLObbb{PNg3UD(7+sqL^e3L1hP&rOH%LNbnK^ERq7r<(!aeiL6cyTT ztVR69^&hch=*(+B{-(^risXC*6;t^xDCkCfniXk~o&lkoOBW}6L%QqaHfhNt*oF2k zI=o~^^-+H*o`zp8Lc{hx-A_4{BJ&uZvji_iSsa2ZI6rqW)xZHC&q@|cen zHCjx7qbM0bevzBY*_U4Z=32Xc)qo&d!jRy1fQ!@=wC#i*p>#!{xQWk3V6l@xkzA1l zat7Mwwl;*`LPC}+m?wvIAQ1>xF9uk#&RjsE2DjOukB|4@lV_>Ng@ zQfiD#(s#9!ftCB~f{}o~Qj9GvkKAl3`m18yE@2buR@kI6wP;jEdr+yBtSXgqfC<%)uvP2vxtWd3_h9to> zL49pPWIX?1kN$U(xO^v*c2pF0wLb8C^%ivBM*;3-Q)#;no|pXxF}uV#(HmrCUcDj4 zvu>-5MXXz-v1gf9OB&AKT9(W@6fZr`@rneogw$F2x?AgUbu?5$Lmq`Jh@_W7vDWf0 z<%cZm2qD=^l=HeV1XxJDk)i$7Np|{`E2MjL<~1k5BfNE5JDyrbQ~-1g0pu!K5Kt6a zn(~jo)2mj~Th>G2=~H!|$jWYBX{2zU56|?MFW7YHX3_Xj5ZlAv0Em5Qx#@d)0du91 zjX_mOBZh>Z_zeugU94QAUctk$m}XvNrwC$+1m4w}9n8UkcPwH8{})^5)Lx0!M%kod z+qP}nwrv{~Cp+A+Z9A#hwkxXGww?63=%@SU`xk4?cg`^c%={}X!0(Xa`Z2XS5lDA^ zB%_t8iT1gU9mXs#W+hwKl;)995l7zh5NGs%qqxkDTPUj)w+6N@)#tXSbcJ#DAmL)A z+!upI_$N1mGtBvp5l1XW~Jx|E9p~R9z*A`+S&)9{ZeP{q`sUWrqjw#!+>s%mK?Bb^uc= zrp@&E2Ql=u?Wcm$;IxK3E}l5~P6kxq!m5D+3a#nf*mvLLlFHJ#Jtbw)I$Dr)xE<1a z{3o3z7=vf>l1DmfmeQhw+rt_(ZwLRikbA^eOy{Sv`d}(s%H}vqF8V3wq^B*~R(V|O z%d($r1sNB2HIVqer(jngDG8~k;v5JmbGGH2FtL81s+-uy9yIISQ8P@Bh%{^Fh_nR1 zsP2RHS_$qx*R0V;1q-d}m*@=PK?cdtcyU;W--wz};1SoJbC@or50X7~=D@}YZZ??! zB41TU(b3m#-$reV---GFz2X20GZQZhf|`g$0{%wxJsWeRlCZEUN&(`ELdv(H&loud zj{;=9zs_H%=`{FrGk}%kL134TkiVEL#2EZ#8$>;lhukaZ$uIm8=#e&HQ29Nhdx|W2 zB4NZ@lUP%TLXaiJ(6NG`;*_5%xIStVaD``hNZvImS;^=ExKCw{n@wAt%0JsGmYA`{ zM@UiZUuuY*CkM;wvZ60IkCFkoAkl1*HY37zy$E$dRh7v%LEFr%=AI$R#<}Qm#H#n3 z_eayV1{CjAn$|KFynWMw2g2}mAXn|13#f4$moLz@Rie-d)D1@}L`^6#8Z*gxmdCc* zyv45*hYq$eB`kZwM%_dzsFP8I@2E#&gu6sx#0EKdhEaij_Exa?a)=MA>okpa_;Im_ zZdN2kE4rza=fVy`w+!4XN;2BVzCzZL>!Ba6mlGPV+#V!48Qoo9PWO!uKeWfTsJs=bSl}e%LB%Sb$E;qt42BDea#3Arb54pb1`AVw@Y2J_LCl^rA%|uCp~&V z`k)8p@6uSOuy0j7@c+9WwnKVvRJDBPizr7g(8oeoPI46azcvk;YxrGA$vxZ}dkYmv zpZqihYcOOcHbEZwp)?s8FQ51hv0ML;h)$lZ3=5jLGy<+QN+|klRJK@9-F-xY{%HS* z*yJnA;PZRvV5pGgw2j$ZM<UIpuzw^LC z08xzfaR&ED|0+&KHEA4I93c-+#iE4FVi|V5CXz<}U{)e64S@)kWDCg+De30;kfIEv zFD{K9uIb;TFozd5EOS_~VCMsNX<}hGbftC4iHbghj3sh^_ab&($DAMJsbg)oe8T!i z=%~4^w@~Z{6^CIa^z7fz_)TYfrt3NJ>x9+VGM*nSs}@4#?U;`4b0l{D&!8o-SkRHgBUt_YH7AGE>TW|H&O8-*BQ-5{V&6Y2Ha3LBtPdQ;!A_k!_C!oqQygzoq5H=aPC~#uPKM z$Qc;4uOtDryVz`L2X3u6`>M^_0OY2~uL~etZA|&uV#o(`uisnw)Ji_)*pyB?9*Pf5}H!1};j@;1zA>aZi)6|n^MWbiQ>zN_Jl>&T4=?Dwb4jwEPgV-WnwuH|dgMqc+LGqvuA z94A*a9kFG{S?P5L)UY|ZPO@y8jd%PfxInpH?!wr6sw7%~#?1KU<54Y|8jCoB3r^@c zKwO$NL{snblMpZ1;7$lYHII8{hUx4^j5_+J9olPgU0P4@kOP%8^>HA$C4nKFk#jEF zG{8^Ba4CxQMt^X&asMKb@n=V7*78(}_z#3a7~~TLnS_5k*k{Xls=Ao7-I7+*m{~VP z-@$KCmUcwBOMp`?{UIoj7?#|M1iQtMplsxVYCXnPqG>I7C;riCouqU)7eQbP`%Caz zJ&8uy4*)w}EV-8<5nuf{u8RqY>QLq~s^}@Dy-Lx#P8K;~DBr7zNm@FUnOa#3gJ1|4I^VE*a#&Ccr)K*N}1nEwjLhjcyD z^9N*u0-qS=_T`Q0&s++Lf9Kxz-T2~YPQZux(^@Y4 zSz_&hO4kgQ0##`&zZ;G8w<_DTuuCiJ_jt;0Dl8N;3{9sqmQuGWdm$%ccayLgezE>* zVnO$gq5Y%ot;y9yVw;`a35-n7`=xss`#_{RkvSzM8GUE8e$)Fz&X?oq_Z||)Bd)o} z4WJJW7-*|=o9d527GKWReBRj}md0ej;rA?guzO_1`*ABaz9wMPm+iZEjBpw#G z5izsslY<|uEKm!IApw@%%JC<9UjxRF!^;VrnD-lf$XcS|M*tkHVbbkQJZ`n1_U&}q z;y%n`rEfN6RQR-!mNZ?M%X7n|uZgSWnOQU$ zP4>aCZe#~zRcUuy%%l_7w~IY+xHp1io%8KKv9e$AV}qav+#ab#`}Qd7^#&xcXhu=q z6e*NLT4VsA+`piU;D%|5ikUON%_-)nC=+}35}eSLLd16Y zUuXX;xgy=mrKzZsYyCsbWoegAXQPG?f}_t45}4`5o)!b=!)pSX3bvv}8)%L=rMjJ| zKq^=#@(UIQol1b$pHqfiEJIBX$PKVyXfJ^Un?>Z_9$I;dHeq#;nLl;#)LI4AS(gw2 zfWQx-d8WYDrnW}%oBh=ZJhw`wc>(YjZzY&r+Zp@Or*nLbzu{(q!=Ja!`>L|PiS@NK zSw*zsu55o_2Gf@iwy|;vJK}V%6$ZA3JT&>gn=BPC25$@!n18od<0dbk>vX_uxpjdu zDemaQLMho)=k1@aj4G-9aU_(L4gtnb_Yf${C{{hTa3W_|Z%ED2P`qDLjsdEvNF3gH z$x}Gh&gJuN7EyQ$sz#aci1p5s%Qpn7443W8-^^WhB~)SvYgK>87DiW&nDaqsE``@l zXbl+DIcOf}&l>6Q-05X%n65Pecoj|=TsZxs<vkVX~H!VHfp+w**PU_NleWH{XLmFtA+Lk!E#U8o61N@eJhb(U`3K$Ma*r} z6aqbqeFt76YFaJwXih9+LlAAHeW-DiXai)Yn>=7AyM{130I{TJ$XP;B^jI_Xg~Ht| z8vG?&`Jy4pZVIu}(kB!GOc62wrQ{}kq^FOBrNmu2K5fTt1|tm2}_I$;<6t9|nza)lnL~30F-bVgW>ch8mH^ zsQyD3;KOd%$K|EQ*THfPeMsAE8ex+}VDxQxacQuzn&$5jxxkB)?;Df;I{=tW?EHh_ zBnu(Y9vlJz!|O#$_H50zIqu}Y=&Mvh$4Z`i)trRoL_HIg74?h-V|O0_&w4ARoR{&f zd3ptjGrE>+iVfJwf%;p}t-RKv1(&;gWQZkt)s!Js$X1s}pH325n;-fsW6|JkK|oe> zLD8ZCLsrg%3RaLsl|0c7q6j$!du_7a;l-3H+Fa6E_a?Y_bc?{cy2-|`GFW<_<) zGM#>$LepLa)GCNmN`vtES_?NeQM;aPV|~4#QyEL_9&&ECwR3Fuf}(=LGg3+jGZldp zh0j!qCR)txnn1clR-6WK9c(CcD$oRfitoET1e6DxiR$I&56^Vx*hR%%*tlf5gWe?V zTHK@GEUG`T8L-$U+kCBJ&W*Gqlie`!y$30lzOk%D5t)jn&nH#`34JB%s zuW}kxo*9%psWs2AmS!)(fce(n79+Z%Fak8S&v#Yz&`j{mcDZ3eSTa};A{dS!gprAV zZEm!=i0p7eZg>>QZ~TRyY74iL4sU6Gsn|*=#)*k(H8zKmj94)e-p-wfxk+viI^WhE z*bv6}eJFOAvt-d76uP#5^&bLdxY^xTo8|?=gcYp}bzAZX_tnqpUGxmI&9d@RA#r>H zYE0`8Q{T&s1a!uREn84WTX#i%prd6NwiGU?Np+BVfJ?5scJFB5Aj|Q^iZ|T>xaDV8Zcs z|M~85b+TlG7afT=4nuIQyJ1)rPRE zI&8Vf$LX*Ey^dI)cQ8d$Lw#UgBtfMHX`)wY&4M{&U>C8xvW(`R;o(nvvRv-EMt1oY zxwOO`P!Kr%_?`ueg|9|+A0YrBKCsc#xsa~dJLLqgRBLlLN!?4X*c6R=Yo_eu6Trx) zPy*Psc4#FY*rG3N?2@Ins{_@=EP+!ZCUeq_LF6RP zBEdTW&^7%quYYnuyD@6Eoqne=AD7<#<=sTJPtP%WGI&~^N zFCfIF#y6<1M=KcZ?)6yGT}YvvRqtaBddfK6hgq8Y9QZL;`=FvmU}s0}>PIwTNg zyd}kGX!@$mWU+z-%&Fbi6MJrq?$DonCt0q4H7mHaKR_0Di48}(R=Dc#x^-$~T&6gn z_OhJV_Kw2o$LYj>CN_>r>FK_;dTD(sM0w$6HCW9@lG&4Fy!;*o>-6&l!|N>w`rWj!aUU%?s}s3qHMm7JFq=eH)$c6H{nprgqd^ z4q}6M{NK18qQ4?f_cwzggM0y{iwH%Vf5&w-!I9qq&=I6C5rnzxedI4B&=s_0;y{4Wv$}e6K26Ico z(Q^GIMPiHqb*Gs38B76}z}_KuERlbL^h+_D8(sbSH3vE{KRHq^RX%3-jOSZ2wp|B8 zw?Yz3m``8yf63X@|G~kuGqTa?N_M}pE!{jPoqNPX3gYkoOYLMSD{WnGPMVnH6JCs| zuy_1IvGC3`rGqbNBlJ^4{yLqYj*zNQ^sb=l_4XuAp-fmDI)kQFBY`_C+eEUbW_6-Q zfz*(TQm_#$3J6d_e-#t6Q7n-n(o`kfbSJL7N2Vve)yP9X|gi zN(r(v*aDWwTKvt%{TQ1{oJtj+VHLtm_*KgAN%DPHk0kLCS*oxi@UW!mG4{@wv44i9D0GWM{Q?&vZMza?4BURym9*+%XB@G^|G{p$h2+X6|Fzp8Q|N>$)%|LMxJt73R)*0be77FLMtb93hl`i!D5KdrkJ zD>r_r>4*sGiZQF(3&gD!#X`Kl6j1=wEt_Sz)9hmp{GDDKCo@V2s9I`%G>;^Hhr@wN zEt40>$_f8Lf5J7Ftt7_VzVvP}{-O*^qBj~cSS6BG75chzES@69s0GzWp7J_Gp(>OK zlaWOk`TV}VF)?sUrxu^71iG|5{|gS4plF#mO#{;|_AYF9dfh>C#(F|HGQwt@IaMi* zv8%@RaP?bm53@fJo|%ZlQsZ6ybS4>7p~h)#-R&{wUEYfqemeP zt=PdnK&1!C!nO2;A)6SW9d?0S^|Fji2K6w^9DH!#$~`Y z{0Ek_!cLdVB~xgSySAUT z>rqiqhN$Ys0?I{+EmVmAPhFH$B1~L6Bi-`}%Y0P>mx#4L)~uZiPl`= zk7OVC1?>>K-`|tV)Su`ATw$mJQ1}~DZ-_2tI%8~JT8 z3+R@PVi;W`t)f?q@5G{$;vX{Ce+5Ud80PP=$WI69$xs6|%}yfvp%%G>UGU5;eQ>Qn zNhoY>3?`B3DRHJv`TY+M$zweF?6;haN?1#fm@~WzW}2Gd@ z;X>U<*A~Rn-Q+P0aISu3$W(%r3NauSgDq$_rrSmlzN)YsnV)NZ-)u|SR0#YhgoBGu z%Z52cFh_&?DnadTX7#JGfQS!=!8~IkAc&>Pnaod!L&tXw2-bQ_f31}|q4qv3?HoKc zZb{TeG{0hd5Vlp^?ijIpX;KpdgYyUE;QfA2)fg{!HCt%@42XOQjnq&@1?uo=Dd03B z!_FPYt0)7Jt^|@%May->is-DVFgW2dsx&ieASF1?sKOH3*;Ac*ZiTlknc+M=E;if^ z{imv?-?Bbrg03A7H>2*bN~ICb0}wUkZIsZHoIueJcJ?4-rbz;_6w+^u-c!XKT*RcN zF-DI+;kC-II--F81@BhGZK~}OMAjcFD2~ipjEelJhI#>UfncfaD$kE6AD&VDJP!uU znFy!Ea87A!yPwkyf;G~B5mloy?xY?vC}YOe@)M7Ax+)9ep3{aqvHOE7@Fz!D5k?Tb z8jIh^Xs!zZebND8(wHOL39`sm3R%=bA=9$e|MyKXhIP=*F`*q`)}s|bp6=+p)lC=LAwgGpa=ZkC~a-@lpNrUh`FU16W zgHKcTzi_*p2(?EO{NcWI!ZVUSG<%w(N{SoLAtJVAYu+If?g?$VgT0s)_TnG}>!eSh4KkUJS{A`@%iE)d&t;&xCD5cf|1T*1>D)%~Hott=t_o2y}eiz6sv z5-Pv=qMbFmtHq;7Z%MJ@MLi!=|B%M9aF?*~+Efy|C?~s; z%o{pc>Wj*ZcX$W`D#NbNV!{16Yw?%RaxGJFK|KVqjwZ^TMtrpleuZct-S#533!Z%- zWLDJVgekO#X$ksbkZVDCuTjlSLp;%U=Gwq?)g3%~!-bEu8vQ}r1NqmGS&3rUyU&{f z5|6`Ivvg|X7fCz+{w}{PKt~5dTtBFlQQUo3)v#eqwd_l@wSz{qFzC3HC~2eYmwfBz zWz-H9Pqnci+!bzHVB&fUTvQ&Eh}EIE!&qFj;sEa<(HrG-f;~GY7<^b5`|v8o9BXr_ zWyTL+i6lm|9`n!2@cIHEobv36cUoQSFLI_i$KV)i&{`e zqj6Qb8h*##V%z%1SBikLUaBNgLtNVV%dwyy0n7Y+6;($V`vV^9Ji=e0JIy<7zeBtc zJ{Jj3SJg4~#D}DDW36fY7$E!XrA55IbI5yWf0`1@rqk;8^MsT3N@jXlgD*{7l50NH zwi{Qx{+9Wi#e|Dm2Z*)*6~oE`xUZz!wNAZie8MLFh8z#(F;D#)K7OKO)cSJlWN6@=|($!M40Z_egGRfriBqjG%ALC}so&#QDTp2Bu?1pHC3 zuRN1h$37_bd-Zopfwcs$3)h7(&wEtEMgqj&_SfN+3r#k+%W1IcVY=_r8PUgZhdqA<-;3;nGmlM5)`c5fe-h0+Oi_ckL4%8l>_v_v z(r{1_nTgBw^)WJC!{^|pIbaCyLXOHW5?+w+Ve~iL#(0V*tGoMw zRpjZa20@)sDYPFbNrru;Ff1^rnDKmkbKSUp^}TMOWY0)4N{TlDU_DAGbf{Vj=$%tX zZ$<|mq{oVg;!w6fJoQNgIZu7=P1tBQNXFRMInaZL=LT08<5jk&ColATzxqP#6pwQg zP?#wP#H~-_t_Fc%;$Cf;8IsEs@d`Z2M&C636a{yQ_6`^wysvDJYvJ6@&!gQghz5L6 z9OJF&&}26>gF%IxMb4%+hZ4zR#rj0(_X%gR*@D?cx?>_O(2~M3gAmgUx9c-KVdkh6 zz;F#`8U=phFVZKP9!1BevZ9 z7jXPml#_h2IH&(|N1hT7oU?@9Z(+{*TQFW@jN6tvq&X3adt|4cB zM1V`FwX%jkZuuR5n&g|n{mQD**>l%jj62|mFNC5vLp?Jeakd2E#_VB*y@O!G=pZEQ}SpIf+EE_ji-`gCZ8qZYQ z@EsO{AdmX~MB))8bJ9z98+)`r<*%0r5|}bypm8DBqGi>HEkA_g${j_H;o(TI(2*B#ggkk8N1Hi#*xnqDBN)EQm)mAg2_$m`Y~ zd%tQQ=#+oy3B4)t?N!Gqoq+PNz%0_`on9(YO}nugE95!n3`m&s$R9>J75|ZRN9zc0JoD zSf~HneeGLW8_s|zrqBsL=@@c+$B%7V(9IA%T$ntC`n`d5SU9v_lU53f&*zEzrhZB} zl(9__T$4%TE}S+z165ZEX}kBPN_;_ng>WKts|es<)oDky$@6cMoj?SXTYu15p_jlz z`;+y$Ya2RCrZJE+b)-AxK0|r6*Ny;wKSw?1+q;XFN~%_0ni2LkE`lh-D<35p$9VTzEh$mTTVqw{_R| zrrNh=#Le85P=io29YPBgqzl&;jw{623!PzLXT8Ub@+nxI8aZ@|DKRa1&arX@XDR}J z=xwPOc(#}-%HBD&s{wXG5Hb(ga#6|3^^^_BUuwbo9BYZ0b48CS?$Zt7FT0UkVFqk4 z!(9&z0JIw4XDm4$)REMl!?`rlpCSKE>R;>zAi93>aq89S5rH!}0O4l$U!PNgdnm-+ zND5%m#B21j8GAKEuk~e*(LC}%BZ<7X<3UiHdz57Gp0NFMQd2w-R%iw>uLP^ZN2ntg znc4E1xAi8`LMQ`tsjSOY#A`~q;ySW`pTkqwQ0;%kTr2|`@<_cnio6E#hoy#P37;TN z8*c10Q;Rj6L7u01A@7JDYjZ9ge&x$@QYZY?6^f8}TB!^b7G!{ zAo*bnMPVzFi-Bz%1XI7}U11%7{uqcj-N6-r0=uZ%J5XoErcaT71r2=)qEA6c0qf_? zOII-v79)^1spt!BDdMx9hI6;gmTNa3T&CLd=v@%*{eq#XIqW`PT1jadiV9xA?!m!C+d-Geq6F-IcFqJeJE-+6#+7Qw-iSLO=-(lPd&p^^57SN7;0N{1l zk@Mtup|}(e5NSgTh)+=_yxaxDW0ISp*a(8{mJ9XyKw+?g+7yVyHt@0X^w`Qhk!*?6 zFylYUnMo~6DaGkvu$hAh@FW?)25oh4EyO9cl7mF=shvUz&eurNYgUTRpeJV=$BtTv z)&@ynWp5pdaIbIsZg4{MO~&SJGW4pGF&!14gM>+56B=)}OE7vc6)OI>@M&}bgz+V% zAVrK>Y8xP8?1~Fy=d=?g}C82c4+T(JH;72Ur*Rt$!pm*j(6{m)e);YoEd+Z|ItJA}*qw zTm|t|(Kx>W`cJFGtr0%}Ol>_29sGJ)Zk=mV-_g5&pXyWbXE6od*?k3kvdedHepK!x zy+ASxZwevR%HpG?cl}!ozPi?xP{jmYV(g=N_hvmkqlHK5PW4)6y5{kTky0Y;6nE4R zlgWO%4$8Zvi05WP1^eM7;qQP`@#6XlB+&>^R@P{bp|s( zzs{8LoHCux>GLBeI&oi(eC12Ddc^a|**V8GWxl$~R-M~%+Rq^lsPsUp!CYRUp30qD zJq&f>`)ZnPG?BEfKm;Og(!{+S{`E^5o7JaE)(52idj;XU?jD~gNC4xnwl3y{K38Mi z)@DkxJTnkpKKXhM#=AOwHsJ!BKN0-RCmtuTXjURwHZ4%1x<-zwQt_U-E^C$W&J03j zsbYuVW_|2|1=Ms`<)VKzEUe@>BK`MAmR*?&0d)s&-cKmRB(A+7&n0T=oM#;HXse9K zpl&z7<4De-;^Bqaisv}|@pAHmkx1Lrk>E%l2*t&Rm7}#7M^&D*cikRYMcDnasS!woA-{f6LLszgS@+h{n5JQlTro1DfoT6pZ6kvywS`@A7k8O95ChviNjv ze^{Lnf6s8o$Q@&5z1)j5POxZmPpPq2E1)776gb{*Raq%*U)_gOm5AU{`Rd!ynWN388PKd;o-#+))FQRK0` z+u9~^{vpl)z(XY_S$r=}V)r9NSxe zw?JIiM@Il7;ifbh66O6-!y&=5k_PnbAuBw{{9Rb$6r_J8C;_8Py@^3rmV?fVmTqNW z{SOoA6dNNa%0z z#goF}>DCMmVjkEt@m#>?5x0zwnmPV8`wk--{79LP{6c-W!UX(lTJPwH`+1>W$RMYG z-+6=V2sL5Hw;9+S^fzQSt-4B08u8(|pQHVOr~GY>p1}J#LWe%5_<^Y?g3x35@=$4 zu>RYHNe%=a2bEsK+b5j}r(u=^%D4)F{cX+d^u+BtU5Ul3SaEeF6{NwX~Gg@l--5^VpooCnpIm^w9gt~0YZ6Oucc zPo_>_1$#~#LI-ROFL^%6b~>(8o&WFGAj5RN#Cwe?E{ zd8HJw8z*umCwhGopzD`eg+p|2%YFGqVi(Lu3--ftib|8_+KqCna)!ZLBnwMlBMVYh zF#9A0PD#x5C|~mH{3wd&ooeDrQr6k`b$UqxuICwtcq>eU5-+BH}Rw8q%^ z$ALoZ(bh!=-p-3g$lKOq+0LE{oTiFtjX)J=erAWR=W}z@rc@i`+1QQz(Z9ja=@j`d zf@c8f7O+{DvD%e-vDEr-nPk6Hl^hSG>we_^$iS+(=pvO5(~vuWgl?*j3FvcrU}4_ni3_X{ zBBBWlYVA3aS5Q3jX8ubqPXyQ$gP`1R;Xo^9t5HJJC`1Mp-S;=QQZn^qzN2k;Y2uax zPoOErPl)5QUl)+P1En9;u)>+&#$+_%#;2rCnjk#+@zj**8Z-G<_p>j)u;# zW4XaYGFka8d@U(N6O#El8nK#{xj0fXNhq2)&Rc$H6Q=!6vOb(pM%WaLZ}}P?OiE+0 zaB|+(#McX{RpjP1Eg#DU8+Ey-Kh8DHwK?BN=a|rk6e%?HCh`83&Bll?b)wDkiO`Yr zgR{V8GSIKCR1mc?S>od4JIIQ0T|<2XLnA&LKG%M(Su{_IDyEK+6_=Pf3%*Iil|TRc zXWRb99X04B)e{;GO2qalXug?xsxs8YYxGOH57|Cx@@!8I7zItcCUE~Wqs=Gz zjRPt_xB}DvD^vH5QOP$5KF|875%^C9D}Gc~Y@%;KWKq{hTTQi1SdJ!POlm=Tzg13+ z8qOgN{J}FIP9dl^-l67y(R_o}+O;$ zaf&B&BjoD;VI|wp2d_JC?D0VrUiB!x&_ThzeA)69UGYI1bNmNMV(7$DBIyEX9$nb< zC>p}D&_Y-pj`<+&Xd+;GI0b&EJLqG@aw%o= z7MYKo+ey~y#o&`;W{}_H(mnobRd_x|d@3C>fFxdl{_tC(vfG9PM%8F;58*?xKuQ9r zhf$t9Xw3l6gm5cYcK!I?Q5upE&E!uthjwz_)hby$1~t<%$coHBwBw5WYp=zfX|y=MAy$kp;H54|GYo@Qn-z(pWf<3DOf zd80tS+{U~|haF9ma3Oy-XEao^@cWAc^U~8FR2>Zy_mpBp2$VN!idBHQ4;V%A%740I zh5-eWy1JO#F>j@`Ni^|9W0(W$3tQxoO{_)JKOzwt7rAqK`nM0I@5(V-2QaFhkb*+q z$jj?dV%wA~QELgn$nwGULk;n~p-r-{Jg=}%wb55HZp1YTG^sN(PYn?%ABTE^C#~gJ z4y$Of7ADeqtz$Km!yU0;n=2^84D|Y9_0-1ZJJ3&}Ecfnn;(do-;(O%XaTLkLmYqtX zqhhl%tV$l9hf9^mW>N1Il6;I6xW>8z9%Qo|$AxQ}O74)1p(s+QYz`i4s4S)@+wVjZ z@wjdhOz>L4`&!Hv0s{>U^!dBy+XMr}mROmXM#2wQJC_M`2*1r$C=%MqY7YsG1^xsM zwZ3rMB~(8O1@u)o+u78mpUP^YPxizl4s2H`wxnq|nR(op^L^UPLTBjLvA>*ik()w9 zQ5#3lCtHbnknsH1nOQi1|K?rw%_6Zz5|jVN9@hGdWmy&Uo@UaSR&_mcs)Lk%FDLET z56l{u@m#4yjnPH+vj4gz4l`{4W&}gl3Vdn?*cDL@AcXPm;rlU-!X?4%PEb`G}2jiMZ-26Qzv;WJ| z8S0}RW1{iir#7)f{~Nhf6u+a|3lbEjgiDP;hcx2fT-BZM6XwLy9TDblKJw$fe<@ZAviS&=*;A-FUknm$oi2duw@TFzQ4ih95o@Rd1IhYaAW2-4L51Y&{PcD`y16?h= zIh+SvZNV03_p|clh`d=W=7cupZ^WPDGxKt2lBHwH(RW%ml>)H{_a)&2ZBQ~H9Rqib zW-E{fB7r^%VHaN1nSdT1c-yu0ZlCqYG@#ABdAv@)K4=z!jb3A*gn{WPs%x>Y-7UkM zyYLWXWhVmPjx+c2lj{~zR`Y*-eg7w65zv!H0P-{2U%8-W(-*PLpNIEKA$|HXa%n~< z?Ye~x2HOznMlr70C$K#UYf0<}B=YS#Xx5|7oy1|?<3s5xy6fqh$xq_Vo;#xSpU zQAtnlgLhjTbbu<(UG~%(3D-IqV&rW&oTt@7yKiY%1w+Iw-XfovQl2hnnvcE;( zp;FI7y5uds??N(rqMIA##SJ~RjkPEk# z3Yx^HaNAygS@kNWr*aA~?7-0{iXRf#Q@(}z=$rGyD7bB3=+@dB1JV<7hXB3|5EsJb zi{5{KYO5fR5hf_T^wDMd2gB-LU`hn@PxcXg;=V#azkU+hwP+72AcJN>hpeW zDib5_n!0b)v`XhgSK_p|VI9{5Kls~(6T1t``D$%LA2zH})<(YgCgPEAK^iJLiaG+| zovNd?%BjlcjM&w^RU74`-pvlI^ea#BwOO?IUFxa47#aNlT`Mh>n3Vt4-AT?B6xY*t zL9css_93XLi=xJ~o=Fs%Tg5?G{jVb^k5QmUz`D=dkpQ zvJ+IOS74NQCUQN>`hZwdXg)$62buaOeVBBA#4Xf2wg z+4L9LIK)4jE492HwM;1C9o-wZOOrX>#p9Dsv<4M3=&8OTLii3id+$tv7(ga=Wi}+V zx4k}OK2b%*^$+2KHShVlmmG&#z}vCj2nIf+ACkhiy1}fQinA{`KcaI zbB)+BBlZjnLM8Y?J<}1P&B`sM!&Q%O|8}(sH^qn@q#JW>)2t-h$uTzbz9fcB9NNC6 zYOOs%WP1-^hHY)&N;8#O@Y$}r%Dzjd<2{`Yq9nP_(R4XE@G%j9-rQ|Qr>cPuWcxK# zWj51Vp=eOO8vTO{gBNkJpcF`z zXlk#VeD<9+8h+0(huX3+D%vTY`oK8eX}xcb9ozY_W5sWm(~r>`Nu_@Nw+@C`Y87R< zmxXd|4EYpr>55J?d?gXarc{=;vq1X~;@~*OS6#7TYpHr@=MAc#0yzfy#+&J``DB~7 z5ji1dHnQGxGU?*J@zT+7n)>>I5`zajn(b<9CQN2mSI*^o=b+S;#B~V?iW?-&LwwBT z9LuTI=O7g^j}+|%NdPnUKVpWCw<0n4XpG4q0K|Uy|Li(jW<1Y6?Is8Uw6&P{nmW?zX5MF zYz^c*D-fJ4p|YlbH^X zMMdDv5nNP@qyx$=x07l&YgL1IM`)ap<$3e>kXn(xZuXa@DwJ4Wgcxd_f0tN<>HTCM z?2zy3OlymVMz;0KqXpeA@oD0Oz6Z#fD{6IjU;&l}?l&J0QPTcY3PR)F-WEu-=%xwY zU#UJFYml6zmQlnrL@I!jSA}cmEQ7(^3)xfGZ3+AW8k*1VoTthi6E01*VZm!WyZGhA zO@muKB?X6Mehz@;-rxc9z0J)(%E9z|EH0$=bv@g{zo~UR-46Aq*eOdprPXob{OP6` z(h~C9!)v<57^kmLu18;9^Jyp7C|I>;*j(GSb^=cX21>az`BpYp0(=k$)eU8oMJoXO zhl1tRaQDCgl0GM?5YtUkAE2(jxAh23qo~mp7>czY5NwaQl|uoqJQ!PSO;=oWAE|dd zfpb>%AyUE*bCf?7{El2;qhebU2<}AVJ4QY%0hlObyt?&p-6n-yPvFOA2qHL;8gJrG zT~UrJOx=+KqaOi^FAY=!BkfyGR-`Vgo)&0PZt5?J)a(@0R}9eq~MmX^3Q`&8(EbyZ-$tOiAr zhxXlggTvXmS+04Ah=)xVnZT6Gdm@7cNU`GKi)Pu(Ta0RQ^yrMf;2Kla_;9b|WhZ=c zg<}Qe)w8|QjHT@rI*w-wH1(s!CERGbSHYhOKHK^Vbr?Zj1;Wh)4`BNIs^yVLp{@sbjeWq87=4p<3!y)06%V_ZP^$`URtfZJL- zXqZ5mgQffxZr_MW$5&|bHcr`t!&u|T33#{Y0{TCBb4~oHKJiM(dtn*}6NHR;Ee7L- zAj!aXRkY5$I+;^lGCF|DfWd<|Uy*XE&4ShpG9e;tWHsi;2>=E!vtgpVfM%vp_H}I2 z8o!#>m7&f^cA8N6KB8SWf1~}olci+6)@8Qo?}#W}Upgyg^$@dxs+kZo zu&5nUi%M=Fz#7JT7g1f;Db{~2nrcv!kKB$xA?@ZYZ^fyETw0zbD?=5a(ckWecws@! zU|6I;!g{{?kLB^k9=4$s0RC2({P1>}nORAsOsRdf0s4xt$%zpG9w{la{C0cH=k{i{ zH5}mb1s-JSCtOvW~Rau!4S#?gHe4cU-4=If3MR4E4?~z=mBAvo-J{X2d<=8TH92bik zyp4YNC1)*OB;~hZsKNeR<-^;54wH5>EEO(CMT;`4plHd|^r0e%Mw~xaK;UCMB$yQ+ zZXpR=(S+4?Z~gPg#KNI+V;NTjiv?2yp@*@fNNY2ObCl`M;+IjfhT*+lGbaOmQ~URe z0+R$Y*K&6Yw2Wn z%w6!Wrf1tjZW3*B0+0QyHjxQ%LHBRCAAkgJD89#Mo@*2Tt5Ib7zen-^3*-F1sOUlt zwswNHZd(5#p0jYU&@!^IurlGZaWK*{vNN$T=+KEd*gD(&2MVh6-*}-)bjl9;HjZ}s z4#qZy?*BC-|Kq}cWKfxZoKXG$*IWMMjQ)p3%EbC#T~IW3v~_kcH2x2IRMghyKkRW) zdVD7K|GbwXohZY<=6~c+|C;|(iuCU>@(#9!O2$rFbn?QYbjrqVPXEOh71H^CAk6=V zKKp-vdeYnlyB6w|vm`6L)d#s=7>~o_ zLT~`v*oGiVyP&$bvn+-=#FLz4M1N3RjToP6NTw^&_{>nuC&`KSSGF*TKt^N!TqeC( za@Y^LooXR^rS8et`J^1CpZ*!rdKyc0=_c5we=c5gYf;ezU2@fP_3m<8>OO_4qMrk> zm*0Pkcw5f6b7M+lH6<1NYkz(%*(96JZ~Pb+V#lcoWk1f#66;|=Jae~2T`URrWXkaQ z6gaCjd^@B5T&4ViA|vV&g_=X{wpPnY^p6y#${GXWrCqC;)K72NPP9cII*NSe5Crlr z!98}vSUT4TmA;XIu;OD&6BnjO2OtQZIHu!{s=>(Ia}eL8&wRfU#kc3ZyJ1R zDUF)`gp}7!1GI@Q;>*0PAy2ffQM6_}cL8;+ny0)vJu<%O!6yyf?5o&yhVp9VWHh*< zKX{Hdzv_fuV*GmDy=Ll=Wjtxq+f+XfY1!egY;CkdE;H>sA~L$eT>7Why3_2GS7G@Q z8w*7K4#)VbL9=Q_Gxze82B4VqpFfCiz-f)I0$?oR^VHkq3~Fjk-FfbQ^{m}nz(CD-0H3;mZAH93dJhT^2oC5Pqx_2Evz&4+JL{>0Yh%sBuKaRF9GzkZ?DI1&1e$HDW$qbR_W|!FL`JMJ zBhV)nB|Y20dt>9iXg4mE0JieYHM}|E%b|64ia-xT!3@C*7+9DYIQ|c? z!Yuzy{0^j*^{;?u@SdDO09IssVF=hH_ZsAoL^;%|?~k+V zA#v;CVdO+rZ87@(rdJ1YnHgYN8IpHdIf%Me)(dLtPa%FwE;6ezwixMG2JZV z!UPwHmG#R(6}hqN<=ct|Kh&fJ9({1A<)!*<{GitTJfkR{;ZV@`>>EIQw4HTq3972; zsCzm7=ElT5mlfv0G476aDFCU_cDp1X6%?R)INpmzXvy*^G~kT)ybju3-IP*x2%i2B zW#R+yzo;9Lp($dNDxW8-GAC<+&nMI(7!@s$#(Szw%D?d90a^Wfl**_j_b_=hRVD$IDB9Sn-T1>^+BdJ zMo1xh88=FdfHb8WFh9Ha@zBkDj^H8V=3aO_p~eDCsE+;1;)r-5n=Ja&Z_BueYoUK zIenm%a`mYgiSbQpT7r?aa&Auyuu~&HA`;(MZAB1WusW6ZgYVY!B{w*|ANYgS871p( ze1dZDdXW4qN&}gF`TluO7hHh>P68(TRYER<0qD z9X(uhkGy~>&ygSxa%!Ac9WD92_<6Yzqu>}O?(ofsMCTE}u;8TLV?qbx5V>*bZxt(U z9v7gM8AHz>E-qxOJ7UQ|zEi?2A_YqWH|S}LSAD8!gVTRcL0zt}Hr_H`umh$cW;nkx=E39%dg-d%U7{K@HLmcREnPRih=?C==B?b;iLUenJvxfqyN*GQL2isdr& z1PX4hE^a)gv?&z$ZyyMAbg~5dT3R9ke-~I>dkavh;iQZQkCFT`KReFN<7LSfW6SRh zMcVXu-ZoSaQpu#W#Irj|)~qxJ#gXpa1GS%_o!;QfUzIMG!Bb#wI}nX7$EVJ5f6Ws4 zI|5z`rq(Guybj}EeeFb93}gBl3>p_=lZ&Zcy_nI{ts8x%sSTC}UKugs3&tCe>NYBJ zh^C5aVtuCuCN6Kt*Cc*XboRSMl}NSv>@e{KOh^n!i$R8?C`#eIFv9T6;i>S{?+Zj< zrq6aEq_YXp5++Qh6BOIH+}D}Kl!cPB@T`7qBIb5W(d)gZ-L6A9VO^}RdYwwbBi=D# z07@otOs&qrU%;~dFbJkIST1R&c@gHo+*j*U{Sza!T7lCts^)YL?~CJ+Jd7efSlOv` zXrJg&hwKOafc4J4H`5w?%BF4(!Kp*fkR1@XEK&nnjAoTWw|(aXOuOf^`V`ptM&%IYK)lh^dcRRGX$Su|i!A$KE~TZ!<1jeFY~7;+2v%Ggji zI4T#9a||CFW(2|bWRkELenxuUDL6B=9#(S-T;^^k3aw%c8{uAfOg~||5r<5`T4v+nHCpVa_PxNDotfKZVO@6nNbB5!14YIX zTs3YIbCeA&C-qWry*AZ0BZ|)$uqr3HskudH^&o%bCIwIvA4h6xK!)Rz?x7y z@gF?uIaa(h%PSe8F>TxX-?8d$iAgC^rE2ysY|<<5_m1J82at;Vs<;#8bWFtL(gsR( z5@OXXl1nc4g{Ah=JLkBd;_MwGgx<~)Z5 zLScrSTB28*enef?7imgI8uE@!9INrlO~FGU9S3y@rolLRfEbqCj)@F*eLNMpuM*Ph z7Q(hF(S-^Z{AYrC91Cx^w`aFSF?4m5%WG>V3Le2g;AE3s*L3%e-X7TPVAE|C+Jgt-nT|N zxkj55ewVqjp6+mI8>a=Z3cuugJY!>W1zoKydw(jIX|I5AHm2iqv1X zTV@360DXXqu@4IG`+syZF~0z-ic4#=4D+Y~=?B}@mX)4xaNKETF7c?$H=EVFT zQNFHJ&G5v5PIG+XM=4%V_JnVX3Gr~5wik!d`p{qF#rkXr!E5jMuANnNKzj-=Y&L3?hj)lzs$`a5v&q%)aSRb#C2pc3}e5L$x!zjv$a3a=~zH-5J$@`vpO@|>| z*wM~N1W+LK$n(KzdIe;<=rnBy$Y>iUkY}2h7;_cP^g43}bQotf6HkD?M#C1sSGh=S{dQ$sXXrFkW$Q8h0Pf zS>Cicibxh}N9#DJ?Et>}he<#^`dp<*U$466ZY<|Mm|ehrhq8%>;ObOV=37SEkqC-0 zwlXI$kF(pWq{grAk{zGi`A{qwxu+XU&P`1ziTd(z*qHqGk?5!qCKr< zoCLY7p%pD~oucPhGlPH_Z`19sth;o%l&>F=sxE zUr|AAysV*q6;kf@{?j!A2t|d;z17f`7}3>J|E~sBemMiba+-}S5j}V~@dux$$eoWo zuxBxr#${cAHukZY4-WtSJyNxCl|8~e>na3kwa?Qa3Qv8%^Y8L11$F=>rMQLD9`r|r zR_z1z9h0{i$r$X#z35mo#2+BtZTmik0xKOiG!W@dKEM^V$M>6ug%ShnG?JXK)7l2* zEkJp#IJ{ciT;)!k5Gs#>geHVROPZ-9{z$uJES6X!|S{E|#5UeTCQ{W4!TidER z;d>KYwTRMU%Aj}%y0Y0md>$mi3X1wqoD4qG%xjh0*16yRgfe$1`D{UmrsXCX{Jg6| z-MRNj!XHDQlnz6F$ybdC85c9cm1{|QVdY|sNy3B=b{ezR62=q+J zL|x${?p9Qmzc*j!R%3-Uhnc(ujA|sWS<*O}PP+T(V?vB#xiF$}^hZED8Wvva3q_x! z>fjY`+B+US+E?MI&Z}`SiO@cYWti6WG(wi9aF=?hOb658lb2xyYbwr~pckSX-(+2;4|E) zR)RrL#}$Ricc)g07js%%5N)c4x}G-v_&0rbNLa5aZ69@dYpbxlIo0guBq20-8o^75 zjSEeKJ@5XZMrsiYao}^YoKmKLDu(WwD9OG#LV5V*i2@-jeJwH{0yA^n#Xg2b7{WiP zb?5$ea|X1u-rEj1fg!^KW-As46OSu0{y;@N|2fQnBh9K?%sNzoHb4PRj{}L&O^D{6mzo%6Win&tSGPcy5HM1QRKL zFfPp}gcJ|VK~J&~@(2-@$=L|%BV!wWSVT@w4W3{-<8#91NiBA$d7C&I( zw%kh{=+-bA+_QebZ*fdf=TPKf=i}$d{Ji_hZIYCN3+1VP{jr?k?e`+fLM zPAjS&IrHIzDtce{z6a_NM3$Q~;J*O8GW=P&?6aMQVfz1~5X}0JBIZ^*`_WRa{m{n>Q1o6#C#z*@f6w{wfjpmB9IV2XUPz9#gyB~km z4T&;YCU6PTq%59X|NLVDQV^_((1yjf3ZYx?eSO}Z7VYB-ldjvL>`c`q&neq%O6}oT zD`x>;2dCodkj_USmPM+&-ymbb!XhcV;1wNulUV)SvEa{h-OMn+ZJnxb&4ixLrk+aGdX8R0VB_w1|2^Dv7u0XW6x6r~_R`k6T#P4r1Me7B~#Y(Ld z{mmdS3&f1^wc5p#FSjtTO1`ihmlTbE=E zI`r>;-bCa)^VNHPcfTTc_ee(sN}woOr9v`_ZMupZke(WDx=33=`%ZzzBz&as?p?A0 zNC)O*b^dglMksQ86Pa?fI0z4-)hj zT+nDZ#n!xb44i-Gm7Ce?S!MuOYs302=ULXO0U>6kc%edw!2sH00qx8(PzgNUI;xZ- z8BQmqNlU=-0&uG#{{YEH96>Oi+?RmsYJ^W&x^PW)`YzPtHrW^5jwS`4!~%7lzPI%# zeTj^QQXy<>pV{$6+^+DC)KvVF05#2>62v$+BS-kDtrFBQJbj zn6?cOaZj9_w|{mRuP(4IY?40#si@87(9QFH>;it?b+Fq_@Xwq#hW6!#I@T?G861vh z>}SV!$Ofk48Z?VN*3H=8`K?#q1qda?(lkXi=T2e4>QzYnI;4BMU31|--q9AH>V~Ls z#2?2z9L8OtO&|s#iaeLgBK?qzpJ7{%yA?W4P^h#%ZGFh?fVK3a<#~JOi=ieIoHmL} zWz$s(6mN8#|?pb_Hb)OSDau715{;`bF1?n zBNgh?9H;#@z``f*Pj{}D;zG38kCuVLw&p=94@-SL`Cd!DVH=klOtgo&lsCI zL}U`xm}|KPJhk27*>%3Le=Z`M*U_6exZVfT*j-`LrC%Bbq;$OIe!smDREP{hsoTIoPxn==(No>e zJ^vtffshJcB*~y$)ng5Qw*lCtFS5!*x64$>r^LHQGDx@aXWO^uNoiAw}`MCAhDk2}R{L6HeJg{7kTkiIj z_*+}d-12}>fO zk&6))2QvoYm&<|x^0r;1>eX|&myG6@-Lw09VmDoii=tqR^ZnJar^1V$uN z+`lu{A$f^Y#q36580x5>J_LJo3HssXobO(Qn>HC*9$ta|o|r^>Q~{pp4IHrwjcFMOke%ru4nP zq57!){pgn&W^3DyC!*{9yJ5dMh@+6d`Up`74kI@am<t*_AQmA$CN4cN-*1>hG zo;s9GDd~f8Zi9*YVXL?VP3lKg?=67?V4`3!73oIFU3tu4c$<<-cGmxXBlp%poXxOw zHBM7_`CDg)JC%OB>xa3Xz|P7&E;X^8q1_BCm6iP92d80E4t1oo7$(@is>~Jj{rK&8 z3jOuorDP52$i&1tWN3>(GA}I-vq#mD%ZQ?sIDJ~AZLi_Ub zbB`ypKvCiX2;{wi3C8?_W!9qX=r`x1MX-XjF(WR1hY%p8yiGclkSKD5U(#37p?S=iVVjHW zfl=uI{^9A%&Wn$x8)zqG<43IxcbpvutiDxYA~dfoRXrq2Zj*9f-s!eoyID|Kf`REMHN$xw5#-2@z@ zurp`LgoAHCKBWiG50tPmstxPlNye}#IMw6Hk_~4 z${z^QoiDo7C3=kW49Ym8Z-(^T4o2*ujuG$tmyVunTPe>^FV=rO!xr&mt#ushHJFwl zmW0}N;UZH8VC!f<)^Q4>20&R{c#f5TV`#fAIv2H%AzY2vXsRoU21LG)4bHorMRnX} z-CLtJ93gIo6biQ!C8lG!N#tbv0G=&7{_;hyS(NGKOiFs%*~m!A@Jd5&7spo z8s{xOkP-T(Q1U2fLDF%HT18#fsA2kV!PqV(A3l{`X6>I2>L0xs!V6_Z4v=_*tH0=K z|Mr`#gq}AkzKq!dVxrFoR=)Z;x6#Z3tWZwHvLju576UQr{xB}+d2OWr{se1?dE|qL zPv&DN(Hen}19zf)_z=-z(b`8!6*F8y^1B()4F+XmcUl>!}W#8ET-av$lOQhsF5Zz}3LbXrB1uoWJE!K^)is`$^_E zyLJ&IT^OZg7U|+;&Zpnk$DM@ykuq_MmE13RnF=zKWY&VLOdOXi6{5d*atZ9{0VsOpq|dgLzQd9F|ynI=O$ALsxlB&7$Z zUi8El`*Erm*@+qw_UM@-@!m2;4?f!$CGWc?;SRhYd$qQ20`|0c_J8HlJ(MQ?`DC_3 zFON3MlJ|1<%_o0xJ?Sn!TUi7JoL$(Kxk|M5`g435>6Fu29?r2@u_d!i)i%Lk?Uoa; zc^R+a;Z2EoqPtsc#2`vgtld~?BuJZ*~>y;ce`DYEcC20kPBjlz=U35jFZ;rX7i3C=rX@plfsKyp` zH>XX9q|E&Fb)-tFiuJxW^~`ots2j1IN1nAkp84K<${eWM!6=8ZOk`{5!hTYz(Lj@( zU6(+5|E8>Jd$qEU0mZB?e=B7KC@G0|XdqCWWoC!9)lnc#@C>#&*nroo`~|!Ro!tLR z3VVyw_8U@wkFgPm1JV``ba#LBbA_Rn4-V>^kRS|i&!L{s*JrvV02z|!x7>jNODD7Z z=rnxDcTdC%{R_b3ePw7lyh8l9xc!w3)H1d~rV!DB*n%H<(5?#z*0+OroV*h32TpJh zo#-KFU^IN-GRODM6qFn-f~q|kUp2O9{J;g=j{h>za;Xf)Z$7&a3-b)_pfNj#Sk5MG%;ns5elfI`y^@?Hiqkm5LDe?&&tGStQY=I`{m zI4voP;G9aM`po-XlRr9AiAsxoO+R|O_KkbK4W>MlAMvy`J2aSK{O%i}I&Qba=KYzEJ}rLP@a+6)JEtc8sFi|hf?a6QPMxV!7?o|cUX z16lhn<+cRx^0?NYz-?L^8X26PSg$mJsDaD1AU$~;dHZKgzX1{$Y+K3^J&k3xJ z!GPf+f!|sFPzL^94x`{sjR6p4bsUP~`2$=F(WAc6nl^7L67$Z~8JZO2)k!8VjdRBW z;Q;i0D1Ez#QJ6GLH9PN>dUVsnRGk&D#r`cDLxpg~w6B&TFPH(Dsd8Mc+S_5kFd=Lp zkFQH9fefL=dRjA-(!rfQjtV|dBS&I&AEzc2-W~aiwtHEKkF;&*M z2^7!i``|?bP+sTSLR;1@Sm}kdll0M;Z~mgJ=lMe0oaY-=DwBE1>q^RPRvk1r9$rT3 zSWl6eRzB#k9gr_U)^r5UDpuE0*k*4Tr8wNl{xCH#>?BFWvT|Eq+w>ASyF-RRO2Qta zR;ad2ZCTnMEl6L3)M!IBJm4XDY8SWyHkxAcF<@oyZ~AdY@qd14{gLaO63C#fI*Qaf=U>!^Ls))}SBRPk|){>eUU0vrmko5Ol7=#&s z3XxP}*D#GBrdXCFv7-s=lj386vB$m`3qJj=X=JQ(6uP*%jiPJ`(`8VYIx) z=vNuA0#vaeSP6l$i39Fn4R>sak_KiEja-sVDR%S`b>7`dj>_KYz02Am5n@nueK06e=D2KzW&Fp5 z<3oTF2B{A?rNikca_vH+3Gmagj7ho#)$0mvn)em?%(nU|!Zh8*%Z+}19_UuZnAL2A z+f*-pPidDNSOqh&J-uVQu$pp%f)}#vjV#Ey&~Z^<1X8PY%K$Yu%|thm&{^wEgU}-i z4>x9AoUK(j5yJFCsl(0OcV*B0wfX?L)m?t^Fy-2NhfTs7z^Ll543$6?-!O!rt)Y(i zM+@h*&18{%hTSA;$%8ij0{czm0TZ0YPG_ zMOk@`XEf3t)b{lJYAGbzM7xvFj_-k&_%;gzmn^Z+i19G#DYX@l^|q=NNpz%KZ(o7Q z!TXvk;EVF;efe^*fjx^VXav}gK?*n+(9D|tjK3#hHnof`!Btf&p&tTb*oJpNR@iL< zBlI)RAz;%VTp8e7DyR6n$AYUpCld6U7M8W-HGhW^AOkm)^9*X9it;9RH9C}mLgyQN zki7{q2i2TtWbk9%wU%O{g|dc^Wa{oT3VtM`dL)3;aFv*jVvRqHU?Ck{5B&xIN z3Qx!xRpY6+1o7T~OHAr*Y^S0vP8W#O;{svkVJ8tlA_Jugl`@G)f=s@E&~!c)FM=_u z2B2rL1^9F_PXc-B`*nuTDH<#DhVYZ>&T)FQr^Z2yw-0vwNt}J0 z*8d#MH&*d>?T`;i;tzpWvjTG*y6+jb2$TQIUr597#yU=VU=4!J;0QJUEAhcE;E80K ztb`Dl4%DW;nxrS(&iYYLh$M&5s`&OWaQR=(I8>! z>F`HX>HaL#m%*%R@k4>O@F!vz9<(NHlwssw+}pFF?t_n+B zI>T_IL8CZMZ1yeKoI@t%NvT?Q_4jsX4o*Ox=r^FXgu{Y+vU|;O_a-GrmVfj52^V3 zTn!;?8U1(j;QFNh%*|5+5qpxOI21sn^H#9dI-sFPyWNutT?AOo7ycCIOC0`v>-m6e zfyPOz)h*&OYNv_oKXVS|Ma@Ki!49xS@-MJ(@!akwRB=#5pkg6p2==$MWx7lmp?S|gN_hK+ zYD=kwm;-TUN)%!dO$SaCqqj8bk+8{68F$tQ$1&e|bdZ2vTlt2*`Y{n-@ic{V{3t zN0Mia2)-N@99GkCw**Fivptp(p2`5!1tagF7_T1XUo}{^W>~ju1T{%NOr}2f3|>1) z(iMrJ#On-D7Ez#g;G;)5Z`H>q{q-lICP*7mw%0$$za>*(sTkRNj61cKLeENBu{yI4 zxVD8Hoi|qN8rF!f$QAnYS-?=gRM82o4DVLnH_=^wy941n>i02jUk&hLP2g2@Hh;(e zAT*nmjV<674E-eQFLX}@z>QWXMdeQ$sS^IEe3nKyt>t?}=6&tpR;oLAY*nj}tZtD2 z`QY*cCfMZ3$naU_S)9cV0L+RbO8vdy5GPx1K>0 z`xx+EzXK38O#poI;)4XZQ`x4$NNy#o^AvFDMFpwBiF&7@V@fAqy*&py@>H*dyqhRS zZtR-T>}5-+T-?MJ=*ykx19ResIxJID_CsE(7>De{li~1OHr6cJzSH`RUX3JQI z`*i_t6E=87Mg`d%xYbc(P$;;oaOJ9yg`s9<45=`?J_l{?cIwa4r0j~8@mMSZW9*ho zJYKW5`#Sm5@~AgDS_ujs-eRdpGoCHHWsQAksy}YFunE=AyQ0dtp-%k`HZ+BdCYonW z4$;v#9osye@Sk!>`<`5KxzD>dvb?uNpp%S8*s7zZ|`$g7=<$C*SoMdr#4 zOK=KjtN}VPLsa_HvNeSSAY%6)zaT4PQxIi&Fb#Cff<9&@1Im@|tja@AuCHOgraw-( zG=(37J(E1tdKP7uQUX9^Z*p7|Ad-B?LkD>>v$d0> zbPF3`<+n|;g*>TD7NCuP`O&pFT=(9<>5h#JTno1dtL}13&9Kz!lga&I+a2wb>2K|g zpMC8{I6NG|Nnnfo1s&}%-3me3-Ohpk zIt{j?2qzB=JHHmmQW);j)X-1xUplbwYY7yBKsXKfY!w7r@-NQWo z0%^XikwVgkmc|siZI8+d)0PZBb|yHrj22CeZbTgXXgN-OukPST-uPA?`7go_WIk<5 zC+C?br0|`aV5d3DB_hM8tr^|Tes+N74sC0}WyEoHhZ5hn6&MNEiGGl1yU-z{nqwFL zgLt_jx*Zot{*9XK4lo_?<|U!LSACYBCY)i%#JfK(r+*v_9 zf<@P%PC8-0(`!4i&DVvwa@RGS(mBO|n~>aWNmE3hEd_u^UGkT1O}Vv&+6L1m^~wh3 zbjsl8Bx&O=B)@{J!v#vau zMhaukstq(SNPP9vMt|!8^1^aR?VPpfd!ExY2BEjSr>58~9-B<)9rm!MWn+?Cw)%vN z{~P5%L*#O4DDXl!JH147hTHtEAS=lYA)p|L!uBVj*bSjrvN5RjluS(=~5mwuN|?IR2}qa5#$X34GDU zIq9XgxL#YxVknQ?@KS(kg3oSh&RubZdPVG!9PBz3?&t6G%-~b3gJ%U+s-mK%4(4}f z;}he1p|vq(AFP$U-%{NljK30|)7AVPv#v%HVR7*vkQ_viV?yB!y;Vhjbr z0a&|Gk3Dimux*3_yDUrXwaTLb2tH}PQepdAMaUC9>oNY9P$YqnN{U`|(S0BZCmc>X z7S?}bLpG;RUt=IveVmn0G=&-5Dyii5o*816B&<|b%ju9A^(1Oy8t`ogwmN@k{{Aa* zMdA>XP3`W<=LG9@anBX8WKE0F5PO_@S$ro>dU%oY59kly-gw(n{s&`!4AaE+;%M#TUikH%dk9-)sOcy1yXMlo)?Via>Hf8f;OWxJhW1`S z(l3Q2ZidMIwzVY6A|6H(FXf9YrubE$N;#J@52XUxC8|ujIQd5IMVJ>SY)x`?9$$Cfiy10K3s8HMpIKi<Jf{`v#FX&iWxx(_3*0z+Y2k*#k)({ zEomi_DvjHz#pKvtQZS#`O}l00@^%GuBqisVi@Fy$u+>8M4KEzfKwA@V`k`!ToNS_z zn6uiWDsE-G_hnvL?B9QkUN^+UGJc=2yc4rmrnArnkHeQY#b8?}1x52nU?l>gB2t)! zeGY~DYIlermnuyAhoDDDoXTfkej-?VQm&N;WXF4nq(8W}{)8a1ZR=ldam{AS=R+*U zuQ>zj6xyfPh?N((^ZwEOD8DqY*?GzqUH5cySGmaxotCM2B1v09v-JNg06`8Dv5mJ# zDH+9UlZ0H&SmOz%4)Bi(zqvp~%Ueo7O!nQ47*2m|thIt<5GLk$P?L58mMVN#0=N_B z<|CxgITko25W^541>7|`lzu!Bc(?~PAsCNxTl1$10kb)v1bJ;FmqY1YLdV~yXGCgk zC~O@9C7U-`tBI%!Wn_XImPR)n6yz)PqgUba6qDqOQi1;M}7%+c&mX z>=oO#?c|MZ+qP|E#kOrHE4FPrE4gd;>3h3x)vmLoeMjqLyU+kc5)U}F2v8{MCvwn5XMX^1I` zAGy^tcLz5w6v9*m;sG>71@2|H0o0uqCQ#YoY5uQ8FzVE5a=F)u{X#+|qzpzJNg9k} z_*hQXfYHe;7x5A7t0*cbqx#MIl&?E%Jj{n)%fhi@-qD_R;iH4 zBic5gP1PhZcwABH;Bka;8W+SmG1l@=Q@+?`MTOSGzf??w6Y|$l$l2BYHQU(ELP9=D zydCIeM|u4@y^fb(H4cvo8h+U7reS!e;|*C91W9eH2!dRd9ID*(e5u$fp-*awAeF=@ zkVYrmsyZy|7R}rIv(MbQB*30)udA8T$|USVXE%YQ{*!62bxs)uYBj9>4N8~rSCexC zm{i(p!ue}-?7P6dAtXNv+=O>HWexqqvw{>AGnd{d4}i{pf0M0?y0K&I%JMn16Q_^Ad9IQZw5EQ$S?72Z>n#h6N2zGz{tLw!kizXz3 zqk~wj$1$s>dp)l(VVAq1O!v9(_AK-lxs5aE-R~4n&4NVa zPM1dKy^|*!I~)ag7~}koF@3k0SHq z!B6_LWInZLxMuMPu*HUdeivr!XFQf~CGOxWWpR65e77<;9O$sr7yICPQ+a=7op=g` zanY&%p;zDGf5v*28WCogt!8m(s_|mlx+XF;djBJ?ZTz&!9;14Vng41VNc3HUEj9bEBy|?q3*yPO1=z3({eCgB1J# z{@`x;G<>YxP2G(Q8xjz;V_bOE`*8)|6(qA^E{M9HPF5?bzRW6?&hojz>y=(E`*;%Z zslPF|N_=C{9z976uRc$q(y)Dm!1*}Q!9M55*m$lTMOWkr8G-LZq>$4+s*LW~ql-qC zzRiu@=r7zgO{Wyh(#C9%UtT z;;Jznq2YzC5i?+v>U+Ys^6;CryokmRM5qv{eK`p-fS;TERYxc8VUxVN6H{ zI;*5pcWSDmwAah}u)hp)(}*5XOo35E{*tEqAYtc3W_^f1A~^z+r4$@v?St2e`@No2 zU~#bM_i?C{Z@Pb7I`;w?OxsBe#Ug`*w!ZAjqguu^ROd&Mr`FN(5##amE_A4#>Rwq0 zbM(0QT>=ShGWce$LFxz99AcknjrrjiSs2T%cZX%6sxOh{$4=^2x0;))uOQ`|!WFc_ zFxoyZ%Q0TR$?H{xH1sN_?BQhi2NowV$Pm3Sj9OWDC(mscp?>n@9m6V^V=%l0s3nHX zeYBiq zJ7poVfW|)6O`I$1Y;g`I|3M+Ci`9omO2j6GgxN3#fVOOfsYIZ6$upUO)1I`TP7au{ zl;R0s&dv~sc7C7e{2B5%*o#$&*lEQb{(BQgPk1qF9Xd(xB`32^! zOt~Aa+A3lh%fqt7SV_V~FSqM8gq_0(s3RMgYXOZ5JU6>SjozuresQp-BVG}^W+>04 zwvlwB-Jg7=`j7+lW|Le<#+A(y7~+D1ic(|wEV#;{fv;LdU}~^cV0kR8za*}gSpVwA zo-lmRTR!ofg}>Hy(rH*ykIpGJGAcZ zyKB~bs|TH)7U#TRwJKLQ%(N1v9GoRT|wCZ%nb( zjv%D_Tr#*?{mEJM5pPRW@&%ctR5bTfT+$0r!#I4&jx)~vtr?TR{05=$#SC(Ch|8G@ zvIdHRzGY;dDH0;pcwwgTMYCN1+ZY&Jf} z@=ZGdqaCWp-b;I_%5EQ|l-?F8qi1e4r;Ibe3BVACthj+=oo08Y5`u!Fjz*Y|e0Rwj z%@>!g;$U9E@i`bgu0Mmrym9hwfzC^wF(TiHhKge&l9x+EU)xmPdPvH2I78^(fEqpy zdXiz+d$!OplJGFLzk(@q&>$2TPdxbES{X5mF^T|@-cMRh28?bcMXtcZR~m zLf$)(%6^862@ekAY>?qlB`e(zj#HZSx9v}39~(#MDCYEdk|=GH`HIeKTc>?*agV(8 z2`||*LpJGB=TIN~eZKu<*&p3nn19R9LJ6F){0+tEjjxA~b+ZKbGu^IDfv2@4wXM;x z>WP(EGniIPh~IFj$A-o)u^7x(SFMR^%!O#>F<%uGfB({+SsBL5gtQ#cRrwy(LszB6kdA6RC6Si-G6Ixw`N%lm97 z*|=AJsJe&pg*@>HxcFFbWbL=ti#dVC5qdi3B<^igeE_ZwU1R4Kivf?8kTqANd!WNc zQMb=_Uo6<4NA4y0!5=CDOY^|9Wj5S#k7zqW(Bj`fIggPEHQ|VCc)S;CO$#WuNs1<} zD0WP-LBNYw0|bMj8u@lgeuJKT-VDuwZh|AR_g#_!+`(2vG$<=yIjOD_N{J*mxAiRS z(y{I+n&&-&G%q@554AzcpkEG#9DF;s?!L49itHiWsSG)IMG6L~+br#yq=G^TRrT&!vip+OBCj^)VRWCh~ z_!Et{mQkMToKJ8{>d&{UvhiE!JcUI|he3&MJ6L||?hP1j6|KL$QfRlnTY^792lrKK zP@>LA8|%N^VX`w77w-9R08_LrCV8_!|$larmTE8!CM30mKi=n&H%seDJP#^3$&Cz#RB%UxJD>A)n zjw<8*y>sAd9e%pJj^yK6?hy4%jX&T(grwo_3sw%n!^dw+!6K(F*;%TQcX%$(&Gw6TzLp zj#l7f9t>3c!ll#vwZ;oC52Kj2d}f|p}EsG+2 z=rOytl2fqSTAv>G1>&2RJ2>ld!^BIIP}X5|94|NK-aWUh$at;Dg6E5^AVc4NpA;Qz(1;IqS-)REbHhmT4}zX5AWnU8Fxxq>&`r`z+rix^a%)qf+Z8 zBlb=i#Si#~MlG8_JEIhE_702syuo?=UWLv)5LqCFD8w6*RYtQlLUq!_zr!i$-#=E?)WZUq!FaX&^I3a^hLD$@omLByK`t+^X`Y}$o^V&|N`uJl(QVP1a82D* zyN&X=@L)~)6cdp-FWojOo#QF5N1F-z1z#zZ?7n!XSO_l-S$E~md%byZD8Uj|CPII67kFO`gd=&j+#WH~ zc4f0km;pm?Y41?R$tWTsv`Y_Ln(s%I(P9y#Px72kUb@{0vbtMl8+c}iQW3(O70Vwu zEHNj9Aa&GAsbv0Fy6=S-BQUAYWRmN=+3wyN;mM_Y-R-@;^~`j(-qP|NH9B!T3K<-TzZb@Bb6}|2uWOf4hx;t@-R6 z%>SYJfY;h+rCOg3PCZ6&l75<^0M6#?(!!1tuwV2i$+n4!MqC%}WPV?gLH>U?=At{$ z=voXSDrnl!wXmf84(3%nd1p`>aU*PPr`5nl#QFQJB7e&K!kXY+-HwR3txCJv9waOxk(+?T*E& z61X+8-b{^0?0Q5UI}a>e|G~tZB7wE7F#>8FrP#(&UI@vLN#jM<@rg&TSQ&o5u$Uj? za#Gx6wXY#X3<<|@+m6Bj6?l5tGBlE!s(5dUOoGMMjq^Khv$KN0q1GhLmx`W)yQTp3 z)_Um8BgC1AA$=HKe5Ac3?iXoRfX~P5A0@Alpy=ee6O~Kq?%yy&jMXyQAp<=`O$WkD z8VO&3DhbX<)hii> zt@o5(UI!3;TWr3P+Eyvbr|Us9(5UP>qSBZ$#_of;ZNGH4eN?RU@C{mkYh%Gdygv~idUB( z?=_whFA5n~_9Pt@R;hI4T^+&VGjl5#JzeZe*te})ecx?;rt0!wpFzq0d0R0vy zxW#10r?b8Yv?w(Av8DAPN<_(%q(7Zdm#>b`MHO2ab4#E6iWL&YbXTMScSYbI$Dc+o z<0hc^3bxl6x_U7-s%fUA<8=bF^USxMNE|KA;gmo`o|w8KZ>aA>*w{K#MyL?^j?UC~ z|H$?e{CH%g-1hI_^6WaTHN2NmkIj!}sENCix}kZLCC@JYS#E}Y`vwEEBy!b#u&kU< ztJue|w;eOn^FS|T7^DfFzy0USza@caQwN`2C4w4gwy~`CG&(PxrHdcGLH$N-9V~2n z9|ujnBFt*3cZ6XaoDdy(sTpJpf1+;IicW}1zFRl?kmU9nRv{if=66@^2le1E@3U^^ zgeut)9;C9-4qT*K6>+!lLChv2J{X$kY;6()t?hvLqMIQkS;1bf^PkypLjGOwvF&49 zMZ%6&ygz(|MX(gf`am4>i=oT};Bfd4<>1F((C&mzzwN~$thA=#ZbyESSS(y~o0-(r z_mYpftuv4eL)4aF>uk*SR?-7DzdR6J1?b=|?L9>&$(NTC3OZ6#9J5x;O&wn@1vf0u z@v1j*68Qsc)C2QThQ?h>>{Um@@?Dn31)m1?6bKP0YMn#>1geqCO)jQ&c z)PevFWqgzmp6$g=zj`?TDi(i8UD=Eho-tS#vkw6~Cm9CtCeoG}0ylx7&(_u)F~io61Am`s_2d zBt61P>#8RJu6Ly8NJA4(Sl77UKeHhJiHHB+=qmm@DE)s_f#IK7kpEr(@c$2W=lC~p z{J(^{|J%jiolI+f;B;TsIetf$r;Dg=eFU#pO-GyV{aj>)m)6+oEP%i)4D}zzHJNR5VUE z#d$9t&1V5K`*P7jZhT(#sneAA0pepuZej~{`n4~%|1L+E<~Kv4NiG$0Eq}s9td8`# zIEFI|@i)WQ&Zm}L0M#UMcwNbb1Ox|AT(8zUX}hc>nEAt7vSM6v-Za;~A(U}0%PV6L2 z{xfu`9cmdNX7j5Mj*nBEA)=mW)6kzecf2tP;2cir28WcxU{Gx*9&aY-%%@$N55Hg< zy~nx;gy@GYe_ftt9{{^Q0uBmTl-U42uvGyuwvhAXWpOO*&vX2NzsnSYPN+!0{jwF+ z5Bdl4K=+ItEwhDCQXfBardfd24P~dabVv?r_A>H`F~+wilM9un*%t~~tPrVFwr5`c zK+9m&W7d_Gz@2dl7dM?oAwb1@jlSvdjR%%lstGvd``_bce1BQ(R=*kVgHAIu^NGqR zXVfip%Gka19#EzzT&xvmxmJIp$yR}%YO5ekHBY#FaJ=C{Cg(}3*CVK!8Jb*z>PXt{ z)tzkGx0T9ifV%-Da8ZB&;uXfqDnT)9A{R^9#zUYV%kM5Lo&{$4Sp>7LaWUtsqlTVk z;M58GE*1#&4K;WC_^Q2tjz^qX73OQI8JMUdOgF7-?@T6jcbh~fbcz(XZ3icKzbriC z=SQ;4fExxD!<7$wO6fT?eQtxrUYCzu8-0#LPf@V8JdD@+%+lM=E=(X0j3_8di`FW_vXg&Ds_5@WF+dOWnI(W0Nqu$cwrSZ#F@#p?Bdi;7r7y;gR( z=BH)K5C_g84m<9@S2594>%(a#Qw>I{6t#^K+6b3Khv1Pi-U`A4GF5g?UPA%>R|? zoVO?9SNmr(XE+Go<-v*mrLVj8_gCDz?vhXg$f;@0c(KU(RqZ+W{q!uSXi&-k1$VeH zin>Pw_V-9?<+&|X-90Sv{wcywd~oyg_~&cV;lEL8!Tk-9HS)7fVOO@8{rOd~my#N& zEkplI&i+%T#(y<2;NN3lM1)mDOpNS|P3R>|EX>TE30XK;{@)lFRwh=?{{sD=mGwWp z>UZ$15`o=~vc9Un0ukJ%7~;o_w@SRr(dlJ9hH}eHkz+yf!^v<3EfqLDBXp&gwGwQkqI=;ewFLMm>qRU#%1|A4|i zNo`ap_kGvX$;-Iyh>#*VzeQyG?TAtQd-dv}E+ZLF&0HqsWl(4#orXp1_N7C<%qNDA z5)j*<4(>-A%iu+(UIN(8ex*Cr;_{7|-d+Ee@nuIB}z}oGiOo#u434 zomTs0Qd+RNIXdAtSXxe{=G{RE8OWNocNS^f4+{Diq121e*e)R%&AU66SYI`;o6dr3 zVT;nGRCsHMh1AQ8+jTiN%9YLB-fN&5*4qXN!B{DKJmnf_)WQK~2uefV};Nq}dMpx_K;?>wRo{$y6pkv|e`8`ad2o zB7fsx$w2i$5L3}*+{SeFd8P_NQuPF1!g5sqe2}p!{Hh(AJjcp5MKF*R;xQp*#$dXe zVz&BnXS=?Ninb@7W0p2$^)E%S8>40d%T0)IpeQ}&TvXp78ivSN7lHNEM-(LWus$p| ziqh$K0cdM`b7-3KOlW)9L|c2lr@&|Q7IEGuQ`5MyqN)&K_++oC@9!NLABNW1mSKYTX&q970p6SqR{eV)^SKLQeZky0RhS=_tfo+tWw#VW>Kwe5bacgTY)cnDw4 zTCIUTWd2z<5xW(Lv8cs6$KgZ$dVFlSPo?7j1%Gg=@e>r2AzO;p6=&($ajK#)R-jqa zqchn7b}_EY`}$Qw|2-$1wrVSE=ts)p!;HxE*cOAMoISaL=IO*4Fa``$tO}<`>*4P0 zSu8{B`MG{xQL;Xmv0;-&ISIrW~|dh^ETQwOEeimu|=wJG3QO(C(-j;OZpNL z>oIh_V9b4KRmx528TG?pw!_R8oA1{|XQ0q9P8T?Zn&Vb^bu19b9OZKHfb{zp%2o*Q zH5nCvhc*Aiq>{KJ5w+uz9>~iQId4d1AebA92u>aLVxqB-}~KsONA92 zW|JD3PJ-!5#+$&!fwAqrnNrETOMyR-cEJmwJ;-3}mxxS=Q+u!Mg zAQ54VKz;mUH?7dkRYL=6ICs+Ed{Z5~Q@m^;9n$h;Bqz}16+BN2%#$vLN*Hc0H0YQ9 z_f2piR>0yN9S^q20g4vWh0_LSAaI10&Bq=L@8$6f=@hYVNN%A_7!6wsFx;6JM6Kao zeX2OCXKw=TG~i><3nBJ8VC3%sgmO~@^hS3NPw9^SZbKsos*b#7J5YOUUG%WMGx9EI)AV~I z^lJ4zPZ|R0=*CKCkX?d){j6HPQQa>DO+gy>nY|?jBqRMKHS<{;GWgj&3!8$CW zya@1`d?)IzZ};p|%3MhTXKgmpsC!GIeLT$reqg;Q3|NAJFd;R^|!m$};{gp8771HO~Gw6J6FFfdy&SUGm*RnHNa9&uY-D zyW$A*jj!c;{1b!DGn8Si_hTC9uECn`+juj-9G3^*EH9n<3bW87?B)X6Iq|D?n~tBm zo{Zhv%2kVZN#qwVgV5-w`l4dSiU+7^fYM(EYf-gyG&M~rxDPB$$yp@7;#j@vJv*m` zg*eQYTcX!9DGADFX_DA%q(kOvNLJ6l-lKzaS-erTPU?VE&NZc#B^ zm?#jQwPtbf;^&TW%&_@etKP7 zCfD#~t1;C0sx3fvstiAG4~HPdq$aZ6z%WQqH0;T|zQD%8&H1-9^jtR4z5A zJnGrKYz3eA{04Z0p3gZd+V6AMwZ-3+jt0wYm-rW@DszLtQddB{8F?(Gq*F_kTh+kE z5vyq!1z={{5%gO1Za)hA)Vbzu5!33a!`+=0LaThkM@~axj9QhOO1lCfo^9v%yXVC(_CYBrcfGY^;_%%W zLkttJdv>rc(b5;EQ2f=oR~hsy*LDpgPupyk$*Zr}Xx#miV8K8jePc~it*Z%cdZ5|e zL2Z=Y)`)TehDAJ+mqikm2V{5o+u1qLD>NiFbNxqZrKU!=NAY9DF9}v3oRyOK(1GZ9 z5!I@K2?tZ&FGXM zdk(1U-l_$xWhMMmHu0P1TDHBVl%n6Ea8$*fsBgDG&&tF7Zg(ZNq6@}Fg&5u^+ zJj3<8lmc~=-AiL%y46K3+-(?;c^(N22v>R`7D9(E(GbV=39 z#}q!Y+l)q^0+yRzC5_!=4U8+Z4MvKHbe?ezS~+bK?L>|g@oV=ckxE1a4+_Wk&_Pj+ z&qgh3##Xnx(^e7DREQzw5*c19zG#$|E4Ch#Tai3c?;5u`+*jjt+qPcHy&$*HRz?Z; zP{6yb5QlA)Nf@M%Z<6l#6_eC_k2HYw=|$Qps+W5?uY!^84i=8uB>yXCI;E_6R+NB| z>EfWxGn_MO1R*x2vqq=Si)iS6X-l7EnxDqDsjF@(1Rm;1ecWWBCuJr@m2NPX>)HO9 z6Dv)Y4kCrth-C|tSuIRfyu|Y++#&yP0XZJD0psj_lnBkb{L(+OVu)`TPJZA5`ryh%NM+#46xkT~+HYN{QAcdDme&611{ zano@jvfHmfu!*PPJn%v-))S7D1Dv%FXVu;fu;R6AXP6xT2h-;i>?ihjyhUqlixr~Cb-7od5eV*aoNT4J zuxv?fb38wh&5c?k@`)qrUzporLQ(IyOtF#TDt{&K^=X$B9(E26EJQMd1O*m}FI8>y zbJKa4f}#Q3FAAPAAFj87MO=e5L?Jv1R||CGXDVa3;De5=AS4D0&3F9dj{L&9S2Uc~}tW42AqV)##a_QQGQYKH6=Sp+kGOL&Cu1O62PfB)#FOZEKH^3s$tfWm;n z;9_HLE-gzh+@fqbm{RH;S{8wb#Ty(u+SD63zQFLLMGA#5!^mRuA0u1_X4{;uCFm2K zEie^mUivI#gtn)BD@crCZkqzDlVa6h#Nk?wGj1|;v?#D5Pm8d109pd>R$ZsXSNaLK zFNYs4+OspiidSa|q-oM&jS(fb{^SRjX7!fxWO9PC;CYr}abV4hyk-qTt+E(N*FYyO zo+G?Z?vor!di|`KhY=9mQ2B&#Gby1IRv7ce5IpVObg(#113GQD%oUYz+7yTK;aU9s zDV31eaBh1tae`)=Fi-y{Y+}Fmcu7Z&w!{cS^@6xr7-5IE3INq_jk%lO1dWa3&*@`b zQsPIqYs)y0F&&(>s~&0PXgA+8%awDeEza%yc?PS|8iZkfpfS)trtj3 zZclFIH?(!8Q|TS8R*OqyBoIH{8wrmGyJcYSqC4X(Rp;=$ICCWobWC(OnBPnTyCu~b z6Ct{dfhtf)CiiVxFT!0N)|80pjK+Z<;N5bROjiIVMN}N?y zLxElOq_)3KjU8bKkx;NX$0WlcnqT5oOwEgVph{(hZ+FyP5J zra>=e|LE1P2wY+G1a=f{8fe5ti^G;jpE)Vg6&3lg3Uid#+P_xi_fG$nON*`3K;Vdw zIup^8T@BBF;14cRiP@HU6namgWuQSAo!*Ny#o9Nuz34tW(*WirQ_|nX_ARIh;;(Aa zPl9jBQ-P48_~$N&(uAx+O^h*jyyi>cu&w>82*nNly0@}p_TG93WQr|K>?aoK^+%A- zp{U_rjcW9x#^CpON~|rg9%(jAhq5B4f@074=|z&prgd zu}fHMl~OL8OcIuj?jPhvIqewOFhjVOFa>^5xYZgyZ3)$W#UdkU>neO9YN8nZ;JP)# z6(ZxYXVQ2MNpHybhVkOQf;BiL#r<8Nzbuag+#BTM?T0;tYXTGFQ4gn(&O2oCW$#%$&tjBPczlKre+RxDcgTS_l^E>3zpE2|1MoulrHaH za@}m>mH4|T$>(oEZFxyP6EXJQ%d9wIA+-*qdC0SAqc3I)Q^*4yH|lnZqJ=ohg>&N; zOD-OnjW`5gjnG2`)>BZBRmZ=0R?CTR%fKBjyqC!ZxT)X&_=CNLh7=?qtU?O<+FrN? zFy%3}=#zfGCrXl12f*Yo;;>MHKe8|a!F!kPKuqDkyL&~9qd$v(Q^0Oh_?7oW3i&6< zh1~w6hc_3NNJZ7!q`wjd8;SL8;f7m$G^(sq;=2lL+pjZ5z*yLv??%H@^KvErmGQvy zHMs~m$v|4JbGr}+J6#YXxM-;*;%4jg=LwChKtU)1%r!P83lm zE`%xKheB4^%QJ>`?YJJL<=t=%&Q4y2dq{G>8D{#gaOxh*HPy(dJ|gJMylm(x>pQd# z#BZUF&@6#9O>78*8ancH=QQWaNk0 zV#QpYqxW)4W!L3^(WFb8NxQhPBP$7-w6VSyo?y7hOs}6jPN6|Py8#V3kGz&#c7Qw~nq(1sCw%2kT99j3m0m5E*m?EP>TU+qxTt6|{xfMqv5v_Acp~DA$VD zpm_-fAqdN%##V%%OwDf8glBzm-am-8Os(@jkqQ8S-`+u-4lOnWRa=oM(ye3pfx&pH zXF%d(WFxUq)ZE|T{uhf--zuPkxwmafFcm^WBP_#k-w5c;MdqbjP`#lWf=| zsUeGFnNWY&%ACW-t_d0K=7*$pv!jps15e0;jMO+Exgza_6@4-$Ig*=77dwP>{G@$n z^5}uSq8_UF%%aB;R94PKgIz5&!}bx_pB900FvJQn;X3oLzxXrii+Oo|KEPzYff_Al zyA{{NsW!va9Ci+#QSf#zExS*uWN4-IUS<1idIt`{=#qvn5V8K*dIxk|84{cJ_t9y@ zi3F^h#h^Py!>$2IbAB^eF=3Kg{W{vLJ~AE{cVw^^Kb553=E0d+O{Je(0#IBEnRypG zEpgJ(heDXs#4Zn=hla!11k^Le)exWW_#05Qb5a@U*HJL`|7;!Lg;9}>4#;4K3%=G| zvaxb`1gWA(H=px}0M$)cwJtJo)`YM?i$i4G>Z z)-gnRHyZ9*g(RX=pW0CzdhdWS+Z!#!1==^Njq!qo7~Ng1~s*EOIS^Js0EOY)U)N=?^#0I(vg{C}5gC$@d7N1>AK>B! z7+JWv8|P9b@UkfX$qC9^FE)K>es+m4rPk;@?>y|_<6B2D!@u1q-Wx;T>>t-*Sp=bD z?}N;_L9)C-?He9#lSm?p^#mgwrk1ZrknVAe?^!y>`!|BBXOM7kX`X_By{U=l>lp9Jv|b1;FV>VNS9~Bc!`e<-Prk4 zmYa(R<#9)A3a_gUkFwl@$JubT`8Dk!SF)=}L~MeGeNqK4Y*cr$BJH}!x`}VoQ_x7U z^&Hnp!Ea&w<6KbBX;w<%fTTSRu60~fvR`qN5Y6-|kAkVV^53C91`G9YzypN0)1g-d zf5)xG&E%Hmnjq+;ukyb%uWwY0K^+_FQ%3LkGaFgm8O-Eb94Cl@iUb3JrjsW?6=l-H zLRBdEs6jNOaz0wtG0;W|1ekL#3{rQuLW?0&wf^R8cm0MJDCKyD4o2uD+0APkl{p7Z z5Ylf*w2ta`Kr#G0x!ph6blb$sFs#f$mp%J%d+H=4FV6(yV|;TI*Zcf~mmzGXg^H*J zfcUf;*}S)0BI$#T8p7vGbFW$S2=&jDv*|i|IVm997vE@!{9E`*#A#*4DNnWZVNJJ} z=}VGNcKzW*WJy445YUglGfB+6MMzqem!hb?r_Vn`=%FP1sx*qZX02zX&qM-(6NmZ7 z>UW1-xErPzKKOF)-PRx38V}Lh%U8v~W4Pjd zgJcHnyK3$LEYVK#-!83?1MNi`={|w%VW!2RQxP;G>b^MR#J3v6vA(EO zjWYq8IS`x-P%gKivF(^^-RV2ROv1J4$k<#QZX1Q!DCr~?uMV%O{e%6lrXPyEM+DA( zW<`ASXfvd*s1+sH0cHs-l_(^Ev)GT$la6q;$L#YxPREkXdQp7zfuu|(KjYH&D#0ik zKgxv?Wr3LvK=3c=DaiPgg4u8y6b{Fs8&g<$T%cV#4=nf8CD|Cq&Zu|y>vn~`qK&WI zMgvI&?gm}#z~(e8T#Vh2xsPE{`8B7@5aE@Ahdosz%MC+O52BRHD<(YVJskEXd*SKj zW7S*OD-jLdJY2(Yfh+V5Tisf(-+~767`oYSQq;|=Jh--PyfZ@^oP}+eJk#`H7nuX5 zPKT*NMa_=D37_6FSMlwC3ziLNKsXnv(Go zV{!$u8za8@gJ%X(Fth|{Gl7F+49gAGNn^M^Wqi-D{ygB@sg}oC=Vk*zo(~!mG?=mM zT?*JP0XFI${*ECSM|^cq&$>6853~cu;h8j>ujOzIL*Mt7)+bCoDoZOjL`rn1y9g^? zJ(j#m<{`YFe50&Q+OvzYFFynifyiKgij47meez7v7)dsk9xMx1vPL87X3QC?cE_^k zGBW@)**$^qV1;vI;82Q8a;r<~w0!~Z&%{W1oQ8Z=kp=+5&mJ4aq=VQbeQ#fL66ON^R53>RBQvCDu}!QirHNlFCuU!Hv`wujgw=XZZ_ZH;Pb1?U z$VCY@mRufZJ1MpIIYw*tHy8f$K$p8 zWktgsr}r>|f*-+~HX;J(vp>S)^cFdQi)QMFlm)e!ga0U~&Gm()rN~RhK44R@Sb)+G zcWY4P0Oi`xfcv$42KSdi78O7pS+P{jF%w?nlBviW{)xFiq%4xSW<@V{)(1>-W}6?u!Y4;H)X=`@mSSUyeBC599dl zOwHUze>5k$w0ko4REpwNi)x=p_3AK@2DeT}eBMyb?X21nn%R*D``l}HX#=zp78;$N zU8I{s5SkqVEDN_#ZwT3>gaKUB~OFTe0D=0=n#CO0sx&5l=ivY!v>rgbv`o zRM)EHN}WA>EiQ(;UMhrkM#M){D{W(cCcA(0Ed_&6rvdI&-28}x8L?V`6-mrY17w|&Em%o|p>ioh@b$|aFhU1|E2g*0E zsA_i%@D%2bdwN@~6ZP=dxsUAL6LvzxOYU|;$3T@3jEIW~zneLM(N#*cd$(p%Luk-| z{IMGP{{TNgz`uCSpM%sTV{peV11b5`cGh}o8W-aN*UPM*4khugXr@o^B;=kLD30@+ ziak-9$Qtr&xWf5L4;LmOx(x|}J6@5~EpjEe>`!8k3~3CV|pk{mM|jG+9YoBv2W-dTHU5%U5noBPP* zg6|X@7-kft{=>K}b!C(9%Vg5|#oW_GlJ|8=xHZfH12nBu+P4$S6CJxsx?8D(20V&g zKK5%7<~XzLmh}=9uSrIzAYDOgySp|CK$Z})+{@iD{7nTQHQHSy^V(z#l=r96%@`36 z^`QSfsB+aq5ja+B({H*KP}3R3lZ?)Y`y_%1jYjm+rfoga`!dQQ%NizE$%hVWz=T#h zux|c(F10sOj6w^AV;d*}x^RE+UVw){=s)@U21P0a>8+6foty;>5vDU3Bs{2XDdYyh zob<@S*8&D&DKdC`@y1+SyN>%K&|J2&$x%^YzD%1W%rzuEl_V52v(IqJGX^$nIcTL* ze^P5^<>pom;OYX{DQEIZ+kZfKA{rk$L!jAVRYYNI##lAp#FBOI&AZ-ARbgWr0^oD@ z>tHN@Tfpl!)}d|w2yoZD49-%+n!^Gxc3fc=8tO!a-E4Ra!p0jV)8m4cvNa;NX}=T& zbd^3$hH5CKI%Jl})_w0TnFg{p?vG-dZk$?QD z%Il=&P#ra%1gCl$XM2GP_m!i2hLKDPT) zlS?vE%XnAA+xlkB5G}d(0Qr zde}F<-^-CElT5&`2z-QjI>1PT(n-2S@kKeu<_#Zb2(DgwqV}hcM!>A~hvO1pWgj}4 zzTo;ruYA@uO!&b`rld*!K!~!G49fVr`U`x%XQ-Kh8}tbn42iL-?HXS+0Lz!!AwUU9Cu) z{~t{hy*B_eM#9T3+sgtydD}m}(w3!CXSp?u6F`65^-zs!(a0rG#82pEPQQX4=&7^) z*6;?{IO!(oli8)Ie*qo;>#`wj0|vMcS$o1%K|V2Ub$R3E6z^(czdWY>q@nF~T=YYH zAkEnGTOMFw5yxOgOd~u`YToJI6qoP`wGEz=fktRp6OXYZ&((^YU^vXs*is4OZ0qZy zECupUk^s6nK-%zH1mg>AmJ?)>@+rL(7q~uvGPwefTPa(vHx{Fp>WHoV{ZrwN@I3T| zHYfcJ_1ak4Q(_7^(rV2(G(TRweQl&PKbOm>(v$-qYny zCl$Qc%ZgZd$kkc%TH}A+pvgUFG*Y(tRiYk;$$x^5Ffj@MnG;0jC=WsiVJ)eloKJrRR(OCPI;YF42?+?xsX z90)uC(u(JYGL=ah#&7~f3YqeWoq^vuD@Y2JTs4yuR(LOv^kVNfe~^z?^ehhc_0P(D z8A=}@L#zjalS!Q1emo$dm(0IQsrK6|dyd#PpmX zjRa}Kvd7OP4sy224HWI*HY?Tnpk(9|=O%$k6|7Xd?j~M+jEN393Ii8(W;qgUj`ak@>bbU zZE8!3sSG$Lx<+zM2PmF#JCxRgTSy|958_zl9wnELoAmoSuk&Px zLzvr4@708#R~1Upt*S0+(k0k~Us$P4bc?YHcLC+AYJx|1GYx}*j~df&O_&u>)8S@J z#eObyPnpdMG@}MYSK{jVm04NWXd79Vj+3zShs@40cMIwxdihm$1Lb`=MMO6a8MD;d`PT8nd?QmPG zo>-l&0kvw7NDyoaHe986cUko$B`z{~SdtkRh#%_f93H z@gHg3(Y@eQb4hXT;Vik=4;mx+V`MfCnDrYGJgwJy4JOc;gjgH0Bt}p(+1qTi6{KtQ_nN-GYTi5G{8YEhfAeB&tze_!fzj^gcZv|1Pkg5Md{FIHyQ{p zB}?U~E`+Kpqyu4~!O#x}!(?rBZ?K5@w^(0RdQP}T*6-mo>;k;~gHj-?u?=JThgZY4 z6HFQjIz*|HHqC;ox$vqc)CN;Hj-!9Foy=sO@N`$>ouW~-N8xTL@fXK6C|br;zFF+A zNr1UCsVV3Q{6*P9Fm;~^J1eTvr!9+u10+$#d4_HEpxl&k&=+-m!f#e|BriFx`e?`y z+R`?au~k}siPiDdO=g_An~C)tTgz)uUW=96H$S z*K;79aDNm2{tfdB5r%Z#pZue@Le>AaoM*n5QOD7!zgt2Ot|0lVWw8aKvI;5+*B6(f zuVsRKzF?Y>HSk{S_fxL8Sy&rXAkQ``ebZCnI!1xWLwnoFP2~)DK?|(&D^-s5OoO!V zfyKL30_TbypQQ&CdrT5ar_WHanb|i?YuKig7)luUgJZSN%Q?RnM-4{Db;?JoIRkUP zG|;;SyuR4vW7;LF)-hn^9=Zmc(MK(Ag?|d0ICBT^*O%cZ9bY^N_mVuLk75@Fmw0X94lu;97H2T{4<@)Gj?1nsd_A zW4$TG3g6hRtT3RVAIU>X=O`6~MoxmQH>)%}x&e(Ob(+JkLdQ`Hf2I5J`mTH``YhMY zFk)2H^Q7b!cf<%T;adfgOo0aoHP8eP-@1^zp4&)sjRb7(8kyfANjo-i0e8;SY*SuQH=5*L5)Tue#X-26XGF)rBn zBU%+dSg0cpg0{F{xxfx%T-SFuE#3C)_AX##@2wnM=K{6${tD^5R`2@xM{=kYA;i4U z7m+xhqp2HG3a_K@3)5G@z4Vu`!EHX}asYMzJEecGUIj(uJXo@hCGQYJL;y#}jjC+# z*t3z)Mqc8dl^5N6ed^%oVL-|?WU?jRIpEVO9*RoocwUKQZcCWx6=^rp8pE5Xu_PdO@07sZ_HrP}5RZQ`_9~XK!B?tgXxlZ= zVW5OMccjNJM$#9^!BVnO!L$AHIZ#QNPGxCDx-&wHj)R--PgO-)ezB5l+oK1gq?Ls) z;vgioK$N!{G@7|EO0MNU@lorqfZ&gTGl}IVYL-|-$+;nj^Rli%8_)lp!E8X5L*G_m z*JHOLlHTqjbbcj z-%GfgU@8CzoihVZX4Nh!m21Z}!5G{gRI14>6G8{tDB218<|;G5+u|qDXAs&bguswZ z24IQWMmThpYXbn(9kW1?@sRw-b8`9KmR}QO9$V#=k9R9%gn#?z(&l}HA6#W_9|O1` zcRK^KN!EY_KaeqDz250rS-_^MESEI5cVlrVJw^g^2mP|jfvbgiqX}8P9xL;8VNJ<) z7tOg_5)TAI2hyBaQ{y%?iZ6e6_ghkSCu0;Cmjb|+fig{UFV9qyF-0QyxrU0KTf%v= zp;9njIqb#hCv;L~3saLhuhhqiNFPk?e7e+^b#20eFz=WT6%Mb|L}SRweKPMrTq0}% zA34-fBf`YJ7)AxRsdlY!0k)|yEy89Ei^y_oGSK88(3!Rd?23z6?3yL;L2(GQZXMWx_Bxg0ny41LN|_;^w7<#%K%fwvq+D_y@lY;pSu>)B&~? zW~8NyE@#fjG3Lvw!Q5q1H*AQYqn^%aA3^KeNc`thHA!r@8020*T$o}w%)~4vaQb0e z3M&{1^VO&jzir=Jfc^e1UwrVAQIl^BUiQx(Jjh zga1@zsd^tMx(;%sciMJJ7>dc|84);YxWaqyd4DnN!U9!jqU>a4{S;iE{x7-qeM^Jo za(Na;zGgm~)Ur&UOH&4$uogEah#bvbO4I6N6=%jDMKT>Jnc&G_z9KbtaIKMqC%rcA zfL=_2%w61+xwZ=W=k#(wl4D1Y@WVP4e1?^hZmMrTQN6qTTQZH8R*`)$^44?V>*mSTmGatK&WlY1f<&;!d|H?%wBH44)J_*OT}r7egyS3#u%A zsBon{L>-)8{R|_3ckC<_!y}2CuVA}>LNL|peEF~h3NSYrX4F=-G5oM$e_AE3%h})q z5W*yQ`9_m4PspMKnm^M~qOF9FEynkaGEVq`(b$*3T*GblkN1HrrQ!P)EwLNP6scC%C%z=(-ZV=wHU-=J^d zWQ?V_Y#28mp0kz%Kw!&EbZ^hjvn?k)m-9?(W~R8Lu?+-I6&$v+;TsqiA zL@_yBTBnzJsm*P?)mw|-1g%G}^o+h!n`hr$`LwCAXAyCt?l1WQ8=-X3Inlf(smGB2 zT-VCOq2=|?@hbez0;$J7c8dsJb|%fDrem$T8NB`!qcY!8v*MW{f{NM=<2!z z>ndW3bTsS3ZzT$~U_JGmyH1tX%jmy?&7VGTZV)~udBqofz zirR#(NGuA|ohhuxH?YD9D4fgSU{cz%n*^W0vFD6-;HpK7{{fzW8LiLk$F5?QL+Rq$1Zr-O^U|J5Bdesfo?fKB z254qpA_T9rhyD*b>n<5xD3faqgaO5>ah7?rr_oXw;8Opr2=sGEQ%dA^ za<}~8u*Eboq78Z{26)l>YUPf7JV9)c4P1@fd(;KWT z9FSw-d}#Ue&9RL$(u2E~g8p9p0~!I&(Z#%-6j;_SND3`?c_ zz$Sq-=WX{CQl+6V(&5Q~$~VYN-ou@=KPY32l2SHI)Pn#dUXh)#Z+7L^#pG`hXq(C@ z%xJOXCA3{SuM1r&VN(Y*9b-K7%+U3%V`U(eSrwm2!m?1LuJfUG*n+*?TY zZL}cK;?g-`s~(C91qrS?{I!QasDx1g}iB~u^W*O%KDI51s? z`{r-jgx=w9=+z+L#~TCKl*WX&J?E2 zJ}*y@SlLSVPfBoAZy>EwBiK5V#tV}Hms*-dsoS#LNCCVDZ=hb z24DOcG~sA`2MAcyg`}Jz0FsGd0~kG+NXZqChRhY;^t)CcV9EX&i|y;{A%T$fo3r_Z zPE$Z;9xG~kzjs@py@cxEk!l>N*Yxqrn9FdASf*>Bxd|}~CN-6Q_uwldJS<9ste8Lp9!fJ2 zRH=j6Tdcaj1AGCt&8sGf4N%bTk;mTPBNP>^@-)$8_F%hQsq58+jBO?jiPW-0I5k^II zL8omh{;MXA91fURZPzNLk(xm^iLlHbT46pD@ZB}G*VO#=JztV{p9~nh2!qlbACM(i z3%=_;u{L-*ypd)ic+fGdO-)Pgj)27a{<8C@g^E5tVkJ(9wWd@io9m7&U(9&95%8^rgzReZ`|;r4VgBLNq08?~6|6cQW5y6dy~8x?O{<#D*xZuiQ-6T?`f;D}vL@ZbX4AxDi*8|A+|6|%l@Ps;j3#^H0yh~|3tS8mj zFzR(iSsu65F+hSeqOMy(V`)^6*}IOoD=+!MiShMvS&#wrw}7t8kZTLZFu}vHT-kk< zcbWl!!6eYraKQ6s!&THOLoihILC!VQQl<)FhK%~uFu+=Fv<@^4_2lU2N>tptlge3O zVhy&LiZYK*-Dd19A{a}$lZ?N0aT4H?;*|ZVA^7{|g-&CttZo)-lAt;T}y6& zo5Br+NSM+a(o*xgpYa5(5|bWRqW$F_@t70?46*nrYs$b7%UimV#0{Q|5cJFQy9_?* z{+wlWynk*!m`-r=fFfbo7QY+*&?%~uT;Ex2O(OZNc=IX*z}5*|ddL#%DgwlVmNf0@~dX+a9Bq zw$-syC(FPW>6WDwm%K61^OR5ffOLBPI{F4GAr}xct}*&gcFjBFck}9`zSfXB0`J#C zWVZ?VG%NJ9%mH~e7HHPLyblLl*%}O{s2jtcMlkh~&2R+Nf@PYv)xaSOnMPs*j``r3 zLSe^|Ebd>e%{{JumKZfCLZO#*(^b@3e3U{{0$slhn2!K~k^m@BK%9~i3CtBe#`L0K z?n^=J$p_-1X2QG@5TNVT$H3Ttw(BWvGvn^#h}_&>U-OB*n_V%8KT5WjWkW!DdTN@E+A=@pc>m3zU z{bVIK9OzfD!+4jan~u3+C3I8P?sy`<(eZE47PbF36TkxR4)tE4aYI(EE|zT`)WpmS z5a~UUt;V*3%g0#}KY2D7GPr|re_-wA*+&^l!6zZ)CgR>dOr2yu5qjn?sAhP zepe)+8M+%`qZ>tnpb0h;-n0vbhjq{!76KO{@TGY~8I~IPL53gSnSV%Pk5LWj9vd6pIEhTD^l*IUYeP!0C@t{VXDuJ-4~tAs9-phNkJdqtxGD-D&%Sd&k|jfX1E znwx!|p0jkdz#O=?Qu@2N)wj;ZdWtZ<=AG#`+T zQVsicp6D~-l6-i|_PWyrtldqAjNUE0G!Ae8AqLHa>0*n`EEhq7G~{tL3&Sy8FfJuI zAx~fHYLA7qK9$}WV&oX=RUfrzG#wGifmIk=0K28O5BXdYXxP4+EAD(@$gglh1Vcol zC%UDfkvz470?MAL!GVI{otk9UEZQgZ;t86VY?vG(i}IQVsRm zAXg7_3X&G84%JGv8CBEL-=ocJz782xh0h29Fw3(J2IJ1OROnu#HS#?#3Q%YD7s zr3X5`Ngq7N0zJSZU-M|Mxg*%exiimNO7Un9#m}*d!0(*oOSi#ZD*6e@`<~qcC;x@V zu~kyti!Av5F^o5zW;j|7W7-Zr86{FI5q-Kb{7h}E?5$R^jbrjHW>}M7!b8DWK7vln(;#5Rex8%UXaZC37lBJTk)7ufIf@a z{G;v+1D*G)Ub5pc#zIdmgEjfPwNVmWyl-()c0{owaX-sYDwvFZvmKqeNh)6(ihkE+ zqE=l-UhlQde?d1q?%Dq)X&wdLRdj1kz>z>9rX?RdLGYl1$N}b`Iy~HOl+dLa3*7K| z6_6cjkO9e(hS920*)njzQu;qD_%5^au)v1BSgO}LGpwB?`F5%e-OnX3IL$$!zkJC@Fg+zdK zTm(>(4?9uY6UbGj>m7Rv&7LPD!X_*a!wQWfcYurNUzCg-80nihH8TS@UhbnbTvkBM z7s?G4SqKRE7B@j+y9xlfAe`(}rLpPsDW2bgBu-D;jH=37KWGhrhhWG->fq&Y!5n!c zi|e_Z;rIeCk%D9k_@9RcpYMa}!$ko-Gu=t`m1661_Z@~v6yCF+9Nh7Usfid`ULH2W z(Vn5d!v?!UU~%2+SHi?Md88UEp-JH0#m8b*$bm@v0)a4VV1;F(+~(cFe@VQynkP2H zG*$z#=c&yC1P_>);aMSxYf_`jC;$+qI^Ed0?691G*-TrkKKi7AZ`J9Ahn%%yEYNOm zBo8tF#pLjndQ1V{=s_NfwXkm9eMkl~GfW0SQcrqIoS#Y4d$c1|0%BBn+7(}XoAXF7 zXk6@3Hc|vBZTGw3;tzAK=Iw0``;3wg|4QH{t^F6nEW-=4oWN7t>bgP027oY__i`Q4 zO0PY^XpsVxhBs;#<&S@(R4&x}KJsIR75&}&S1$+TP~nxPPW?9*!FIF72H9PiI~S}t z2Ki^x9Fsw!Jbgs}(9`S+7oQXBUAiWvsxGi@DEoaogJ&I@eZBL3B zV1{X_pw_TlV`)|G&@3>MBUj1x2j`wbK^{Bt6 z&CCU-DkPMGgb&8_*!u@Uwiq=Qxcy3Bh6i-oTdo4yYx~6Jyx;7;)sQ#Pax2qB!z&OX z^VWjv*|EsTG#c0?0O)Fs{v&_2r9$c|CF~5skBycM0((l3Y&dvE`*$}B#q=OXNkLfGL0WFaK>UujyZx%n~NSNjw7&sf$r`w}6OdqQ;zKOKY!`7uW)m3BwE{;?9O=*ncbl*Y&8lq+mW=^D5CLW`$E?XcdI z1hC$Fz*wY&`dbIBjdTg+gK(^zT23>|3ro>;NL&2cZwp0t2pKe?;K?iAU;k1nCYJw0 zzCtAZv}te*Li8{FX5f9%un1>X9Q=3^5ho+|iWK#(Hz3&PVv{!W866ELIa{@@Ko8Lm zg^Uz>%Pz*5{hECj9WaN`d?W$L(%>&*urmQTT6;{wq8bliA z*O0n;&zR$2kwj+-;fF)gwURb!=aC>+ZZPk&nnf3dE56h07{e4k zk>51?gVciU?b&`+^PkDxVe!zm^xcj8NOB;v(8B5MTuH7bd z2HLTqd?jWU^|s4=qCZUh6eR_FXTF=dN&p(Dl`9H6Ii@3c^3z$>bqd!to3JiyYU_X$fTAzS$ru{|(N4pi%hvWZ?h@3LSz%p`x#r#ole zdJlfM!aUY2U>-=*(7qpl<^>?HwrTOr$hP@ktes(5s-+x3I@*^Sd8ob1VU4pnjQFx} zlpUs%E?NAF_B?e&4|8QTrZFA#6m~2K239`da`S)@4M@!RsTKis+%7&jN~uO2?dS42 zZ1kZYZXS>vdaHXDzsaMOQ$xw1d=CbSwZB~9 zAqU^z*h=FzKp>KL?m*&yfRcV&wOhFUoM)|(itm}Zb^=d!C5~cPNOLhkcJVKbsdA_- zL+-g57x&WNPiR4GApoGn|KA81(Eg-msD}tw3)^}-StQVto}oN+C*=}#A0jzfVUH_t zwJ}kgZ2Y!6pBv&b_fUy0!3JO`ski|3K*(lIS1&EjXyuANxhUu1djfRrawg{xrN?oH zL07M4p!kGExaj{WX+A&ja`=hvigpb`#rCE2)O~kOyKZ~ps8C~OgSr`e*AWS2y9ULi zf2l8QKr(eYK&iS@pHgYIS%7os$ovdO{~?B3JkA;RFy?qhX5AjPFaIRNtUuk7WE|^9AzrocSfm_Mp94r7wPYaU$SL+*AwDs9iEcF+J5n9q- z3V0hnSsQ+OKXQ`OtcU*K>}S97=NZx*R<-IZoMR}n+<3K4*(rp~ToEyZz2WFBtUqtY zr8X<9P0Y`{>?NS^PwT$e!8d1YgnHta^{@Bf{XQ))9}1+HO4L4skifHK&I(=X=wV#- z`}v|L&DST5gF#{smRFS#q$^s1A^zLbxS7KR_q|h~g&A`4us|<;Vn(4#7=zod&s^*4 z($*)zT)#UTmw#tDSeArpc*6ly(F}kcTf!%k_JQ67CPKKoCI&rIV4mUuPu#o^|Errw zOUP&&1jp&?S1|szY6LgOf1%s0U>hSvRKTe#=(0|zL?OU}qnOQIoL3GL{3P0%@V6=4 z7Ggxei)vIyauR;k$P3taQoirk;7zL~p|^HuVEKmk(Cv*JPhnvh3_Lg-DI0-OC)>(R z9`NSXK;mw(mk*y025%SEVR=}L@!D}-81f5hd|y~%Z`~o4yd}@pZj~`3I^MQVEqEs_ zCT`{=lx;_dbA&NH6al=o3BPr5*uVGV0?W(GF@NaYhJQ@vWVQ1{kCMf4!n3-07{E40 z%moV$AmB`+(Ev%9F)950{-7fYA-stqJBhglLzwckGbm&NUImb5ofdF1iN2LLfpd+#uvN^=g9w zQOuLPNSdFJW~vuP&mtIo4v?jhVtT+|Wa3(DC-%x3N6E;vl?Z9jC812%#x+al~-cTWxs~r!;FTfW{t|KrNWYh8Abh zLH9!PI1%^zW2@%lj9%%$!6Z};{9>@nhh>VEhQdIt7b(}zwh0>D;}s8y$KRCIFj1y< z2C*nprKmAIbcRmgX!rW=tr$=P`6KP2aAb+fo*UkRg7K(C3;l}4YKVphh$4Q1&}ubp z&o09u)HJ!f%X%u{?kd^WeH%a6c73En$AB0Nz>vc;K z{G}{@d;*hpHQrlkce0?4F^qjQ>#EOSpy>$>1y3F`hT+xxT{vnS@`Stj1?+trd4Tt~qXPr0BwZpZYyHyIvad@?Ac1;|dV z;&GtMsZ4kol~tmp<0l7%P!l(7-@1tdmcj_5JoI{zkUwu!Z{6ti0kg2`t05Z@r-_3H zR-Farko@-uAJHXmKN!xGP>F2!v++El?UAWVImlml1{U0i$etAUM!QoQ6#OGU{qr48 zZ2Bt7@puGtya>Ytq|vPD0#=_o;&Qg|auV%oEcVb3`bVg2bJ!`Kw$E%jW%((q#e*`D z{rf-R$Zux&q`X!$S#q#o_Nz@2o^Yvk8EcKA)r4Wv^F`Xqm&rjqn=wf8ycj=!{231_ z*pt|9FF_$nO^C5>dv)yljNb=gi_$pdU|xBv1+6?vfTCQfIzcRtQDx*v{MX2yjDiK> zN%NK~n0K2E)&mDM_gq#NNbz%oL&^_m&k#gGo$^@mm~{nrmqUEKv>@s>47h#Q3BIkd zDH-X0fZv?ED2OVn(yaE!w4%*~_2xY;zRWHDa7OIv zgktmg0b9$1^<~S{-NlYwcjx0#Eh(kWw#ISj|=&u)0N|GBy@UydmLHfa)SG<{j0dSo3B1 z5!d4I`N{b>g}1g?|FbAz-`{a8B+0EZ*2+H_<`RT!r>|&g!qcFcve-f~l-02YQV4ve~FS05>+wzy21?QxBuZ?W|iwzt^ZJzk@n!$xMZ~vFS2H?JjWK>R| z02rR@15J1PYs(=CA6?`(a|jx`sMh4h(3Jv8ix!Q+Y+YcL_MH3o4$@)tq4uo8V52w= z4EJ<&wNij0NU1lGJ)@eN@Q!7Wy1yf1;2{4Fdjervve(7G-ga>b;|@ zoxGzFG~SuStq<%IxbduA(M3SkjUl%>)i(Blw^%E^X>63`3uq&m#PfI0)L7Vq=7T^< zzURKQiM9_DKX$MK_{U**UTCR8LB&r1C!-7Ai52T&e7udmUX$L)R<--at8{OT0@sANkS6&1rd}konp*41FTM=fG820Sf%flV{L`l3A!5~&_m)D-2 zbZWWLq;4)M30K8=(rkD-FvxanT%l>hr&anlNfyhFI&l&e9z0ucbMaLF_5;u;bo3-F zD^KNDbTJ&4EzI=Tg#`5-Bv_lqV)#wPn!{$C;3Awv=};N(wa^2iuB^Lh!}2igb^?lbB*{DO(9b?YCi8KiSXi77-bmOqTF=!)7V}PSV5j&~|T5E242U0BJlST;ul9 zQ^20q4f0NjMK|n)O5DVkEvvn#%+DnUDz`p{B zY;Xy@Lll7CnpVq_`FiLsuc7`Fux(L_6dcF{y@pn&1=3yw{UoNQk@lifu$VI4sj29< z0{%a`*I|uWPM(2oDDCIGTwuf+_{3l$``r%_o8xv8mw$zFx67W$yq~li(1ecH-gYE&dTkus?d^?FsSPqx9{$ z8F_|1=F2r|UFYI1l?Yrjn+ukEkKvV;5TooxOojTi7)(zAFo2%I%v+VC$oZZ_eT zoGFjk$`yyy@%n0hAoi189LWpn{;v{E#RCJ%riUiGDh+E~n;hMhF33n9jzOv&f%M6@ctZhK7}~Y~lrN9?dz*Tw@&0 z41~&5NIEcsKO3I2k;)Ai<1uTEAe}=9SYrWIcQ8$tZ+S=v!>M4iyl1Wx=EY*6G8u1rm0R91%%!(!s zlv_X@bKA@W5u_c+FT9;AY2>Wei_)&H*;99HY`=7y|BWy3rYkNZucqquUZw<5&UyB= z&C#fn@N1Qwy5HUplR(Bv>pQKVz0I7_G$2G&l{ZEDH`;#6#`|ih>>6nY)RjX@j5M3rM zaUT883`)dJofBt@o%i&c(Q)a7yZfoh8j;o|(N+-XMS0GzJD7h7P zK^TrH6faFVftCB1t@HjeOlt878ytGMx=}$V%sxOW&P_Z;wjKMPuKYnQtR~f9SE0~s5xkh(gmy<(a_t16pU$H0oSUQSr+i`|SA%;#n|MUs_)#hDc_Gawr zD?*tlW3*0V@d;CfhS&*^La2ud0)R#@zlii~RqbR&F@Pwo?uw`>nA{`57?P(@^XCz+ zVs*~$UXtie#V``CCe|#f7|W`LGo48PQt9vQFn{ z!Ktt9rHhnB#bN#;vX6H>)1v*xz+M{_v&oOE8u$JAS693Vl4@0}v4KgB2p|KWJd#V< z?clR}1PAZ3i9>cy_525PVbexdbS!cL1ZzA>Bj4tXS&z-!a@J-3qLNXWY^?|H7F%>VFZb1f17P z$4uvq`=&aNH%y!#3c#IGv0ut>JWE(~ONsbuQ#N6DtyDakNgk-HtBU*Ih6;k5^-%gw zE(OVju&ATCJVJ6DWuhMiX^+C9GgA0h3c|e60+PghL~q(k=tlZAV2K&5gT@zKFpl_ePpm*Qw^z z16AJ@E?wMoYtC#H47DUbD;1WPuwTTr7oCfkPr;NqbKNxJ12{*Vrl-n1 z*1eb_me%De(DdM5{Og|`+m%BE)335D-v=ho7cfy0N7u(qj5zte#Pj*7nO{{>ZvqxG z^w4d?a^DK)Rft6V|2&WK4U&ItY9IrkylnSno5~Dr#Pg*puvusNEu#xK(OkGa;DGmJNa4`>^_dczNqYSY zVH%$w!nhogoKDa=wVl{F(KatK2VOtb`<9sd;uxdA?q!+dF!;K2_YAgvo{`x$*z?EkBAvL{aJ7!Nvc>-QR=++k7@f zB}ewq?a~8z+ybh({S&6imT!)b1t=$~z;_(EKD?D0v;t4tFq7E%E=-D}=44X~Q0QX0O3Ju*p ziP=8o9I6CgCm6o@dGQ(A1$;g$2=f@16Lv8zo!FK&{1U4GF{c&i3pW_Uhhx|W26z?x z|HQ?*KT1iez2x0MeJIovCUY!>6(%8SZKDDROi&Vo-UuhiFJ{Yzx@v^SevEUP!2e*0DI3 zu*$Y;O>>fEuZLf@#NAlz*z2bu!4Lr6x!%T+wiErN#g({j$(GTbNS#uao*+p+r_Qj( z+2f;SJQjqU4BgG;_?IYXeQK5nh$WF&ur6&N<&2x8DWV_K5jsze4YqE?puXhKPr_vD z_j5+wBtk;YSITA0TZHM!EDaS<2{V3$0G+;l&b8kw7hW_<22i-fPD@s(H~2<5qa(%L zS5Po8J+H+fbVguCi%7CvB^Oa?=~aj7drRhc)BZ5-JH>8t_rs*~o`e9j_MC%Ya1g;v z(ny&G>wlpZE<5AH)}>ntCarI6K9LRVTDD(%7R@lE`~t*$^z5;}8z;u_@4BN7T8y0k z+)1c_sBkRwv2jJQ1uI9^{ge}K=FuuSF>{EJXc3|FMH!)(Z>E%u9o~}vG|nhs`~JtB zB79T3D}Ma72A=56y%_0cODZs(dS$BK=4d@59rM2;y&;QFNMA0iBRRga80?dN!-()( zW*bop42GEAsNt{RjSMo ze5=loW!qLJ3|r0zp>%J+b@c~i^bW^Ev{I>h_Sv@?z&9G81x5CITI7Ro+q*7smQ~{w zC`ws}of(ri3#xcQf(SCZnqUY;Hf_$%8h5xPa=z(s!z&U*WKKqIGIK-TMY*bW-bxBi zzyEr2^iGp`jtQj3is-W=mC;r-Alf&Ic#3#g3++a72zR;5x+o&X7|Dd>3;-?iXl#2M zU-abo>OKyT^T|IarcM@wB~avO37nn5>$AV{W>5I) zC%Ce84^WP;H=?Un#qKJlw=w^#+s1>MtejfExjeBQMr35CJR}g-T}fJa`?Rae`N{SB zebuIzqNqlpM9Lm{bJz4_+w>mpyFh5kdd%zPf8P!=kKrFUpWD$(D~2!x zlWVr^5;}>WOW!4R(2jW_Y8Cof5U$&|w(J$#-JdRo@$Ab=AtDKrY4tbSPLRtxYkZ4&IGknsiy)!av~i+? zbW971J1KH(m!-jPVjc<$@1W}nnlCigg*(}hvK29MaAM*FK%-xKTlJfW^u(|B%y33P zuinefKXT?0(g?;Fd8$>Rgs(a+uTGYphGNgh7J^PW}Q_;6*2tJdvM?hHcS#SaviR~O@grN6gH^P=W=BV4mR zj&Th0wuG0j>m+ATcR!viyk~!iV~w$SG;E|8{_U4hFWtb9e))$Z8O_V|cGsMXoBU``)gP?);C1N}k1&kD>L!-{J>2pv`Wx$=e=T-d~j2w0b=z`oRkJSSX~g zF0%-*JoXi-cxnnB_%CtmHbkqwT;ww$(BnEp`y3a+PA(AjB5S&0d>_LvoK5HfHl|37 z`@=QyiL;EL7yPmk{$y>4SHS5G>Wz9lR*6(2m40;#+^_p>6uGs`gkt=UBWTb=u=dx? zV5od|)qfp!&>RG;gw+%n%Z48vTWvGmSTBycSypF>7uDAXqKMlyjV2j7?l1D6GCJr7*0Q2{WhH+3 zk^RNW9dGvH-ReLqM;QC-=g~Sw68O@{gr7$Xo(=|FHT|C}sL&~SCe8b~)et@GWgEN) z@hFq?gcZ$V#YmBXk{np}*+)g5(}fdr4lyYb#pX`hz6FSHZEa0 zY}fXCcfUF36H3^(^Cnp^0TY+EKkDk<%Ks+^aS810Saoip&o>Mfda0#cvu>6cAG_qK zfex9-5f-WzS3PDAkG83T24!m`nZ-`eKcW4$Pnw%px%9+a-eVHf8G|&JV<`>bVt@LJ zoI)2Go8GHS>u6>hmpw~o@~v8A6@dYk#Kj4Vz2zdevKPVS_Fm26DuK#n^j$Dql?jI zW@%uW&u>E*WxUqGuY!EhvJ=B`=wIpy ztnYWIW4wzg{2W!PObkl7)xELx(Tw9FTc&*U*631Anm@dVGoSK9cQ_k5)|w5Pamcu% zC2$;JSl+r32@UeB4XcRWiiyJg0R;&jFruKHacRMo3@l^Ae^aCiuGgwCb#ccwuzNy` z0~}wZXkMKgWIED7s>8fc3CV>WjnvVMx{F=hqnz1JH6`=zFchsRF&!ryJs!GLH4O~s zO}bR0fDoLILu&3KsRje?uAt(FKDldC8YDg07;!xaxt@fLoZLw$^!sX(zmy_@m>sm7 zd^$l)r<<4y0t|rwk7MWgwrPWY{Nd#09$~bh9gA=MOiNIuduJ%VPH*8D`Q&QZhN2cz z%{*@d;de6$`|zp(ne~z|#?%7r@5r#aHTZe?l(+x?mpFAQzL~q+yM#E!!fV=$3#;cKxD#)lmZ&K++%l{RKrQw=dQ>hTCy5W<8Lw3sF|ud90Vg zou--R$K+H+sbEOJy(s30vtrS#zfU^wp^fhoq8%=-1*<_l`i=+W#Y`TKVIsLy|6AJS zGq#`gQHk-)YWC5RbO9bp8srG0B(jpmD^nbwlo06c@KP%Wp!La3#OpE<`u>&h|5rib zyx!5p3P>JhwfBog*nKln0`Xf(LhiRH@>B36ixKnmZ8cbO4vV&b|Ez(nRxUfB)N-u$ z4)K`i+0TIcmbt)nL7KFYtZa=fVdMh)>CTY(f`gN_0!W%4=n#ORlM}gUil~LLhW{|p zX4HRPkHDCdybt4*pU7{#8+eC=tPxvG;9p`r-tN;dF@yX*#RrOOy)l25`T1iC>RH7H zj>h}JyeSVj?r*)(z<=<mejl@?N?E!%oCqYriNw8`T2&t1S(f0YP? z1YCjjAzzUt2atV5YjGzPTiH;ta-lMcW@ZPv*O$OP;GC&{7wL`!``XsYZUBp2Tv;37ToX?ZKofh2(vSm`Fj4B`#gAaTRd zYZEXrG)0N}1u7i>*=nfx$MYbt{?iR-G(Mq8)lY4tiK{3k2V)_E&>OHPwHq86fX%1| zx49LrRXslP1)7V!Z=ZK~oju0|O2&|IcU=mZdCwDOzxjl!E@sY)p3JL5a)iLrsDea0Xl__U$R#ec*3iqvVO^!(uBgh92REcDQdFK_33_0 z|J07hwP|nkUE@Ey@8?+JT979%jycH=eN5wraDelpm@v1guMcK%V^5|*LHf7pM2$<6 zip<9IY1B+WA7=EYy*6(T@82M-j69x7TYv+h1*yOBHRQb@x!BrdL{H%$Df?@G z!qCi7Pl7{>^a;ElVjbkelMWh5Tju;elJ(75Lr;z(W?`9^aWY+AuKl@CL=kS>BY6w! z{P&qK;U$bEILV9mxjxjUmD-C6R$ss_r&`9 z7&94BHY35qjWDP=mN+|eLD8o$Zk+2tyNw>;DPzEep!~-8TMFbx4+#!@43Ybn=73Wz zP}2VYmdiAYHRPav{r}RLaz>?Ghj+L+mqUievO~gI>{?yt$ZHy87D%#McRQ6dVpZ?0 z7js#oLlb;>2aZDhUfPEANu_OXf!b@&hNW34fy$<1gR$R&0E{v83UNWo|0Wkm$9+6E ziab!0#&l1i3=Tc#ps=&v;+%@VE?$;ehLP#m7f>vw+Sgevx7#(kmBFtEo!J@aL+mJL zXYH;(`AS=nzj)4Qd284=FONv3WP&>o@rVL4@rAs>8QDJ_cIC%~>%LFlo2Q~Me`!^E zqDO|}Y=`?A1Ei0D;*w5)+XIWRFDD|8kC7`C{Y-(~f>4he?i;*UdO3Fb8$^a*r1DeC zoHPFNtiDD9e~r+MypJEyt-*FpX_REIdfjo>5#GZ~721ocH`RZFXyuCizsW=9SjdIb z7M3v*XedX0&N*6-7uZ7#+2jWG4d5t=;#Sn5=_jkSK>4an=Y_3?4gi2ayjYa2pTt?< znqM!1)c9@)@P;2Go^K1Kps(^fZM5=-F^(Y7>O|x=TY%!~C9v0H;KG)rH~{y%sg)Rq zZdIvfxTo&Qh}&7#-*T#>ISHn~@D)w**36k3lCg@{xkLc)^j*8aEzc44evdKbyR0Mp z2%xk+i-Z-ba>&k9GWNzr11Z7jy&%1!B z>zgc|r7iy!#3gKc8dfM{<;QQa4e!j7OZn>c20R(pXa{A+WEE#2r$!gjM9Vw^anvH< zh9AKn+;;E8uMZ9=X}=N8n)?NR!0@^THFo0S1xE}M1a!=(h`0eBl$$J8-FUc1*La4T ziJ?|<{OY;dmP~-h;C*R1y%vy!C+96*_>HVL2^!|hw@*o&cKe~ukz<$45G`EN55-#c z%2TO0hl=W-_9S=4E6vbrO2X7~vqY{El^fxt{@TmejYt-ZVXK z5VNyXwZ?(C-C4JKuqnjz2B?lE!M_ph)OjJ;F0X_}5X0#l#K_FAT2RkteGZS$s^_<} z>7@@{ytKG63|+beD?$U)b&LtnHY)jEu|*d!E(_h0+-U3<4k-XtaKFT@T6%fTfvKm|?0&zd-qX@##n(c=2KWvq9Ql10fi(e$H5GqnW-Pp_Ac z_(ldF7Ve9z)vn!h(Rpk5>i}b@!wwK-qX_-%OA@?k5T&7I&)FTQG4CtNv$6g_I;8cq zg(z*iL(RGw3b@D-Q41BsD{>+XMw&I2PEaqK{XxMgX-%&B$do$cif>9KX9x0@T8#x_ z*?+0GX-K!eY7hGJxmF*$teSeUG!a#C^vR_Tby_bvH}*;f04UogZ}bi zlO$w1rTQ6u<9R&Jm5h+U;=)d#A0PGMqUVJbt=AVxx{o75zaLNZU6YKUx!MR78eo_K zWHX-Wd>?OR!PiFL9t(IXSQ(Tg3cw55bJ=#oJt?b>(qWhTlD27EG^w>D*95b=`X5Lx zX%2N@t6|5(^B+}O_>mn78g@-Hq~uS?NLAkLP^D~Cc=eyyRX{0)JuYN0xiJ{y{c0?9 zH|=#VxIN8?wKDYN&q26zEIg=`{hOGPDf|7%e@&`i83=obHj}^WW4+ghg@;XE@`H{1 zbxQm%S`vEAj2KJ}AOQGKfjV`zFxhXN%9c@{pZCD-^@ER=Rdr78uCgfr5ICn zmwV42D^yk@2bCEz>u6IKx~VsWL-8f}>z!!ZiN0EaxC0@$^-uwISK_a#yKr)R)$8nl zRk0&jH+HopZj)A+e^Mo5Tvo4?i%}3FF@tNeumh~(S6~6UjjZv9i~y$B*VCc}Y27TTyL2y%G&aCz}Fc|3syLkW|TR;%!0?e`{|T@NfWMB`z;p`SnU@b4)M} z5##h96iWwQ^$z^|MTLcE)#neOuMWTE$ZCK(3U%Je(%o|bC}328B-b^KZItGn>Wtj% z!2|OoUSnvw-X!ot7r>;IUso4u!1RAv#dDzz`BX&t8DwL59~X{R7@y`;7?{RPvcC6} zQ9irV_X!X=qxR!14kLdO+rm=5>ci6ko)iwb80=FX(cFMNcY>v9YGtdMm3}s*vzu>2AM(UTWhq zto6x_U3B+qN7pjvSzqJYO2JSaK}aoh=afJ{*Hmf63$QS`He*r`YR#v5?~L=|jjMpO z%HN79lpIAK2z1?#zAmDampa704LsUp8BgJ^W<(0+wSt^&kdU#V`$B+mc)aKq(#Y!t zgB|Af3~fEF16Hn6RID66Fa2OP1X5|r8DF@5+5dY;#l-E+w`7azvk=H)FePu|D2f)G zbfL(Q1_m2!a1_S7fSjxHr}?wcBOI1-1ZI8@&*z9gM!O}riyw*OIl-EBeQa;2&^nQZ zoDuDLVvx7PL_vHzaY6W(!HL>CWDm?>J zis}r~Lh^t?FB>NOi|_cg*^MCbi4vPeETL^;lb8j<6mSrS24_#sWcxLVwLaSR;>*m! zZ@Fv11KEn=B_}fJJtnpG{S+SL&DWsCYNyzFzZnGf)Mh{VIP`csgPLuKl3176RBW~Dqm41g2U4k4I~01YHG^LN$$J}FvY6K!~r zbsJC8Tm^Ykxr(;88$TQJ&6LL0-Cv1B)d&94h2!ZwqFWtycW(M~4|k?%ymw5_V!7Y;S)G})N#pSwpwL` z3I+0Q0SYGieS(MhhK9V!;?UQwhc9yXr^-GSuS0`?S#Dv%R^v89S3iH$=M^On|6qyT z36R6t4q}*3))(HA!fsJAKR6||YB%2% zHTGeI{h2)0?{RcUdMOcxL;4&)#=lVPMb+VEK9~Ng|L!!)&?}M)cOHa9?DQIgbgy1N z1c5xi1iTxV$>S@q9PXP_zb!mAT)HCKMSDM5Ot2 z_#Lq7`LtCOYK`#~>7b8k{PYZO8EJ?mbAKtiRPNcBU1C{06#E8UiHW1|+)J!k*1(N9 z#k8oNR7v=Yi|RPMampM7k0AMk1QdWvak+?crl+&9+V>$Oj{{IvgnFJff3U_CdJzBJ zMCTwJN`fYL=EMh!q{g{7S!$f?%6bFYRIG>Qza25##)2(>21vp-+`&gQHWnC0*4VD3Px4{w9)c8gB>X2$^?aD z8uceF4^5E>VB`xOHZ^d^z|{~h3oGZpS~?456WqRW(~Q#wGk17?1rrNtHr4eh36Vna zwc^clqOwdw-tBM_408ti7O=j;KS1&oLpexb(~gV==p zT_X~S4s2r{{5`n_9=))4viVsIxYLja*WoP)~XB>(R&>B4<;W>XgL?s7|$yUc z!%q0DbcZAdce#QHQztL4*Z1hcss;gtygzLC^*OymZv}t1PV$zQpt1uVT)Na7`5B?b z1`!GF779W>ih2lN+$6qMR=T%#fD))?=3J$kCffekIUiBuonM@DNv31oA>m;i0s#Z= zjX&UbzwvLqKCOV&+q6utCN5}gNqw`yldEK{TJ`rfn_HnP=~0PVEq~@9*fV&h{39JuHN3dCLH4&& zG~`-f)9Ox=^pnvv^`%V8jX}S62ElX*i2UMx&)?~W^alMfLGqe;rzSW!Pv;!;sfe ze7qx!Pau@1`%=2rtAM)iZ|%23S-)7f>cjKRwpqgoRJKzI$-177okYQ6A=x_C&xsxO z-;D8qyIU~z$0D>{Cwr)t_H$hQ_J}h0L%$85m|8N+r^nW#r=#~ z#e=gWc;sOnI6&XEsbRUM{9GTA?CSH&b9TfLReJToj;C1XVyV>W{ei&EqvUsJdkuqR z6RSz;gpbPtw;T!IeT#bLA3fez%cMe0%Ma3$cmukv!;Pow?nOo}xWoIw#rBj=M%pQN zVqFaI)pUr=ltl2f-siz7+2LkirTvn1H0YNHU>tCYTMu)C%g9)l;Zt zCfVU@RJZnOt6^-O`}f0E2A!ejO{k^zxmFfd^^3_0+2h9Pw^^ObqAB4HP=^!G0{y|y zbv&y#TJK~Vl0jdf`w?Q9>+=4GuQb6$po4uE*#5np_GZ|W4`X7Cqvw@Tkp!~y{!4Bc zZjs9Di;YQ=&;LYPSfuzN7u=KRKzS8Y|E|6YSkF4yrhmu?z8DN+C>e9N%h%Wq!ic*8 zFWO>0ZYDRu)_lRB67R-h>%63}#vM-Gq@@6cu8s{54)cSxa^2lxDGr{~+?1(-S_q1D z=U3i5#=*a{9(a(gP&ALp<xk4_Ir|qSvePHXH2XRa51|?H$dxYuZ<1LZk3cPwSW;fnEfGg8!bg(!&JT9D z<)fpdm66)=#Ib>uJs#FMtJiw~MGn^96H%F_#W)R~*6Vbo}8W6@Ol)>eIip2alKtcMHRaF7p5!|UW zU%*h8JEl>3>Hfr)nu)2Sa^&)A-&~G~F%EI7*{;vfDnC(!f!?*NbPXTUn3n)$6mx`J zYEeu#exG~zpljrUy=ZwVRp*Adc5W{@*|w&p1aSPx)@o1M(q|5Jg^j^k6jSOtgP+pq z;SZW4Z(bBFYwbz3@5*CWD$bh34X`O$aPlD>F_ui{`^1?fiplypZ<}@N&Sa;i;8)t| zf|$IncrM-!`@h2_j^p%rT_}BHBYiFjQtb+XO zNQ{dCmOuMxSL#L#Dcs6;LZxeUgh#7`4q z@9v!^Og;2rg*o)s{wMDgcwV1~A89e|d;BN~@Mgxu`BPyPNXaM^g$S8c(xk(U1CI8p zUKf+fJ)PZ|vDr{e2PBU}vFwqsrv3;8@7o^D7pXwQqFHe0fC8OIRk~FRUsLxu!UNin zKB%cEwX|ETEm1zN#>XwB$oKk3vwA!c>oxD{cw3ubozsBk`;y1t*H9154a-lqy(!al z`vJ_7yK=3JwFn^zvnN2n8_z{C-m28VjScfC8GUE!wJ?z?TZX0IoB#C95)&28T!iW^ zBsk>Y>`Bn2p2JsUs%hf|M`BlU-WB3wLrFzfYv=yVC@@$^rl@WApAyXn1Bi82vPZrS z-vEOkX(llp$E0DarI!s~UAg8wn7v8}yI!^_svWc--Y2B{j9_c8=G87R-?4mWP1x~v zwx^`y{^K>jeF>h=amS7UsqJljStQ$zNqmoe8WY$+Ty=pP+;6gWr&;ZwL&SFSI26To zp(p3YjkxWuNS+;%BH%64B!5BatSF%*g$ip4soHOW)0Bl^uWSFM6NpmxM1Yre!-Nkk zbXGs%zlOpyRnV3U=ZOdjHnuL)nOeQ7)S+~HYccxnpOZFbqbKePb^ib!l9cN^?mD)L zUFcj*oyF$u%O9@nlsS(#w=zr8@;1zU=n)l%i(xMo(U1HWuP?ZwgW)q@n!5L7nMv_Y z2DU%OfV}QPd)4^To?%ei>Z~V*t!g>;tqS=KasLFxjLH!Jos6#ZVd9>6m51voxb`lc zC1u3ZSW0sc+2Im48+NtZK_6ql zL3v_zv)VM}Ijvi0*_l%HPbLwI^c5rk=4tLg+G4 zJp@@(3k)kPk)AlAM+$6NTYHVIK&d$Bfl=--6=nl>;$5?}c;zD?fmk3vc2xqtIg3q~ zT(o>qUEZEgrnGtVBje!~A_ZyTmHBe^ z8s;VWhS9rUwZ!P&l7&hqz`nJ4BIodd!g2Lx7FK9!w|zDI!&bFkrp8FYo4JJY#QLVS zr~|3U7I_AF-J{)n)SBv?LUk8K$RAOu`$v{(rme7COXVOSuh|^-S1NfZrsWJf=XF<< zDyOyghK7O$2#;*;m|gH6Io|PpBfv||o5({p#m7#*ve;TmOU}U{Peo7b*yvW2zVl9c zM8WzedgwIQ*#2YHYbZ|hjqn_UkvW@(tlYS1jX}AJjG5Km z-?{C^M`~IA!C43{)PM;9ce96y5EKd3bB%2d)y_^eX>L*CC@o&n{e8%B4={&@!jPNF zlI4D_d%jU_ra~m{sc)|Uz!Ejzu%8GwgK?C5vZW2s>$l}Q<{Js5{dk_k$Sm6)1I3f& z3`zOBR+COZbHnmX5V6@P64MDV+awkArQn~a*4w~SZv%7uWjwRKuVj(WK?djwQ;KK{ zCDM72F{Q{6`pRQ`f4`rP*fL@nLph7?pZF*(ql3~cY+gc5`maDaV-5-3Eo#r^%puCf zs^*Nn+whX{rue8-M`DxxM**ERGza}@N$qm`{HEn)7VQX6BT-^uheJ26P!$bm_|V{k zCo~7Z$F+MorsHKLh4r4OSDIPpj{D%P62`SZtaL!w=r`(fWJ&+IU#=+Av~MY>eI$fR z5KGx{jRmidO^m7#YcLwk4`xKo+9l0Vd(0J6S{NS~_V!JKvBfHzMu?~Y$u34w{xCuuY!&g{xUEw!t$dpM=EINX^3kSa|P zbYFW947I$k6I@7jZqWVZ!pQXu0S{znxcJE!QjH6}(18h{HLW+Z)PX!o`r3yD@~Y3e zXGTIbgV(u_9i>r^IdwuYH2#$$&w?;3Ayn}UUB-CJNb@1Z*~%|ELwd7DG}rLoaiCym zJ1^aX^KlcQ8&l*f6!F9Aa3b-6qstJ>a8mD0yRmutfO@sTKV22_=^rU#UWq<0t9Y-x z`;+w<9Y>qkOG&IMlE`uZSp2pfxsG?#VD!_ZQ%;)#SA40}nZ*Gb*rL_ev_H=l@zvfz z>t`(G2&D5>DG)Ey+0PHBYQ1&OMS53&EGRYL4>EqQe}SHz8U>m0YrS9g5`759RGsLd zaYuC|w+Q`FStI7})TIjC8vK>Na(Hr8S9YBJQ+K~z?$dm%H^z5FhfK+X0(M9u`tSa; z92gJfimRhs!Ju!2ixtQo?x2JIsOss?>T0~lB7|_Y&NjLamh>2<*5ilGL;x?L1(mqc zl*{skjlQ&!;M}Oj(V3uFuFYZMxNWy1ga!C`&S9|15mKE!Ga4Fgw_IyTF>A+a7-=F# zfLn(e0Uv_l&I@rS$T%{8xmHf|`zYLp6+ra*R{HXBYqY-77B49Gwi%8*Bjxvj~ks+43Q>{9S~Q}XJ2hP)t`jLZh- zH9*&d1Cko-h||YJBD+-<4a@K)&yA5dYK7-3Rbrtu9}yifDD&7P4q*AJaUU!C@~)dH zJ3?l@_@lI&84tD$SfkePr#gZYpPd9jh`NJ+9e?&AK&5Za@Z& zy0>W6U?lc8TwqaX(FfUKw;$m{>e^3DSs%aaF9){hLz`g^x&K`}^1jgQL6R;QErw70 z-rLo9^Z+E(rY2&&TlW;PW~P4?#_V1e?cXgi=_Mz#?3m?*Ce$oP-eCIs{qpUV2lK81 zk+RZjoDwt|f~+Or%q%xK{3Rw&;g_}1{4Xa1gtW_L;;z@gcZlU;F;Q;kGM!(n9XFTV zEFx?1?t$i~d22)KEQk1!(=zkY0Y+2p4CP+oS*IHGd@^r{;5M2j?^w=7ttRbTrrN&S zu)Tfxqld-_Py+b!V5>09!9T%VXzajnDr0O!9L;-gpdU z!n|A1k*9$t|HN^9VAJxl!9HjUVQJy7s&_^yZ!;Y7t0mdQ2Z5_=K|(d}6!@U|?;83oRDq3b&+Yq5L+!8y z((!_(n}To!sGwt*JtEh!6eP&m=79G=hHUI=qpPLQpAZ5-$Mm>|iCt(rK?*A80Eh?q z0;rdMt|ehSL~OLii~1@7E=xQ`oOzm%G&WR%h@Q36{o{bpP;0N>m}n9RZrJZGf1>nb zxjWCvp#Wn%j8XIFL3oknZwNs;)s^ydT)z5K6^Z|CXX0+M}xR)@gh3-?Ix|1Bm}hNLR{)X zIU|6^4aYLw`oc)wmx|KQ2`R)ewyNA59~wgKoAj^2kmy=3GbA?*oS#>4b4zSxOrc{$lj?ASwF!a7fo4%@h;+WRIUxifmmD< z#|4A=neCEkJx;9)6$4p!YH||`YDn4A%_x)?A>hbwphcOFSqVF`?itwg!Ugx-@uqg% zk(;VU{C}2#*oRu^@FWBLtKCp`hpL0hvKV`^GgKqObczlWx6N*; zAj|$KXf=hO_ce2I&5li~ldWl6S?2OsD(CCcBDA?}b$s_ftu$+~{jx;n`?MQM%mnC1 z4G?IfC$n~DLRW@A8*0aOclnWmRv4CqQ_5i6Wtb6;%_tEH9KOfq`*?Fb#SA3C%~gxr z75RCwHHLOs1Y!bRS*coQ^FL(X2B_n?x6FtfcYcHRr!`~cO4`{1vK!XE^jK0oqX%PY z2Bx(p6V24(Oh-T9d{Z+}Qk+sS2A@png!;!iCRj2BjK@VZOg=;EjPpTjr1`)2H@2!cGm6g?PeLX6+2(Msbyp4x)f)uO#P!>^ z;A?boNIgUq3SVgb`=)@m|8HxyR+u)g-O57nOKX0WF;M4i9srWbh)#{?zF1)oYmApJ zG{c(Q@piYeXBCIY(Z5KUV!S1TDWmosoD3|}uw9+S?+F-sA+C_wO*Nsa-YYam*7kFU zF;MzU+;hMBCb!`Px=V1LT>3QiA6`A#xZmawJ>5#N%fDhw@-55yfh-`HK`j$qB{4YQ zYRmnHgEDpn7!|^?1K(Armr<`QdBarRkXMu6TiVB6hni70%3OWzNG{ipMFCO0vUYrN0%s>$p|kZkTC9uJkur+TBnw$N z=F9UO?!A7YtFyekPpFK{uOx?v^BFr7O3}F{rKjJcR!-jT?YVgzRl#T+#9GeLo+cEp|!t28{yBuRV0!P9<`bbfJ2#Kh3VZ z%c8v=r$SFw8Tc`|S`gDyrbHgf4`r@LR~&iavHe_Q7el(g7t6@wmhy)CcjsaH7P3-t zVsmbjaHtn^?SH8M+FyyKoUE^`S7`E`bOaDr8$*e)qpqW_X>^fpd{Jtp_(q+7bJvZU z=dwF3eZ}snw)*4@`_vYg{3bfco#bip>$k0@*2zl==7zOCSq5a-)iW`&u;5rIef ztafJDb~tS~MiVL_nTVs5x3JZ4tNnD?wn$$boaNnqtD?w<0ZUxn&clif7_D{9Ikrc= z6yKRW*Q3MEL^Zk?r96F2Wv)G=BN&U1U5jzHdqcg#Z}raeV(%}oW)#AOwMUszHYlMi zc-M4d2UY8+f5CjNs=_TwnWHKun(t2jDG#y#R$2}kKaor|1~3aqXwd4tiW#IgN00YL zbea7$69B8lm+7UVSZ{#)%iC{j5F{1i?OpF@nnk@w6n9KNJC9a_!^!Hma(#{0>JSX0 zI0urduGb-pFvg>)>L@s-V-{Sr=h}i#{Y3=wzq3NC**4V~xQiu=npYFDzKY}e0!tQ& zZg1$kC?HqCKJmSH`%CrS?;jbiddYm)JjB0fHS!PA|=zx50W;8)I5N;BnD=?`mdS;wzv&u z!u!_G8_PauZHdnbK)#=rWQ z@sCFpK1t%z(r+>umW9YEQ1yF7pTL=%gz*9-8@#tZy6V)-UP9Mdvfq6P^U=l;WrL9$ z+^N1do6FZeX2c>;s2|;eip^DLdD251$5=-aO2Vv{QM8@}UGo~zBc9vAWc6qZSBd%2 zL)aR0(}Up~ri@)}7H`;B6CQNQ0-=uX_ZI09QySmu@P#%!n!`O3M8e?#Fmaa!Y&{c+ zOzRE&Mu#Y%u)&q7H{8Hyfrg@iJ8HSQwk3e5> zP^z!I=;L9MYGy6CSgO7?e!F7>*b0Y+LVb|Sw0@xs1)a>MIAo6iMg)hNsu;@;TpMi| zVa6^#2;yhwOEy)Q8!(*?|$aKBogc{O6Y0;;~modb|(`qqnF;S!F}kF$1-_JZazLlqQMkuyk9 zmb!ktzS7GQ1#o+NsSm-!Z|YKP-&dGx-UJTzw0ZYX3N07iIL#G}@oTE6gamH*=aio# zw$2J7kb@ogiZaM7Z6h%z195v5^8(-@zRT(_pJUn1LY^Ub6E)r(8Hwwb?ud*c%OD<^ z4>k%HuXD*@^=;F7RqG+{v7h=~*jU~oz3jUt@zZ$2&8>U4`JU#SfdxoZRjwYuE6Uz# z52`?P|3t!AfLj68wsSJiS1Z^qA9MqxF+7W{N$fV4qthbD71tn~zBC~kpVxj)-8Hl; zM3c5gwD(ys-Rfd5B7)IDz=6&%Mye(=dhf5;w*jp;yURIx!K@L4d%>z6&RY*VkYW@X z5$5RDS}F5tz5G2l4?YyM5iy%T(;E|BP;4 zht2Sg4_s7Aq1;#F8O#>23$P4dfM$4B)QiFGedxFM38CTIB^sMM_s$!0RnzuoQ%_>! ztAXb}U@acr8yc|@8sB+9QV6TCyxIQHqFj>P>1D)H5{t!zrw$s`Yf`8B&)TrW|1Gp= zU5H<#942%|qwK=oR2^?H)1EW)SA|!!D#Mh(+(SrFLr(x)OhDUkwP2301i%+g*O{zu z$u|}AoTK{@BE@Li?0^;cH!e03Kisff$4X0Mfe`xxbI<~b@{X50st0&A0GTadsY(WJZOnqkQ%|- z>eP!SvMM@IE>KVSEaS~kW~=-EBnM$TpCh3a zxYyv=BR54N8!Np4HKn^(IV+VZz*a^`O!XMUZyIAFt6qqFMBNiCjPeFKRN`F(ObP*v zGq&3V^!$0e4>II+FEglbQh*CGV?pIv`EjLqY0fS8lRGevDs8Lz$b&!g5r0Z)Uo^G8 z%Eod@>gj&rjGR?gv&{2>A^V$7F6VBm#-df^Kz=8ixs)?v#i4y667a6|)JwTo)kLJ^ z6&Vio#T26rb$7EoXWl^z(cN!$KAx!7arB&`)?u!Z zv^vl(+CLoUu%5550ge0aloNgWghS;D2DG>)6c3b&=)M!*5Mj+aW2!oL%Qi&NV&dPUQrlbs1shLbIVHwAMMzeBNNI_3l3n780 z*H-To<%1Lyb|1?;%@<;4h8&Zot?eW|lQJ;zjjCrXJDkXoDm0MT=i0_mFCJ8erCZDjs7pSIZQoE~;Z) zn4{WrWQA#|_9-d#ba(z|xpq^wuWh)nrv6*xlS`-27)1ww{FA{q7(}Ip*)Y6VctIG zI8UxJMayIJQj5*2z1i^KL+A$ONVCj_w<{F{Ds3kypW?FfLC^(Z%mGRUEm=kEdVRYj z$S)dM147m5U@WzlaE0Zn-V;wupn_;+#Z6V7$v}I)`1)clC4Le zin}!M@J+EIWaB%Gf(-BlninH{;p>Xdm=iEZ7aa!6#pqiUi}NJ&zm^MZB1LFp#yLiY zT^>45+daU#@1@o__8S67E-(?O+{Nhi{A5UZ0WP`5b(!^L#pQ`H9*#$6Vh>%mjq4X8TF6;pt7Aa|r)T%RDMA?J z=PrByxDjXGQ^>KtKKpo){Qgg^qF%x;1hH+k27ovEknjP_9eq~M{W)wAi8aGHJ!T8g zW(0S(Yu~$y?8f<0&R8dT&?3@JlwX+@qC(&!rm<7=GU$>gRB2Kd_(Na{Got3J^S$+hE_uxG4Gi1SnH66EvA&si(ew3^f=e*$n zRYSgZaLr0oiG4qI4`3w8RkqF5U%EdpFYbC%JMWFEpE*@%r^}tna8d-+ePfU)QPbYq zwrzX&u5H`4ZQHhO+_iC6cWv9Yz1bJ_C6)S;R4P@e4J6=BQ=|fUtwUMT zz?(M?!c{MkrlUDwHh+4>@i71Uw(lz9JLUlHNE8j?U2`zQeh0DzQ|F)m28R4Q%*&19 zoP*}T^lu%(N?c>^x__%!#k+thk_va~olQP{`d~pv(shlRZ7v!wEAfit4tZ~nnATK{ z-!k)*ifJ)CNw&A_TTB1QXDSi_7X9-5c_)l8!6A#w*_J4i-DkG z$ktVUa&mi+^O)$1?-t;Wi0H({KAa7XOZb$8F`$QT_| z8rOvoHe?93VKt)iNQZ$ZKR5!}8G7^4B;25It*mRpkQCPp?q}H{y(g*On*UuLxA=R4 z=ILV|&ERMEt!}DF&Fd_Fjjp|6?7&8f?OKIXAPO;lOE#5C40(WfK+6r)Q6(%OrA*zd zV=$Xu3XU~e0m6Wi0BC{;&GfvuNfTgk0PXI(Z94Uq;k!Wj0TwF8OL!^^Xt=Q7PI;9*INiv5~X2sYEM40tab1G@9ib3SAt zLQmkX|9mcQ=s+Cci<4htvkd zlQqCA>E0p@6xpka19WRm#$rgG8cVdgQ$L4N?Lx*p-h6;%oeRC{z@858U0E;qV8Mjq zz9I$GXF@%rDn$$I>52Qdn}6wV1}ivj+&`P(qo!gpQ;#|5mJRBR0ktVqdx*L5y4Rb7 z`AN))1MDI%=4N3*fuuHg8p5DpbnK8`*!sKG*)<^WACzh>PHgp5+F5QIY5oPHg|c1% zHD`A*M!;4?jpAEcH;~{0(5{$Ee%)>2)7_2}3SD(;`cKJcWtl=raytoK>(#RDeixVdc8}h1tI+=gBUV}sr z!LXJ8=qNBHqx*(yb*Xhnv4yvwwryj2j&QmZL}8q2##9DFJ*b}+4;qxKaR?$2$V_H= z6X8XM^B+2nVyLN-Rs-NFjG6?@GyHAYSdF!!vFkCl7GWRgR`Ou1QM-|fsDCG(7xR#_ z$6NDrQ2&r$Kt~XC@ugv7G9Q1!SmFtC-J2cBQeTjV3c>N;TmM^mb5E1%Sf5yyZ-7gv zqE7H6kM{c8oQ4U#WyaZbtvZ|1pu{<}kST_D?|l{Mgv_qk?}g5IGrA_{jQnal0VF0gI-}I0X5t3dK&h5{H#*sJhbl<)$T)l%J6V( zh_a42Vo`sTQ)n4!LTbg<>I)bJB=7W^oM`VEnj3~9qtBrli*4jm2mxohZvDPb7BJNp zl0E5eHpq7p1&aFTqQ1T<)>=((La%`Es$|fk+76rb?x$l64US230@G<)hvfLl9%)=F zJ8W7jE3ADO#(B(>?9fvLwtkmMO}$ForDL>6FhDrPEWzYC4@W-Plcwm10)j#KvYBA$ z8KWL2X@Z~1v`HYyOC@3My8UbjdBVr6rWcqpI5vk=wt_W8E+~Te*P*6&)0cT1e`eXh zsZ&eRej$}84)-Zj_}P)xvvuZF+o|bD{ar67RzZ8;WjwTnn_uZDR?Dm3qZ1NR?d^!f z9@r((mIAj0u;9xUj7q#+{lj`V_oYo<605|u15pIBmUhF^Y3S+SanGrm>>$nufxfse zy?1Zy*t2b_DogrnIE1kt1aHPSRoh}oG*SlZrkn1_bjk2Rkj0gfw(QqxEz8Wf6A1>^ zga|?pnx-iXuFnAi3ItagxZ|mi2z~hcqwc?=(PlDDjf|Un7@HF}p$6_LBZcVYLmz)8 zT=+fFQVk*{W~AjBz4cL)H*5=7m~b3`rKar5W~hI6H(WB22mlQhmJ#m)2~&@ zF*(25t*FZ>pf3}NdxXK{5aP_@t`-^|e?WoKdNre_;j1X~eFCs52lVD~TNB9E@2no! z^6Lt$A?cp;5dJ-=S9X8rIoO*5g3eJa;ctl?F0gnT%1!jm=F~p8(+huhuEZ<{MS;Ot zm8Au>8B(r)J&&`%ok97bXkqsGYxeVC)(Z?Q@Y<^{2!gR?+ZM zEPXIWmEw;1>3Al?8Dh;hN=29f4d8G+{aV|9mqLUYjZhM>)Q2=XqFAdcsuzxsic{k! zsw9As9u^o?a6{F8Z=~`5AOMRM9V$=8no&2KndShEWl=(X5#Bx3j3;u+#x)^3A52W-{j8TR82=8{uW_NN<+6?k_Lkku01^bv_j9qFD@ZFH*D#1O2Z%1A)!{ zKPSfax!RzX-BGY|dK}GMtzavTOh=_r7RayrFjigPMoM(h-3;!tSp+w??GVd#H=E|U zedq8x#R9uC^Uolxt<}nScfROS9OkI}P!EzARmTZdaqgKx1rw#pfT=Iq&6rtc5doKC zmz_2k%R9X3i_DfLpGD0J)r}IyL6f)VDIkEEpJhB@eod8Uqt^b}@4cHMpM#*Pf|0fY z4+|?GXjC!0%lzSEbU14$5U*97l=CdC7 zB_lcn#l@r=d&jZ889xr0#q{v4UN+TVj=a4tw6K4Dh$^x+eNXdHObjozYAXq&P7YyX zAww35Mu}7YC6Qq1?2GlM{{_&wcH_0hxmHn>O_is zwuhNBlIa^3Z3R^zrKg9%2%#ptacv9cSRw^Z3c$6aRl!gzz;H6Ot*Ta4QuHiC({%+EvHLW|oD`z#H1)^7z{DFcbLlq?^E%!f6ajL`Pb$ z&a*w_uC#PU>o23aW2PNLPQDO66Ll7vZN>`;rx&rjg{Ry>V70G$UoJ?~b;#_5=S!tc z?(wIEfQ9Qf&9cgu-8hwN=sF?#&8YnlAR55Qsb4jKpxjnSuyeZAj43QE zu%9i=6>-DbPei`Dw1Byw0`cA~WNmB!ew2hBII}(TE3K)T8A-ist6T{O<8)(G#GA_k zt+e$X%Is9m7K&AR?odDkTR(AtWLviPFFT){#I^4=g;!(|`(5U_LwsMy{Az2E=)z*} ztI?n5^YMCp(0VWu*TDnNa38fj0x%G&i2MIAOBhVL53@9Gj5u-ZQKe|?S^u73^GQUSQV~^`&&#zwhZNtmrkUjO?28_cgvMOxEA6Y`HvT z7Ps}}Gi!Np)|H4|2P3IuDvJe?U};B*eC*J62U4hs$yy8CfffP1byu=ObR){mNbitU ziX_q0JSTh?W{00-6J+E!20c;xm`Dc~6*KI#I5ss(ZjPP8h?4pP_6ty@v5;w_k(P~H zHn%@(QwCEzJvV3n+EtQI(2l|-11|GUM-wL?%rr@45{f|Icttv7=~7%Db+fUyY|a%e zCN8GWxlN~Mf(G%uXBj}=W2;5?R)vB2!hzr8OB?KP;;rEHeI`%+aNKINQu-%t&_I&~ z`%_9VFh&(TAcA3q`R3YJOXC=DE&S=GwGlO76 zC^VlpY4jv~Vb8`_*!;wE47hq&>3K=_?niM+>j$z<(?_8KROXt72gM=~v@z{KUm(Y3 ziTRufzr+WXXP67@SLPUh- z7=eQ&ub&fIW`NY5s*YC7x>+WFb_qhbsnp?Mmc?)sFTPMd8%jqc0KrEY$Aw~xT8PdO z&U7KCg~2&1Zp|)UWP>j+Bh=4_U^G9CfW8@mTt6>~6#J-*NC2jH*#*a32hwc^hz_Yr!pG&Aia~vou>hXV;oRbWNeK=E`27dGQQO1{L<58 zel_339N#87Wnz$`S3p{lv#6b_Xkav24N7cQEmy<(5iWgNpD@!cWdvUWypt!2fr^-O ztinQz{t7P|<~+K%1db7@AbzZ<{+1=U;yZM-&quh%f`;Qs z$J^fR(B`PwfE?SzUGbKo^LsDvg^t3w2dE&mC=Rp) zioy!Vu(2Mio{z()j&mZM`h^qx+5iBLTb52dq@lnHggH!V{r(=VhClX-RDAfWMQ;t`^CBMoK z=3;r5*?tuop=iw!3oU{DykzFj{Dym7(=o~N;&WRL%tucRustY9gvgA`)R9eRLwL* zB!iy!Y@=Z5;Z#N97b7I?v1NzAMEsi9x2mTohIO+rRA@3TEgo=A4O|PI3no$oZHXT3 znkM~KDi!t%-<`;GN8CiS{v z>c;=d;t-T4WQa*j#rI2KFl7;luld@8U%*<_SW5FJ%imZ)7_G2YPn%dGHDuke z@sE>xs!3D3q)62WjV(Tz#`&5YnQ4bBn%Oi16ZU2jVayRO9!+BnPF)&^EKnoMR?xuO z@CLC5p0zlI>DpBknvc{U=;z%29Zn&v$crpf2ou5FTr+lSCb1w(z`zd(*mQBr2r`iZ z#xFmjY{%_zPO82 z;$kn|!cd7K;hfPaNHYE{DeUuFr+LRHyS(tp)OxWKlC8Qu(#Av4Oo02TVAXV_X=6IU zFbZtVsJ~~QKvMn^nD;k}RU;tPu&Bz(pMxFtUA580cEZ)E{4sU%LI3ynoB%xq1CcDQ zgk#l#1(cJQ*qugM0Zb1ea46GOq^``0lOAeX3?g;$a=``g7T(&E z!Y>|C7$xs!ve#j!{kKCUVu#u&Ftu?~u(H5f@6yz&DGlwhE&44oTW}>apDm{$Gyl7X zJ{#id8L?OGODrxu#f_#gct>PwPGcgM$dIIAIfn>aK8bxFt8cl|v_`(H*NLD9EItCh zYPvHnBy9e_{y%vh``C@?%5*88lKtK&iGY;{5jz&_=}*@9D4U=Bbp^?6s6M6^HFg1% z%+76iS$&7DGJ!2Aeq7l>548J~+2s%2mEhdPl9A$V`)q5`@zmrpKhSGe@aWr8Pm0W> zWPSWfTrhV-9bHW=|0Z?h?!Ri3dTihOZam^7`=r&^aOOaeWd;r7GpbEmYOxc~xSouJ-+UgP$%Me3*w=|bf84-%9-AMksV9hn6>g#3ZGEe&QL?RB#mta=7}cqjmhPY5}|1n;Cu$8{X^ z0+(`)SkM-c@|zGRJM;fc~J8Kw}0(DTI6~Raakai6$16McKI&Z-d-iTo)N5=}H-1hC@{Byp} z3U!2bHK%*{nQ4pnAG|KVnEaCXg~B2ltlI?hTz(z_Bm|jcsTJ666&(t+=sG1f*ZAcr zj?#q#SMq|}oS=VFa;HvohiN<@i)l$fPxEsN`O~VhNA^pB) z09z;T{miL`Pr-Rlrr?K|u&&9}Gd$}UG|-}?O^#CYa^SJ#I5&K_Hs+c~EFF9s(ET!T zdLY0rNYK@E)Msde=qS*_`LU6t+cT1|9P2%?lzd)4TsA0CIqjK)T<%xLN31wugDOyDECT;V-!9*QU>dF03D+ z6wq0ZdZLGbxj)A69JtF?rLb9GKy9*8K%@B_HQT6E+pn@@Vs4KobYJoKXmZ#&SfFcN ziwxS@u{*X9Z$plGt(z=4fG#6VdryAV{C!n8z!%W9Bd$pupRrybK4n(ha~}piy-Yq| z=*O)(eq$DGH?GGN50M?G?Rw$l@MJL0s1H<{Ud$}>AoMM1^tpu;6-dG36 z|D$pVND{ZUJTlr_#s$!giMVMDxYZN>u368Jsja|L-T%sSbTO)h8MrE&Zd;HPtNM!? zHe>@x0y|pg;YJca@%*U9x=`pAdb9db(7at*qJk!IY!rM(4>(;0fIkU1`6o%vIMv=d zoAUdUg21`2;7UR&&%(jIZmQ!2{jB7z91_*;5}6w4P$frw8tZXNasizddYTHlDWV39*?-9Z^f__k~nB> z+J1hqf{;Gv38_x5mxlMljfh=fdGh~I51p2cAKmL;_350A5GR_h1 z7HX~(-|^oW-!Nn`#4dL{p6W5|-fdMmUeYBIY&JV%)(GLPggMe$^$RH!>mxM+39(Zs zOM;7`HwF*kyl^_DL?oDf=iyKUeTaDSU@>FiT97=7p%rK3LlS*3pdZtiQ8hL$7K+$M zsJxyiIB@b3F5A?FlN+7A1=vhj^5WKw?_$u}Nm~L-bF^}GGt+xyD~<$jA>Wli$F6&= zYjbF_ioP$5>>HHDy-7LDfxvAC&23wL@QRxaxfwEV9Q6uYyK}Q$cX7Uu?1};U>&}3Y zGG(RXz!9O*uAhrc-#g@jS$RP*;bkq|Q0JF_@kC9kxQC7+nomIb9I}hBZw4AP5Hdh% zuX$HeTwQiv&kwQ$&zRnVo+Oaj5zo&H1iySlD8Qh05tU3k?1$@&&+)dIYacqIhecF1 z_2qwjCHS|ye_8FO2g6dr^FC=%bvCfzMlop&3BLJ_1HW))-w=O zZCf6UvV6W;B2i5So# zgEFXkd~C)FC%o)M#mx_mOw$=J4{zh9Ez8h3(myVuNEz6l9tu*`DjU7&DS52d*J8*GU|g76cTPjYiqP_wCrhMR-ZH`}3m5rdQAN;6;o5OC4BY zdTf-M#8w>zlYW?3z+t?%hv>N)zVuK~2FojKM`pB$-$-2c zHp1IGR!VUppe56Z)vk-e{ja1xF%d)A_pljdb9ZVCV^3aiv326eF?C^mqF}%O`0*%R zO7051%E5q>Me-L5%zMh%KVEo~ZQoF9Z);v&_Hh=wacVo#xg7O86)=zFPvObZE9f1TxjFd$L|1BNa z;u29%7?HN4jY+cAS=g9O2O-oC{TH-`@D#kt7E|+~%Pw-rDUP^L-^45`Dj_#Upcq{N=JVI_SZ=JiI6o2=|*TybhLS*4G+VfJ+M%<7U2k8 zc=ys2C-E^j6zMNZ$sV9uttb#)fa8ma122T5;+)uAiBX{?_<7_WVC@x$@EXxJb%RrG zhKKjshEA%1>w^_#dQShKqxbbEwH}JOa@cdR#s?X>x=6FPH^OIN8~INe5FOwckdYx8 z>+g8{5Lsi%dimfRm~>W`9zD%u=}Xr{^42sJc+q!eqBVOjoj?NO_BBp5cE#{=(UAB- zM4SUOzbM1RfnI$qb{X5c2BP2TQ#3IK3i=mTx!w98Dasl-u-s<{$vY}V26=!t&ZzBO2W7mnnzi#D#Gr@mMs#`tZ!4>AT@As7J?TCG2Op#Q(2!jy?PTMI3JHK}FoVQ|&eJN(D3vkr!>(MgVVOS5F{ttk|&&>t8zuy1J#UAdk^sxX`XwDGjabxT~c6M zxvR|#PkPb3ae09eE0Hh2@d6|KbP|S|RGZUfZyIe_Ozex8s{%vqhpzVSxfXa#NziBm zQM}GBfTQ_}mtpOZ&sxstum|TUA4f}^z?kMcRIMm4@>zW+h#JhHTHc}-e)Vn`baSsL zd$KjK6AC5Z7Vv^1azQwlH`cLy03@om6!Nd5E!0*(R`By!wcj0e3}g98Hhe{(biBj? zMkM8E{On+>K}NoTXQDB2xgVz085P-*K6kYZWV8s!NN!0wZ2jbeh_O9X;mPx7FVpF9 zSg0AFF5ZNt@H?a2Y;EKAm!My8m_5-)63$5oyGK#dAq|54k&zN(MEHCZ%h=3MHTo!e znTF1m+oNA0+Vhn=x7u|BSQFIXpgo(cQL{IFD1b@LfacGa?SI-CC&&;w^6GQY_FxbGI z-BqC+thM=Qj6LA6&w6_J>yA_6$%-7L_Tm$7$(*+U+f2%SD~Bg8KI+wFK)$t|{T5>~ z;4TDGuF=%2Pz+PmpTTgB`PVLpb`IK*EIfNI%2}pusavk-V-c8)ngKegWO4{I#&71) z7U7t++_z{l8pPBedSCn#iwQz?@m;;EM0_hfVzpHm* zD3~0Br`;SGP%S8|T^g6Zz$HG8{iAc9;c8o!V`lj8b6djQNcF&(gPO@56pWLF!XxzX z96)g5n!i@hL4QkRM+toc<&L9q2Tv=o!Mok{(2599hf~Xv^SF^K4(x@W@y90_huJm* zf;ksgqDWcJJ0wGQ{vv5k*C25b(D7COvQrzero4dYY}Fmt3DsC-ZA zopPysP;Oke_%lHPQEbw-|Ee!wzGrVz?P(h;sDTm#lYhm7!#1RX|9bF(D$y_pn+PK8 zgI*#dJo00FK10h?8L|yiW)^z6o9}Qpeq?6>3x%TwPuTNW4|0pUaisoU^Zi@8RaOyu znuU}8rSbJ$tRXPSNGfYkWvq*))0p}ljLMXso-mnf-Z_j}Q;oNybf?vT5;}|`SMcu0lYX9Eg zjIkcmMa;81xa)h$D&w$AJo;n^f_0;D`m?BaRl;!}$y!{Z^=`az$rWsyj|P|`ifz;2 zd+5?Yhm!?l8pa?fI6VyIj%$Qzi(%=D36QTUPMDl!tpg=ab#@ly$VI$jt{^4_)55i^ z*cmBRgfN5Wv$hX|uA_}=)m!+hN2E_wnLX=hWE2JORYx!pqM_9CEp=G$+^3CmG_}O+ zHJ!$amijAPNy82^$aGvG$5`>aDFw!VdAgOyj$JkA=Bl-JSvZts#pRo-ZV4`drAXku z*4wQH$%7gM>&qA@&SLNn*Q`{W_I@8fA8=84rDg+z@_>+dFnbh(pvI-^A(a(9SI4UD z{Xkd`Z+t9B%#49nsN6k1PxydfNY1ip=YU+RWLBsCJCCzefnu0Du+d<96YfM}LYh(t z7z&WYvNxM^&3-P^ORJ`kQ+^uS1mRpeY`N3e%KSv zJv!Dz(ATl6%qNd96jm+~=<=>I0dyqG5_wKRC6}gutbv;Xnx^=N0~| zn~K4*Fglc^em_Q0(hMBA=+t#1A$<%B$d><2DuEZn;-IU?1*h_zx6bkgGLem#XHJ6K z*dGyP=v(R>_=sl$FI1w0G3ATq7ar>ioU%jxnK=OMEid~nBOR3~)V@DKhT+lCzd) zq}sIQ7uORBYaG|%gnAK*X*F%1;k4O8Xk5s6>WL`=%|^ePOwiA7&<)(>lzn6;<#m5J zIuS(AbQ?p_%*V)e^!-r)TAw#3K(m1kdZ;UTyY=YZJJpt*p&Ny|k#2lb>DB)xf5FgB z2;3-RTq}$9@LqYSjFu8{1T33%U~WQkNZk7cP+pG}EwEm(1QRI8W&y6%DZvUYUED22 z=v$zAX_VU*EFaNX*RZ3Kvfz|v@I(`81VulW06lEKF}MtgWN*V4M&0`35!8}+`=o?7 z74VnE6Lf$N?jtkxsJt8&%EZ>#$=T7wzy|h*vNN=VlQ z@WW&M#Y)G-&dSD2!2XMgj){Yrl~IRY$j;W;#Maq~fP?L)iXy#`qn-W#w1JqTos0cX z*FsA4!X~a3Mkb14g7ixC%8mxMPWA?lCbmW%^vWLgCiMTT$jeJFV`6M!@IUQR{vXYp z37FYf{y7Cj6DKPnFyE|{!KFfknI2Bvi^|2{zY;S zFfnrcgZvp>-qFrT$;4TUUS3#~UfIOmnO?%iz|2HQ=ci23&e_1(gn;2+)0O{W^YZ?O z5dR^Ke+yC6!rIxykzUmLr?#+(k)5##y|js~nX@?o<1Y>dUfzF;AFA!K7dd;Y#ZK(} zbTE7b<&~hh#0rN>!#=B(BGF%invvUj$iA-d_e;EwG^st&DMc(X6}{HoBz7>Y2a1$h z&KEs#dP^|il@7wG{t@F7Sf$t)RNq5gso{DZ1Gn|b{-Ub+FS|Ge6s;#Gx4(>Bb#Bp_ zNZ8`pR}evd&zz*{uQh+Eg2ZS&u7p#+;N;|(yVXM|`5Hqd)Zcj0s4ftR3BlRNMrPfAnl5^|BDA<*DVtV#V6nH;H;Imk@be^^fPPU~h8hp0RE8IQHoyuZE&v zT*dB6Y8(<5+l~`0hF7VSXu?hb2;e1VF|Z=}eTh4eQohNSv3oA}ZHS=%-u+!dd-p4^H9Ns>_#U zLQ-6`>h{`nLYgdm729nQ6+rET#KARrHX3qZ#?pK{@-5?u=BhQGoKp?TJj-Dg}*me!(r!Zfp*oh@iY_i5Hn@)ODk=gkXtOsNwA z(+(#SMX7meKb0V+|D^~UXjnOQa^_=O530YEE~z7EOEt;zM~K z`Vs4Sk@twqNtRq5F4v_7;6|GG{6;QjqSMimu!Juego=|7sI!A!=@);HspP3nV?r!< zJ)+%@GYHJO)a4-wE`(Z*e4Wp1-;I2Psdh(L&8FZ2x)`>+g#)18*66vf|m$KA2UD3#%kzOKYI&TlU z$=Xyb^oiqBwZ#%J$(8{Jbj^skoMY5Sj4EK87)N)XIbh}F>i&$7g<7j|%SBUF(qJ`02hK;VAYo0n%zjhx89`3%sK>Fgiq?%5$Oa#5{Yq=J|KrY;RgGsedjy< zW(e&+#EkO6R(94?&VhN13s!SA-);87b4cNjnmnsbDpRlifLQ866=$^>TRp@_Fsj}Z zN+AK+HS9Q=`~LYva%85v9M8>+WtF)ECwq3LZw}l6yYpF&@Tx}Va}hE_QZ&-3yw$V~ z?^7{$08>bRx)iBv1ke|r;CwniMAJng_=U#FE>@y7?U`4}+_9kL&x@7tpHwJn{FBn~oq{wnHv~1)vE@>a6Nq zh@+J<`1+|A+7m!+UVBw^BhRU7z=;>!HeOGPR$*Jc=<-oNHRwvz8y#65c5a-%iOj<# zzl?wU0<~wbS1rt8_hh$(_b<~%af;Vzh15wa1e9(t#rZeKeA&v|Y$@9GO2OLuLLW&m zxOIqFkjRp+nCmO!+h@^3+IIf~hxaH@TiK6txdS3*C}nw0@`X#5Z=Q-8TdMI&!tCyM z&KlJda5xyooh9dnz_?yw!ERI^VWP@POoV|~@&vFkg{hRE95OgMUd-rP`Nk?IZ4rJ^ zYqg@g)*_#I>J*)=gA4d+%Di^1;7Mbm`t62z*~gXTc6eIr`lkIsX}s z`UkT3Hv|x{wYB@fNQ@l+#1;Qru;D8+ z{iCn{C9Goq#qwWK)dDW9@c6p%C*q+t@R23Y*J$Mg@s z1-ZUAzIgdPXO-D&@a1Mi?@o@2|Cn&`ZW3S#E9EYwnE72bQAqX#D66CJ)$@e$1qv28 z=&AULdL{<57Il26qZkodZ?CtGyHLAs8{nTlG(k*YP>M&54+v@xmF~Itzc71yPTkR_ z#CS)lFTv8D~_O*k6|JQz54F^GE$Lc z*bBXzje4Vy!$tbui23gy(FqeuZK~?}J6zhi6Bdf=8LAhYuZ0a1(al+OV$hD(c9mKT zQR3fMvcprXHx#a@wiZr4F>b%rl~}mkD;7tuK7xW(;}X(%o!g7sz$?+roQg(&aU!WJ z2t^jc+wvs^5Pxm)v6EnYsn>N3Haofv)V6}*l=aj`T1eGB^<@&{x+vZQke3aKkXfLJ z9)@KoSVf}#1&Hz&6;b)o##~V7z)oDE|~sB>Q+CHn_?j{Em?kd z!!;}sd~rNLBq*ta=6mvpX#Qe)q$#*g=>vG&wK=BlF3djNM64|HEI>6Z_IRZ;z!73q za%n3hq%ya>Y8cw(Q6zE`DuURQKx*)*Dbb!0U6ktH=?4qxLv_lCI#D&c^! z6PpyL5>7#^vpC_~j>C@d;thboVz|vB;vT~Qco)^4-~-uOXn-mg4vI6TY}4peUG!T1 zhd;-FEO7YQRtrXIU4);-L0Q*Ds(89MbT0W2)vaP)C$dB&+JK-Ff9(r#n}i;PXl$Wp zu4nl&=MsKwL~t%)I*A!~QAlh6mWh_;rTH1`($>T%2&ufd?BQr50LhV~O)wv@gO?gw zVY!wfGR9Xprloz3N-N<>9Isp~%8+mUe(RNt{6EuK|1g36$LmYf!qLf@fRX7x>=!mh zdRYRde;Gh>hL%dsHve_u@#Ey=JO47&l7bV%7MAv>przO7eDSfud}~6pCW1at=I*w#Z_0m2s+YX>;R?U*hX1vI z%RJl@C6Rwljc28?7=|rlb{cWHLR1AILtUyf?3xs z6>-{4hWVmDo!L&A`ANVa=fpXOzve%=|6l*GbN@S?{ZF3a|BlZ7#rV_9{|~0{{}rA6 zPig#*->H9_!Yu5J|Bcc{3Kh@2MR7&*U%_g2y%7cc=0OzR*tP})b<6`7BASdgDZ2?n z`h!)!9O@iNcJ(?RWjqP%C2v&I&9LDpG%fh$%Wo`L{2EO=x%1(#3RFdfu_{I9AytwLZ^i7;0!!(B=A2zM7{l$ESa2JLs{*oqnSER>##Kiz&8^6^)7)e-n#(AX-65 ztZ1nF8zaDkF2$!Sq~v6@7tR^WwO}PIQ)x8LkWlxS$Qe_G!B$aU-BR)M@Q`!wqiMR? z2sGbrg-G>;4*O&NO@}AF9bl$; z{7ADB++L!WDCEM$wR>ELDYJY?={r+YAeN!_0TP20DMR{2cOn!K zg;1jEmy}$8-sFHS<|BH%u1y;DIwqZ%fD<{BwRots?mcRK|A0N)*xfU^j*@ z<2U-Bq_%IY^+EVmD0chW{iLiia5ntic2owLH|;T^N!+uP*Tj%VsR|zVDON%Eavf&g z9tp_dkH>z~C7}f+SyfxMC`shp3n(mMPBjjJ^9iU*Hr&OEi z1u3-gB7F8;;rEGy8u?*1*$L16oW9<*(ysRXfK{%JHKxj+ViHwt<`#NwG>e7Sy}^Bx z^vVHB6UQ}WjyLzlsDWm z)&ju({J0P0XyQc&Qw<8TaDh*Cz6VAEW7uizC=p;bC1pQxWhk)m{I~0H3!QW~7iW;X z(Oh=M`0YpFQ~39g7$ya1xQ>ZZ{oXtsp)UrA&&PA{ALokk*CE1f=&VDD(fe2;-s?f` zZ)J1htEd;(hzgBh^s_i%F~eAsL;S|tteRP7RqD@n(tXG9t<2tqv=GLUZjeZbTZwJg zsq6Yt9%}0Lt(iyZ-tv-kHkq%l<_$)4*7$Y~-&YUF3fnOai%GG!S#h6(qxT~?IQ*^M9zj{JPJri zYXi)6-m)LJrjv=N)Jf&m05##oqy1I8Qr0frabq@njRyP(U!|&(eT# zq#~)!>yoQnSugEriLO%uKB!XK?^mKSH_(>LGLZ65EeZo27pX7N=nGr14rCCCJMzb$ zFqgXp_jBpm>7pMUyuh;2IWi!VGD3z;HQOD+MVWq0m;Hm>-CGXQ6rVi>J#Q7Xz*{oo zjaA*|R_ux(Ya~o0o{!2yrfBbW1(-X&p|9+WbYvw!RkuzNA1Sw%a(9ocoMt@Jb&_-h zRyVc@c8`nxr58^#&O$2%t<7p!Y8Uc|yIzdQ<*D$2_6C_fh*plxO244?I^g#`mm}rO z8m2Yw7PK|-UJ6EoshR<1p6_3|PWY%UbkG>2Hv2*OF1%2$C^RsJO{E&W0Ly|}736X0 zOCAlnKZ1?8cSoXKe6Wi6;B#Z1-UgWQDj1XrFGX^NA<4?C^204bfx0sKnnIHo+-UZl zCWs(E^n|dD?k5%Ea#dl?1QGq9YCXHlYEs7%%ymLO#;QKCrPmB5vo-hC0gZHVE|$HD zhM}6#Y&phbsj4OS`_WdM9l~ZK<7YuGxpz!NPFBuwR3#Fx)t?ILt!h|kSWg`}-6~LV zK(jHFx1>ILA+l~IoIG9b*auZElG{@(uA|xn#swi&cKTsPywyb2^mFHxh-EcOuSlu$ zSHF54_<_8mI8eCv*556`8C0TxWk?1A1Umx-i*mz(hnYFI4Ta9Dej-DFgl+<8FSh&y zwO?0`tMz8g+d11ECr%LSR)P$yJO#RMuS5sevT}SD`BrK&SGbj|d{qJp!Y%s!6PM9~ z;2YRv7@m}q0iM{Zcc`8(54BDhcrE{?#v@PD|p0t&h7mgtlIzB$XMCZR&+xz#w%cNg}qoX%9vko%qO zo!^qj0Kq#EEM5552t2pD>524Ru;6rB?${>yER;ZwSKK0*ich;Pkpv54^EswvZ2A0? zt=C>rq{00X@Z#w=?`P?u2nBc2luFUNz7`T`xzO?vfFxABU|ubSvvL20uyY6!1Za?T zThq2}+qP}nwr$(CZQHipJ#Cvi8?i6m9{z|u)vfAMQOL;5uZvhwFSXoM%Wayy#VS~2 znN%p-P<#u5$t?)6`yivXu8(Yi4C75|b&oBKD|v>^6|3PVe31#wjl#*@*Rjf^2YS$l zV?!k){L5d0?W#wbaV|kGGm}NYfePbWi54G!5))K`e=e7v6Yvc(t1X{c71#lb36k{s zosJ>e`Vw{~aHsDbzAT6Laxa>a@z&FsZivT7=ccyjN~Iu~7L6W80S^dgi&5GnCSvOP zkknF;Q_!n&dMYZ!SEmLe*dACRQ+&GY6*Q z7jUdT2P58$4^w~P#g#>M{C<@aKQ_F0)Y&6*50{^>#q2@CRavu!lIUF&T-rzVS0_lH z-%E4^uwXLVNUEbnjy0%lb0B!2oDp&No*1f?X)2!HU!$}|RE*$Ytc!t8D@Goyj5Ez~ z$@WyUewO zNPY=R+qd&T;8gvOVm^Uy#}0PS?mioIOo(wxWc{@)e!&B6S)2f0yL*Yh{OuC}zy@!H zgXUqRSzi$$77Sq%ZDW;oJ^+r(Ax-|um1)1>Ckz*hZ;RF-D?!N;7>GtJGDi1%c`6oS zxB)pPj}Gw_DdGp3cGg(_&&CD}Gc`l>oL{ zOv~3?uOjhxhG2Jvh~2Wx1@3I73=#+EP?Z{4bm~T*5nzdg}n!5mn&c1e32HT+C z?}xB;;%OU&q^>sbAx-r-%%)>9How}7{n=s)34#k}@%Al0MX6#_LfYtAC=nU=rm z;`t{WX@(KP$C*pzvFerxia%*as-xD7Or!)O!z66i!grDWOW8|yGL?~(zPHood=a`e zIz8cS)$Na=8qRzWD~Q1&lBQs6r}|D7M=^$&F!7aese`ZbV2@!y144G}fa(3Y-Y>r3 zYP~Wd9*Kgg|a&B%Io62%yB~xm@`EE*wf}OFbJuQEcno zPH+^J(6Rwac#LN4NP<#LZhdwo^!wD1lsLf4Z@X%=Ud>Ibd7~?E=ls7@_OttCP|2~5 zeKU{f8-Yj3eZy)b77Cl#tY?jn4n=NBDeQ_r)$CR_IjQu5GhUj=O%+f^Er&D?CwR(E z!cEwB?l+{}L7E7MW=MB%K+9do>UJHi(*?l@VQTxEeNN{Tzqw>YV8mV^aD-)Y=)Va4fG@~iU= z=s(+V601C7u4S?%Q|!bMGj7@3rH+UXlI~65py_hKgMiUTuE_MpmPN|UR_MP1mr5bT zf&RhV->49%zTqeZTgj=|V5o`<*ulsH(qzOzyFo<{D}>>R#)-R~5SDptYsn}CO)70i zwGM|#pD;V`iX(GzT=A`W9ob?GqwdfRKJ9rAVLP2(lxTmv-h5_AzRWT;nuNeDsdEzu za~>ellQvV2?&H2q0}n{&Vu#PI-CT#?R3!pmil$04uv*%o zug!9T7rH?rnMm_ip<|9$(m9V-A0tLcw95lS46Q*N*svt?}21qr|3j3|a-r(S!{!E*~X(_R0 zO$ip_#SC995 z9ln0gXW0DLz9#5EQnZAZ<4*fz43B(yQ)AV3<|y8b@Bj=lc37hhs(UB0Q}Po(K?=7Z zGI4!0>Oq!sc!q_cd1MV~AE7SI{%Sr-X}2z57qZ|u5RavyrQLbx@e6{0$SU>$Rs*|S zj^GzxRGZU-&Bgh7EuNc(1ZV=?s3+@3j|xlc5yEzAA-v~NVZ{}8K+rm(P&$R0LOvrv zSORP0j*Y>!NCR5Vw^Oy0MNLP{g3g&Jc0qO6mHK!qP?1yZcma6_opjYD@e)$)%ZAmw+5$|FM762d&7ydBLmjP| zQ^bLS6cS0`O#+(`042J&7zdl5+;(0M^mOd&i z!Wr))H=afk1AzwnJsP&B{trw_c^(r#hCD02FO#iMIoIYG1_tMG-X;wGj~LZfExP@y4w+$5ezc=!<`8D6%rH`4$7( z(?0H1_3YuQL+;OVydZmx;Vf1W2$1J9e^M)u9R>6vqJ@fkGGSP;&p#we;l zO=9dsFA@-&kMTdsSEVUGT77(t+z%x~oXYu6hpS|Pm_HIVou8Q@I{tmhPtJ+b z&#LzrmjsQKww$Untwi+YAiY?L0dLEc-_zx_#v1%?dC@x5z-CT}p8D|wfwfFYF7J(YY%^3n zs-?bPM5m+FIP2qM=M0XW8)q3_^5bq=$p)%AI}4Q)k}dAR0&g4XXIp5X&22>-J$o6j zHn3~PXag`zY0wGuXv>Z)A%U^&M9Q5bi|{Vqw3u8!sdCSUTC$ehD;UJsDbA8^u$J>v z)S6GF{X98sO$L#l=3qffGIcznuS49gtaEL2mztvn#?D#I(Lwa}_~CV}NQS1Uf5ARI znR_^#D~Yi)>oEckUy=fWKR~55O9Wmsd;DPp*{+mj6O4Z9KdfVebvK`va~cZZNGTc~ z*Qx$T!WfFqP?6D3DGy|t2C@O2%gkyV@pgBJQy!7mwgvk=!O658^9y}iz* zSIAlT2YvfS8yT{Lg9aoLP|DuvPUUeVaZJ4Ae4ZV#1IyVef9P%;i>%?fO2@vKmam!& z4YxUxA~JvsRD>{m`cRHo1RjN3@uKBR>($-L?}w}f&fGd1{+x9lKT~Y_X(&!$&|Om8 z;8-o#HBfBb)An^&z8dd>Fe3ZccPX)Ckc@dxcGP5@vEiBibm7zbCidY#YA^(eLN8Z0 zmY(VM>MFOLpTG9_f?v+Jh1Lkn$OTGUg6A6l(UL*oQXE19JN*t=oxZ*!-X{acX(;^_kagb25(R+57MFxi-zgGDN_1Es_-M{krr{`&1K(!DOZMiKO|G$)2& zN42B*zL2c@7@nLj9U(+V9va)G|j~*(*JPz`RY-^uxN(cqTY%!!h60;}uc^ z12WT4cou=QK1?&L>Dx?V`RC-vlL|-Pd^1px}cj+!j9nu*;SxZ6^ozUfT2Yij%tKWD7ASrp8w-a+aNy1Qq;0e=%oov zcdp^oto=(q#3skQ{T*AqFeNp<@1R(Z`93z<$i`oX2#{) zIS2-E27GF10$G*3)QjIw-DN6t#SF6dLnW0^;py zm$7oOLW$pc-UlV@aYoI_y6C5IgDqUPqw3BVwk69y7*evusiV1@j!x4xq7@maHOHOA zZS@5sPiH`1lbgv9!7>Hw@0p8xRrdx$1X!OL^Hjvb%y1N{NsDGWKZ*&B`ekZsv%q;} zcZV%7?A&*m@-B?77p9QpV4LV#T1(L-ETc*Ils07qSmbMvk6nUP%I}@U%+S1qETy9V z;`9G519&R6r88 z2J9rzfbBCeuwWs9>+2f2z*LeQLoybzp6*8EZW)6Wr(?(>+GLuyck27y8qN5p))!0% z?s50CExrx6bWZc6i_$FQ{^-1D`vN@GaHmjImb?aM9P!8dERfTq{FAORD2i|tU;2+k zOvH6ROP=?g&yss``whD++D|FHyEnEOy}YB7gPTK`%XWK=;M-CIv<^x=33C%&){s<6 zkLkVGSQcz+#~j?;dQdUGD(97K0Nv*N4O-}w48G~4h!vFdj0J(PGIC_o>OHSR60Uf|q4VuYr@AH69v96w-9LrmWEv*6nxOu!q zw!z)$P|j@5pACxP*mN(cqVj=cVXEYF#1(44XGbQ?4h2bPi&9l@(#E$vGL-iRwo^LZ z7@6OE$?+()M@})JM0CH7kZXGaN zH|W1{LKDKQ2ZV$WNXhY2k#}(0Rc7k6wa@ky+QDp3ssMVVw1=rE$PP2sa}Sr!DBvWR zEUqUPajBj+10!?yCt}*gmv6np-0vv47=>I&*WW zZU_DqDXAGt8(%1H7NKi__~bLVl#Z13+EvA4I#k_Vww52uW7tZeZlR;LJD#DZDqdBc z$}3eXQ-{p$&}W?E9Txg?{+s~?11ZGOt$JK2&yd~i{!~({r;=+8cq-|!M>y*I!tu<< z@+BV%x(ue7-R$jUww4CH8KKnkgkA{3)uF;=)l>yy{xllsl7qK2@ni}V(W&-)lhoA) zaifB_C9(*_j)EPt+ZHM5qSwLGu%7=8gVz{GtsBlSi}dVr;5(gZPYcZ;T$adVc6iRZ zkJj6meVEpWM)_^=7H{r~L->Z(>fiM($N@gxY_!=7+PxrA-`vZ$8wL`UvsScr+;Tqo ztMWBy+9t`G!t+Z|`=o6=?xo4~PF*;*p131{_rYlS56IZmd9X(0CQaYVX*E&OxvJ3$ z%r!`Eiksqw=qtd9C*-hjH$F1^pJ{<&QGWXiX-0Ftl+8%{2uC)e{bxGKGAAOls>xAn|P| zo=EQ*L1SEK^48q3mKB{=m!0fL>2~_!s3&}Qc#3EKz9JfUQ;Tk|6#d?!Hmasv?XP|2oSVisj zaa{Q}iv+Kt4gZoj0h?E4T)K16+En;!e>$kdVy0AdEEX|odNtojpzoMpQ^R=in z@aBEznC^)B^{a$gj$NOx>U=HIpH9#kC1(Jb{!3VAL?TgOKRDMm;4JOu+2|BlJJT`N z?9vl-9NzT}DEd$o5TR-UkJdgz4VA0(QHNyPNsJOCAuXseigKi)o@m7>r}J9-RrqxC z_$wqT}B&li__@qZ*3;-kXrn(5IS& zmJpIqYiksXqno$-s0m*%jB?y_(JEIxn6+|%y+WquJ*IjJu$_H;E|IAFE3r(gDH=Y`h%-L%#Iq`$HiW(>y*LN$nBcap>|m7}Rh@0MnVXD9g0 z)s@aVlzv9d!QEY3$kp#607~H{4+E~B;kl3>;^L)ugvfDWfYhp;OkBani-*dUTN9s; zy}KaJYbB@%@ffsM&vV+F3k+N?AiRl#&kAr;G0*?GN=+FOQok;+7vDRJ3^Jm)ZPUs> znohl06Fe$9HjT%cD9OZMddxr-I(zi$4o`@<>s4ooP(}T9NMfTlGEez@{Y;-5^k8!1 zF|p#R?cdeLMA`4#zTq~iABELX(hX6rs(WMGtRH~l!_+xIwo;FjE9y|J z6Y!w@M0-uAI=0FkA;tFS%%1g1=A5E9i;!@(h-Q-YLHLKgc=^(;he~HhOS34&yh^G~ zWBlOQ@q7lhM8lVPlb7SYY|2#nF@b-Fw&+cwtSpQbW>bU*3te~4AbBn@?lRnsgGxlD zBmmGyTVkl_4AGH#%IqSob+0Z4+q-G9Dshl!K`matWaQ=<^aUINxy-q1{)vF6Cdu|; z5A$~2=w1)(*~?y;ca*{)^_$O6&vGlYUB>mV@yq=8G*kF6Kqk;0mK!vWi|g!C#KDkM zLI9qC;P8+r`nbO1LAjpVGTc!)$Qi5uK@PZg@4RXPK_@cyU+2{JT$)C@>}EH;oxkh1 zb!cKsUffXQ;~6A4bHSCV<4aMKo5qPzrBtI03t;-JS!9F8t;?<*$u7}ND~&Z7=*aN? z7|Q}}XPEu1p|mhGgx|ed%^Llh=SDSBFCrFhWy+9lNRqQ*lA>zX@+pV|Qy>y}($~bb zFvjM81RzXvv-=;OjP#kd25RdhK(yJ(?AKibyN9=tfN$P0XLII8j)GuzQi+vB3S4v~# zR350N4v@4t^?wZnrj@-i zvUm#7lSh|rZ4$9kfB_{Xu^fiNWaRwBsG#2evEdN{3Mlg_db}U(L>k*fz&gJ1#66-z z=dZrF>2BxSM*x!CPqQK+XDR;`I;>U=OOYL)no~AZ`2r|!TkZD*ko9dZUBl~UNwu%y zgI$@M7>Sx2nDI1w(lsd>BR-zDa=hK&^1&3@tu_7C3RO%2T5%DoICej3}yPy57CpQ1EE&}zDr zhyGz~p)!aY=?2y7L#ApOTJr=??(L=V8t;S z_3>M7_1ere3@i9rlE&rzhD?dOT`SfwGeoX6;7d0bi6cX34q3lE-u}~@&*mO@aHC1? zyoC<;`>_G zDB2@$NUiX#eKjJJ?vWvF8J#1J+i2G&-XzHNlHLy&m6!@e&^u1$L9w@NY!`8(+rIx)I3Q3B3|>Ev%RX8!j>IX=k2zbl5trN7PRN0}Auztt12 zG?>0qMsS&2aCYb}Zf2Nn5Ut>g6Vlh)u4!=cE_DkX0ki*TWFIG4Rm@1PIAydlxyXb{ zQY}F*p!n&s!Zxm^e|GWYaZ!PM@Ad- z&e3R20@o3i4&%hoR@9t4AIyA)D6HIlY#75u({NT^1;tNiTut1Z82Of^8bJ9hP1NvD z(1}LOdGSjfz4N27oUs$d`S|RHbueXEjbI{c3>wTm6X8CGh(|~`{^T}K^R)TJOp!ZP zoU``oNJ3jL+~Q&0EuilyxqRef5FMbe;GmLrVm5AiBPLmRO8xMERY8f73q|G=SQ0%L zA8JXUL8sL`B_@zjta&~QyYZj7yGw{!<=0|Brq-?UR=7|N;i#@ac?g+Lk!mgXw?ejM zr>gVv-+zv#y1n%U=+ujxQ**S)Aq)CMXUx6>bh|w5Lfy-x!SSG-4%tvysUK_>?Hcu%iFzt!yUfrVtgY8 zCC@K8?B_zq^kxrxr;8vV{ydvEM_baviLuD|S;$-?poK5~0w!rLT_;6`D&ws$#vxis zMuvp$ERkh9mdq;aLZIX!-XvD=QJ9ecj6NLtWi4^pFfvWt4jroo)YK;>it<&Rps9ju zkX>JLV|B!TasK5_ETuZcsXw!E!7l23^>kG!UVtCGoMK0KRc6^|6!LVFR;tVpa?v%R zC|23e+N@f7M>+rPdG+qV8RJfmFwK{m#v!B!5nC)i{Rd9NeAapAH_hT&JE+86GYhfK z`s?bX<@_cCZX^S+_2QiYo^DOsbmj)v`-_sbBOPsD-W2g5|6J+pK^u7`-<@bRk@I;A zYTP5pCBU~LaSZ~nsIFfIKeyh(VF=D!T!4!U4gOr@<*S~ptV9H7AGTFfbwNim#Y3Y)Y)Z< zVX6?ODn@^QQ39`gd@rk+M?dKsRIQj(;!H-D^Yy#cToGE`$gT>jfMSVog}RCvKi4Ql z5kG1vS{@5+ew5&DYUr)^PGroVr-h+})hbCj12^ItB4|BwclolGNNgGu$iX9VEAvi9 zCzz~oZv7J0#m;rbS5xvg!JRu7#t}dQcxKYves0jmFvUdV)d|$Jxy1>?tWl*|PLn{e zg+|{d>b^g0nZBspWU|0Xppt(O$ZLaf2Ia*M6$(i@;k{KG7~Fq#+Dx}%yz~{z9ozKG z#O0VAFKLzP)*X9x=k5UdDt)+xMlnWq*Wj-l;Sq@T{CKUrH zhhAgfSHN^7Vr+r~iuSBd`E>~R1c?tNV%4v#SQiYc$Q{;}_t(DpRe$FqV+}ibAF<03 z5|)cMw^rhJG#|CIKCAK@H|TJmwDViD$lp{(>6R*^hhr^QCc@|omVpF0>hYc6lvB323ud4?ufLG7{b!5Ce+rW+%91vaXL zznS`#-IlW3-jO%OQm}{X(0SCT{7t(6hI)i8O;<+%wTV{%*pF z)ofp+kg7oBU)z}K*_c{^LtU^@kpsB$yu^o1rJAARE7x}#V58}#lo=U~KYocBTJS_~ zGoj|#IG@Zlg)K%1 zD}fw!G(Qg95e=!J$v9<3TzC6bATk3;;0_QG^(<5qtn^5U zQF*z&_Go8J5g2|~#U+?5PdI@gMH^76El7#KmNb<7qmd-4OG!)v=DZIwaHnKXo>GD2 zTbjYQk)#zU2OJJ8Iy$NRxifSy2<<<|cx_wLWW{u%O|Z)GTq-k3!Jv@FLf0Y3aCkgP z&^%|s4Ssi-BcA|`T>n_}06&(SK$2sFUK2;~e3HJL)$;B6(YZ|o>YK8TP4);Z_Wa|w z!rXGp>B^(;7`glWTolaYBN$UHYY?>vk_BLw*FZPY?YN$3_+bC*4y_!hctutsBgt3G zAiw4_fe-NH-3H0gllaVA4Q?i6upgvd+q;?L@i(wy-%#Sm_&IY>!jn#T3dNo0rq@&U zMi~0hSjXXYQDRb4!j8T5uT*BOVzasFY;DqZp&4NVAxq;(YJjJecHi|&aX6v*rPJ&6 zi3z2wzJ8!A)`8AMe#N}r8+QcNep(;pI>vl@#wo3SN%}2gs9bLAKm4IchT|S)LNUyI z$plglaYo?Dn@zRiK)Gbj4i)3JAp~s}4+!uU93;lQ&PF?;D{YGI7N#9kmWN;Tm6I}1 zqIEfw^*h9x0h-X{DM|a1&UA+qW3o|~Y8&!9B~dPoDGDGl#A46)7?W0lV{;#2_!UpI z=Sh)FP*PdVNfFA8=>2c;B#q%#Xs}q+Fb6x(fi)r&YiV{%2Da`RP0-C=ME*r=8lA-Y zSnC7%B8`YgE9)V{Xb773BDXtmGbeN@80aL#9SMU3G?Q zn^#d-PoqLmwo5Kmphww^KO31)4<^7s4s1#~pfidWK$nB}p!vu&1LY3Pkr9CJBXP#9 zpU5lirF!~C^gG`t1}9V9pk{D0LGOvouEr?AK3LYorA(Rp>Q@)`M3d+$MiES?H_pxH zR26;lhSVd8JkFZ5+8&^dkrg7<_kYz zt8PU%Q~8D&+Pd#{!|RG5!AG7MK+?|TRuI~MmTh<^PO7hiR`rN=NIDR?WymbIE&p=O zGz|vd4)O9glLB0tMswF@>PBFzI$O2Bsg3GsGN#za_0|b!4eqQQfQuXsTMi*191HZj zlg_fAQJXMAWLQOf5fwv9`gb@Z#zTg`Ge5seolJv!y}`7X0k%eXJA6(qdHy?j=bN}z{6iMP~7FCTeV zj!#)WziXR>dnN-eiOK|SWu1Zgz(njA&wo7)c0A?#lHP$62wTtq3$StlTuK_PhA)To z(;L)Zy=i7aEe)WM#CC^dPp*G1nmm}62f1f2wNYL85P~T$ybG`ww81CqQ~$6xdYo$w zVt)0VLTMoJq{|fA+|5-DN403&*)#4Ov|;sD!ZyV0DZJi_m?O>zka8ERddi&4v>1Xu zupn?I-o7Am`NQEm_C>tr$}*tnXE7y68d^k#Yt7n-m6ed_#XrWWq@ugvz-FATnE%H3 z%;e^cim>>KA9Xrr7-)Acgx(l-R(DLL9cE*mvgud+bkcUjHR~u1J^P^&f9x>Xulgmhh1(1n5`si z@l0zUTkQMCc;hwdW}FI!{wXe#lB*ZtOZ)mZi%V0n{z5Pl5+fbO5Y4#R#QO)0bO+#g zb$#gh2N(o|`}OorQrScS{VN%yI77eRpzxJ_J?E#n`ym+oULZCAW!lSpf0lotNrn%Z z9?oH5(qXpRaXKQe^IuvFs6KPVe%|s}rCK!VESVZ0*BCFJrSGS`7E4d~DB1EMir8-0 zB-?(F{s+Dq(!b=Jx<%$^+m!GPVa%e*%d={;1a`-#Xy8{3>7@-OT8dVu#6ebY88X9` zV?nX7;+8ph{VpEX8U~dfa1hFYzj4UO%L1Ochg%-Kgo5x%YYU^WnowB50q>3mFegz+ z1-(hZXZ`pi1<38NU10NB*Ry0$piU=MfuIV_c7JNo* zZZM3g24iOc8UM$HpF)S|j+~bYPfz26L)BVtQ#N}6IBGL^)$ZXj5P1gDC3S(tpq6u$ z1*ApcYJed_9Cs3wtH?H3ddeC;iCha&);#3;ha@T>qT`J?+I5#mVQVm1(P)4Kv zT#ZuXR(LF1rB!!~idaLla*Y0*!T_`3@(%Qj{2)KgC|0iFoY79X|JD)&G2WvyfK*H9 z5i{SG<6fmycYWKB1W-tupPq)aQ9wU|Ap7ta>x)Qr{weG9R=-YZGD4GT-NgnO)s#LH z^j{;x=yH)}WTK6+WD4R?(~(kdxHC0w2%&k#Ztm<;DZ#UY1?lH+k0(Vmn_Ee}t-7_} zX-g<-F-wWa*v2sO5%ENMCEd{I*5$!pttJY?lbvewGm z$@jF!xArKR*W9s|^_h|9bj&9zl9z??GAoQcE&u60{QbM6tEB$SzZm8WO;9Xj<^pg7H(~-{N+78qn9vW?s zN)|I~(CqPjlcxZ?Jy1-Wo0N6@`(@{_zyPk1hxtCc%u*4~PWef^srzw`3Q4;XpaG<$ z)9O1^nLABU0BajSnPW>#^lc?bpkQlBdatf%UrOC=S#Z1kMjj{r60&X^yt-D9I{aL}EQ#DXU6GiRJ(Fy;R1zhB(eWAO+6}3ch<6?hO@7tf=2z_iOTYa58r_Gm(0e#lqoYkn zv&bMn!QU!i@gmEDjY5(;-%S^B%2*&tR?{CJ6GTMUHd~sfft#26YXes1><%n`gS=W5 z3odk_%Tr_LpLx$E3NR%-DD;naB9TO9auEyNN|8*~i18$(W2b{;(a-fok@mihjxk*_ybVD}0LN$yz;hDfG+r1bPjD+XZg**gmDE zgq)a8bn7wQ&=;6jspX-vj;I-za+lsF{*5}7#l2dN_Q15zymY1d;!%8jW6H5c#*KQy z-|!v;pL*&PtTED&^jdB-^oj|>*!jh`h;HpocZT1Ij>~8UaYdcLsJgcfD9Z1Z%qyf- zAhxeT>HxZiwbpY=amGs?;AKln@Y%x<&>D$Q*Sfk=R3hhGG~@be2u%1p?;nGgHoD`d zId@u})W%SYD8`O5KD4bVsu?zo+HoCF$(C(e`(ZGtQl|gew<6G7NRW zqFLK^->cao88n4wja+ph6w{tU(Vb)lj*su>@QW^Z4nWj<_mPzdy*VscyS{>cYvf-W zuKtqRp~S|!EjaG|EJmX$Ie0>#3|~`nVcj7ghJuE){wf=ApTjJ_ecuyk9wFK>dh>IH z`AjOP{|9iH@U&VQC^>-i3^Lz9B&2D1nzb`?{BCSGJ#|Ua1q6uMT4uVB?T66T=yS64XgZP7DwP8J?RNF_UmOB*d^&T!jZHOp)rUB!{ z*InPFE_DzU_a#5C>>ssMYf|mHe3J~KgGg}SY(_OYtmgt;)kf0>*BvsV*Bs3pwooP# z4hIrp5xJ~#aT~f@mvF#VxM4;&NYmLIPe*j8 zRfZ?P*Qb#6NMfp4V1BY&s)~kR)~$wKC|B_8Qi5Q+1;S7w?^fDPoqd}LUTTwzU*3NY zT95EE0Czo4FgA8s@T)*iEFXADugE9-%W%qvIR^RdPZ8p`-UbiN6^N@)JLyY1sY97m z+;i1na+*rVUmya7utPvZ4Rzy*s)Ri|x-&qG3|M=B)H#cQayh&hx86^!KE-YC1VJB*GED_&i#`lc z+pdk?Pc^*OE`BZUV;%$gJKy@S0=M5_e_VzMIqCHMsJMl-dD?!e`<0NlN;TqaznWKA z532=_wdB{?wkqCWP4x`jvxY~Lw~3z>CS{D7U9HLpj{OayJfSVLc-w|a2DJXa{IErb ztxz0NlP+O;Db>1X3OFg+}KR+x83`rc#D-diKaesm0%Vy{5QYH$W3~= zN-n!1tS@~|GBrAD&fH?G{fG=+Hu&R18xr#aDqV(Re;1wf{kxhpwCOWePcdjlYVA4WNogf96#M#vVK83I(QytI)X4Kj0k zy_sjq!Zv;liAZ`w1Up;cFT=>%%QL~&52oqO2%BusiA>x?Mu2t6nN>p^%b*hXcM|18 zI4XWBpMAyPhxZxeY@fXR0;S3LL(2Uo8~%QqbAyxeVeutQ3LeP zi!0j5%I&!YSVH~oRHG~RORNM8hAMIqy)ibyOq|9U6v!Z{m>&KQuqwD?TW+nu^#{xZ zS`83`prqCtCv@_g>azYYs47Bjh}Uc%G}UB!=EWJSayob+F)SA)0|HcoF9;Gs1 zI}HTJ3Z`wIS2V~R->vo{5mPhWNUK1rwd+;Fw`Gjbo4F<|e*g_=Y;-KvFglq+r*;A4 z{Fb+fFMUfbr-^(;;r=q`PdLXAjTp5fw`oC)QX_V!HaB5KO5VBdjIAXdxdHey|FzdQ z4r4syCt`HG#n$#^oPuAmX647jGqWAy%)!X1*q4&`d5B11_f<6@>QV`s@Ig`rpMqRn z=*5;wUF@<I;5v`crNgbdmVC<$hwi!9wT{Uq`;O-4Y6QLrLl54*gbHIQ ziC>!10URAedWRE)b+#1%D|c1l>fI8x@s5I;3*Ux90V@CQWj%?hvavQgzZR#~o!!Ky zr=`bzII9QDCNxXO4iII)h6c@I;u9oOTyYn$#ZZX+G{g(zb`~rfC@kB_jW?sqXFD2` zM&Vtw4F83w*w@&gTlA82-iM{adGmls&L1>~*#bv^$P-rZs!gTz><^%KG|&5`R?qvQ6U!RR?WE{kgja|Gu&(ADDW67u`}Vr zFB}NatVWxR@09fBr5*Tai%$Fx6r{-Q&c;x)Ejg@(TQX)vv{2koLxq;VU4T$ymzb9{ zRxPy$xXIuu^yUBxA8>Icj~j1nPDJ3C&k1&~4^x2o3M2}2;D<0h$#GBF+)TLc$$1K* ziFHY`fXx50%1E^~f@EW|YFVcx)2!gs+j~vZ(mwDs&M;g-uX?OeNoAWf_gdBEe~Y7@ zXLi$yzAPVJs> zzs{ecCOByNhNs)rQAaPkt(p^hTPld%s~3(%pZR)jMKqCr$~m|${?vT9r1ON4*fU$W z%=mdblq`5?;Jn+i(yyYjtdVyg+c%poy&#=^UPqa9mJJmFz<_lm6)^h_l#xw5TCCSc zaPsH;6ejk=7LPv+HhB1cxeU#}E2KLnKP;7jp2ai9nwT_X%76f7`;Vkc2W@ukKZ-J2 zl30O%)d0R3?0c#0o%BKtUGT@$?#IuG*D*uFFwG_>@xPtOuaRXMHqi2ipxKagtuJ}? zGqQ>9?a_3N(>CBS$Q1K&poEi*;aRo0+WlQPvafdZ*voWI_yL&JfHIH6=pK2?|Fmne zOmS+t@Gu{Fe9V7$|BfVyY5-7wUOsUO2Quvj&u_i6uT;+d>=8TohXw>J?Umpj{N% zRf4%wPN3IqZOjJd!py}Dn;hO3Mko!MkHM>3Fk<| zpY{B_-kNB;6cs+g5kyTRNp1e)x!w)$PCP2PmJE7m#K|f3K3wK-_A4}&5ahzJ)f7s?S z|Gwu^h!J*6tPI;%V^SNBJFxi6~I$a#F4p#`q`?4w* z2Bi&IEnZ&kE^h&K0^hD@i1yV+d~qpZuI-Fg6ZL;Q$Gp)Lhb{x+ z%&;C=b7*ULvUK6gf7*io)T)G1u^0C4)dk*l4!)p?61 z)0i+J4R7~sE)RkcSGI5riAl@r``|xgPq{jWn%D^i1m{Ft*CpwLq@8&S5f~n!%nfsA z#D5-#_KiwPuUwZ;rQMEHF&U!)`G%J1?c7mwn1)hP0wUeH5^nxpPb#`XfiY@Aip5Qz zaxStIg9M}4h*>JUA1xSQh8R1wLxF$~c`xi~-Ey-QfcET$0dy^_-kuSmAltT@qsd)N z!(yZZM`yq}IN1Em>|p2AaW^xxNI-HR*4=1pOnN(Pk87LU?nXWo}+L z+~%?fDxKKY!w-z39p4?Iu4WMwiBN^R2{BdrNE;pCNZyFu62B9lC7Z^pY5ZKVn)$o^ zw!`zoKrCqMcA}OzQ6XI*L?(#KLN~oZntSy{p(YJlTQ6)~&K4Izklr!wXvO@L5b z`dV%W*SdkPqKs4&S~aN%+Qta=D?=B5s^%*Do5e?3UmAXD2%CVm2OE;CJ?HXF7VrDE zqXK8@r3PJ{Pfn=`u@KPQZ1h7*GRNc`I)JePM`AI2K_L;#4b}QB_;Ig(4HQ+|wA(5K zGYxEkU}TIe^tTs85jOgH))KcdKG~xulL04Kpne6wx+xQRv-S21=teNbo!Bby&$ODM zYo{k0{fP2?+5J?n9MkJB0r8hF(_$t^dxW`h^2BD9SIcho%5nh6m}V70!FkY!BcM(q z4wxAM_X!)=41)QkI4eAa>qGV*&h}`dW+kohoe&~WqpT+e8Hb2Lfp#dF4Ot7-0DCiJ z-2VbQK*YbEXt1|b8eTqZBmr^@^tR4`lX=x70cFVV@7sU;j6F0k3pP0Yaxlg)lWMLR z@%E2--!^e4(JfuGLlekdTOjzU2Ax5QO|mXwhO9TdOs%5RWkIjWF;|Je+np|+{PGs` zY&-aOpfFbI`JIRqB;Zen*jD5w5o~zPfQub_?V|!t%RX^g0QQpVkFU&hc8GJa+p>?( zYzp4|QcA$u>!$p#p)*ru=~;L?Ao$Y*GsTEvt^sOZStSfO7u9@J@CYQp7`W~C=b!As z)zaG@vn~}vzH8dFiH{TlS+f?`CIj+>+}LVc$ZiA+Gj4RTHBddAN#UWT7^=J2=>CrV z%v48UT@VMbqiX&)ZhFc6T=wPw$9#X!X<8`D0*9=xZGu`^a*0@;)%&UCBDBqmWxRaC zCmL+T<(HfHcA%vc`WI%muBtQ#IDE_`vr4v*FZw}GPr$Qm;ZRQvA$YoZ(~Hu59&VAA znfJ((l;^{xxlsMew;{}50I`*WgRs=uwRAA0crClo;oxPyUBGgDlr4aFoZXcEuz=&L za%Nb3q9N;G34aZpp<&S<*6Oy3WMMKou$z25+8dG_WtqIjbyzLT!{JL0V2@KUc#tMnCfo^gFOgt#9bIqJg*ZC9v` zl1^!rh9Ixv%1N2L7hlPPfKE?!Krea&C6kyiPJFp641Z*RIgLh+5*_F3@6n?Dz9WEO z2iGF($Eq940oS($VmDtNz2H=eq-hhxq*vH@r_!T70!P%Nrb@Oe)|Vz8{=3dq|wM;y(TK$%iTKAcuPCAPGQnu6N^LVX_*?G z+?!Snmb9N$zE^-DEtWZEb3ySvDhJpL-4?mBuqAo%iT~o=N&&x;Jx@D9b0Wr-q;hU_ zv>wR<7s~%OG@8Qk;Un2z{Id8n9vqfypc|`p#O5~yz4I-`Q72*>74hNI=;jgMqom9~ znq8nZRFnwM*Z}myv(U{QA^I$nz?XV;eRen4)wunN+vMzF^n5S3faLw!0s(NcBuSTs zK(zkE`|>0{%O~4uRHuc*uKio?4CnF;@wBwM(AVOntD;=uF9<(-Mesak#)$Y{$>!U? z`%&g~7itZu4ft!4>YGgb=giocikJ=ChRNj=WsfZl@OMaD#y4CLL>l^tUTF8E$TB+N4TFy;I;?Gf|9w9!Of3KQ)WzO*5ctGkiG5X8f{8r zJ6voygTh~Iyr~bJ4b1`|oXM}%`;b`}^CUKE)dgRj*ipYAm8nO;Af`a{?<&+1PrsXo z4ry#3BUsP8P#)s3IMoT+nEAA$!Mh6JE2=YTtppDbFM8Vrew|-9i`p(og`4yw2~!yW zo#>w`GG2Gk3_5Qt0}Q3~mYql@bOBrqX;!3NN8hjDE>rbS8q1~c6LCf}T9B(_(>L7R zLxTn+ZHMm03uU2 zt)1x9i0rw537njEf4?2KyXjh_?>UP3S!mrwZxODOHg|Cay57V~f)_itgd&zc*&f>3 zKxG}%mh%pN>kchmzJqDmX@X{0ufh6KkpFzK$j5z2Yh_iP)&nQ>hsu+c=R@-K!;NW9 zx{vf#-+Y7{-Flh_>)T{gSqSF6GhmN>>*ybnc&h(>DU^zU;#oDDu1%58;_vN6Tk#2! z@Y<3*NKpS{>H$aYEY4?CqkEz11S~#a&Kz%G`Zki&%p>}a2fTM(Io^d()tLo#0s~bz% zR6VJdtwWwF;>VcQToxR9Fb|FFfFyak`7mak3#EW#22m=xGti3eR;Z5tDQuBg^1;Mo zpp0WmZ9?uuh~eH=cFdbp^#dw)Jzho2$G!p^kpm)PeFR$aze4$@ZCk0c50}qN+Ghyl zp;KWAy7T?lgSJfB-I(2rY>Md%v6^WfTU>_~(k_N()#o;;iO31yF`40w$y359?BE>G zBtYD4+{sG6b1nbBh5(~<7Gv#)G!UvL^6YMtc_}1(fg`pp*%RF+pLuzvnB^h1*}F>+ zz`C*h8AEkoswf`qoM%_-R) zWduPqLDd;h*-o`Sb5vJ6frNx5Xf6M9s2eFe_e^4%!+|&l=w9oFf!>kxmipw6r(dUX z8azbn@oIAJH*bQ)*+~CDE0sJ z@tI)fL5LSuz9`pnGJ@AnM2P!j&>Kqbm&o=szd(kqiRLI^aSLFcoEt6{%tmgS0 zW0ypi#!>e1o~>yuA@#$Gpy`~1o^R5<^WB{d6$a1m2fPufJVI5Z6!w7UgA%wZ`lk3> zb&-wxsGVP7SZD^FdT<;Me`=C@GehW&KcG~9h*@UZ!;H_T7!T1Cy>prG{h7hKK5 zwL<-*Y)JU6K^M>?MF&CQ7tK_@iHA!nmLWE*pQ=bb|5VVxs%sk;ZZw&IZmDKN(U8i; z{PbogSx6Q#*?4h7;r1%X@4Ex)(f5Q8@1zW3SmNVyVElk`r2>TJD zLdHhO$=mHfW&kpuXNl7TS&P)N=a?(a5sVyM*+^PqkNN%RdirgT6d_02f;4{ecW3ST z60zm4AV0b)pBg>Q6Y9~lx?QLYgixp0LKLbL_>L%XS`p62me>sNZ=uooV0-4`F>401 zqbC_&1(MB z$@Nb5r}h*YBML}CQi{4%`v@bQETqVvOJUDXYO!u3d;9*^I(4_V<4AjoVK;0J3eq^jQQ#c1I?WQBb|Z<(BP1= zCMu#5L~*C?VFy8E#phd@!yH5J!^VV97l49r6~L)2NG~Eeo&3~yU?S+04pE0pIa|t4U|O^xuRXRptb39;kCT<5 zCpPyal^$E@Y^0nO#&C!IeHjQ;OCTe_V1Aa-x<>o&%3$@aJUKH$qe<+-_F*%tGp6T= zGn^3>n8bINZicW39g$sFDa@}5`N(j0OcSOSIHUDiD%|YNruR}dMM*=~E_=@vuPM+y zLQw6>2R?)MF%)f=_Y*ZRyxsLpVv)TzSF;<=4CHIRvria@c)&fzSA_m5})|xau&P9|MJiV zYGcX*Uz93iGJJ>M9&S#9{4#-pFN6~4q{_C>z zn1OvdqC8g3nbW4}q&mg2w|DCIF5PPmnrB)$k!l=ALJx6C^f7|CXd%k1aQJdSc%uzr ze){VdD!AqMzb`QEB@4e(##r6~D}OlAk)kHxcH)0^YJ>zP>>a)95$p@?{j41 z;4$3`2m!(LH&P)_>q#;KCm@fh$lPu%5GmU5yJXyR?90!*T_uLlb+)Yx$?egjSIE@S z7woFG;yDlAR(gB1^8mUbLP_2RM9YajNF&K?EOwJCQWf{??P&Wbpl>M?@@~%>>BAc3 z$KYWm){6JkX6xWE{#>b<$zP)@QtanFELrD_GJC}{d|%>R9tet_`1r`fRS5#8UGbky zr%n6SxBGD`&n;k6gg*Cs!P=Jdss4D^>;o zj!B^)?lZ2HT#X-7N*3PKU2Y9VoT{-wbcBi2h+sgMp*zSdaDVyUi;YnbHt!Ch7-mQx z)zObtL6tO|ap`BK=af?;TD|ekv?rjV9Z82K^QyZabV;NKPhjyv8-mpn%~}cT0);UQ zC_uv{)l+(rUY8Xw;xAr1`$XhsbDuKRecTMjMUb;N&}$G^$_1I&dfSDAvmu>QnBT#V zEujUjeg6m-p&W)8v;ls1yOQD`EA6iGQV91M?_b(K!FPwFbPE1B+5oa*29!`_+zgFJ<5Bd-h$#>u5CsoxY*s#uMW1Z^Ho2Z@Hd#vv zt%Quc@JaFE)a$@kM5hgnbh2q`@4=k z@T*t)w2I&5hwDKb`~h>9fP*oJrA@2-vb*N0wq0c`9h*BW{A!T{5JO4j+J3A(-Ur5f zK|Vhz!_K4y!zr-qJUm^bi`X%VFJ=OxEWS`}#F~}wizOT*t$-ma!P`0JPY76zQy2Yr z8q?vreK4+E?tx=8fpzcrpwR~`<|G5X5W~aO#PmxULF&8QVCOk#hxl5sQ0vse+wcrV z5^*d04kmX5p0%G3ctVz$U~2}H?3XnYPo~sjRfhZJD8l2N#=}8quND^pj+XjONv}l$ zwWs-?DPX>YT4MYd>gT+jt`0LG|ix< z@Sn~wLh6WSoZAUu&nwRZ3-d)BLIpQC& z&Urv;aS!Av45U4iMk>%6Q8Z|QKkRwg2;qQ0)a|{ge`_sVt->*(wBxd)KhRHZyByZJ zDaD9{CDNzx9qdH83(9Iv@6}6&MB#!DWFjlzndm{>!qP1i1rub@08Ze*nB3m`pieHm z;L3lnmC6qH28Q-uVC=_PceOmXBj2mX z9mL0VHU&DO-xe~^>O)54k<9K^XcTz3UQ+!x$3W%#TN&pLaiI=gh`a;K#&HIz@e=^d z#^_nd1Cebqw7Py30*@l-fs|?sOeCMdZb&dNVOR6@0&*GJXez|M%{8J&mcFc%iWmh0 zAP6z362@B2p#@vNwWsx>wXH%`(TN`S6|aUYb8-JkK8J~mTj%TiL^O1raX+r1>c@$; zs;{g3&-g(u9)vEuMGl;Tm-Yt;a(Oul;xelAAe#ZzY*Sib!OFx2m5(>MnJ*mAy$gv4 z2>*9=h&La!IY*foOA!H z5FwXf9sXOPBr_d5Vs`^c*M~^=MSv5az2>vtCuHCO2@?=rN@2j6y@8JG=)b^9e~h4p zz3iZxnY3s1FHb2CirKf>GNBL1igxc!k_o}!(*+l+xai@5Gpw;u#1=pcp*6QVB=FE4 z0Rl&^jWK*j@xxSUsRdLA?fmqed3J21=z>m0I&5iZ(#F&1E*2go*fIy?Ze<(4yiQQO zQdl0#v4w44uwW5mT1=U)zRS@gj2V%g%>@>~-`H_p1>e>bzG6BrAUugXzaw&(cCn9wbUg_mo1cglS`i@0=V~ajRYLHrwpO_V5cfr>lbEq1(emK97 z%`rDkL=2|i?|HOCDEm$J2xCq$5tfYkbCGd`d+D4uKGBHNk7z`?#9Dj)vE#yly<;!R z&aS&F3I;)w&_sKf3h}v!<~_mAS-<}S*ggQPH37N7bPgyRsZve{2G}H*`?HHo(=5`x zH+*FwCqeiC-B)_0vc~wY=f;-xHAJONy5o1nbGX4CdRm~ z@Nb2_=-)mK!FIMujTBX?6^Ok{jADFHqIy{|UUM3MW>$FJDW>d1t5}{-BoM)4S#mAK znKrQEurjWJLb&}sc~@^lcT3bTgmC=t$hEZBBAa0vWWwcM`bZoy=#_V^fyJCr2l(Z@0I z-l2x$qz$@-2N^9D+b&Grfq=Ee+uA84BqaA%sw=r`%^;r^dr2Vsn&r{ts8JG2TN$yB zjLFM9mc!vB&TO@oX*~}wkNB?TOy40(e!0wHmx<0|9BgURW$Q`#INff}kuUN=S4j~= z04C75tdc!cw3~5$_|qmfisQ>AVMRU~pVaBWIij{7WCo^!-G?1198*LWjI|5iR7U=O zgy1_^GipWPb3kaQ(QG*+`h9Sj7r`1j?HSO4VvxYS?HvzlW@g&vw%5k<3^LYXA0EqC*cpqZ@{8Oni`*uU z0Ux+%P+6HQtv0bw`KFkwOW1<|4aN~bjQD_gZS8&pIyVbFab16Cd?VD8e+!R#1EO9z z(e@txb!#DvJpI0Dz0vnqP3Iig9l*l1q8>MduMtEmoWO(PaT+TD zo=c6j-rVxyYs*S$sgauS;9ctN3JHH?N<2gd;bmYx8hUayAX&?ALKx^<`v~E*8pBcY zT&Ltd^Y_-WIDabE@I`5b^C!$~iD--K2B7(3{-4D^Hz=9iz@n${V3Yu)-sm^)$tE#X zIZ$zveeI~B{$=VTnRGMzPOSWP;@83U)wY@}Li2( zy^TI8R;U7&m~Ms{MUO?lX(t|Q9jxFtT2+;=P|g7}Cw*<|C$}0#SqYc-t^ZhEo^zFr z{-$RFyp=eg!p(*afe3g` zBB#&F;48)9Vgj^Ox_!KB&)&)?)DVMMM2M47?y2zDWIjwxukF4JxAoKf0D-cNMME!V zWUow2PWDkC$)`#XHdaw1)fVa|fw_(=lX@l2fL;K(>vtiEw1_nOFn z44O=pl5}(dv}S1YkgL_ABQJvK^+ z@2BPV{1sa+W(Xxi(=XqMT}8SsQf)ksnq$BUkhn4Nwq}o8JqBp^pZ64$7xdfAayaFP zOP1tIq&kzESkS58K$@lR5AQ=O zPKa~Sg>1B$*yM>Sx5zdqA4cQKv11t&#$XDwZHscyoBy-}@XEONW?3=4LP0A^-Tlz1 znvP+eFHK2FFx*s-NIU77Ud%p_0*NjZ3hp~{@1foY&IKxuCK83#7hM|8B|eAuPtVpy zIoK_Szm8+NeMY_`(CWaWq6)NGdA+99!Ct;E50$pXHpCureYjo~W3>du2sf+jCB8RQ zi(XTpaLNGu%0IPx0Lb1O^}ZU4L)t{|=cnN~8*9TusubLY~bGb*=PU zkR-6j9c6qjOTZ7V!i>y--m0x-e>>X2{x^8f9f-G<;>#tJI6|9FiM99&Z6QpOVCw6+ zyBOvrHKe2Xw=l%I3Fp_kwStK1Dy##g05Jk7 z{yFG&Al8r=$R1(dl>@`4VBH0)A3_sMO%g9 zRllHWUt*?l%8=4Tmax)wY%(aNg-Fu-M#X6)d` zr}LpM)^H}Op!qOsohcd-&@-4>0DXu%G;2hSO=9`;9 z0nJ`zJ4%KxEw+2mVqIqY?4n$;ZP zKDm0g<3pX};Jd)kxt&|!wtm=IDOtFHnPQX)b}S$EC=6RbB>q+&sj;VhqzrN_S$PjP z6Mt3>i_uPAlhpsRBB4J|^P|luB5oOhl7y`1k96Dl z{@m>{d?MW_=LK|6v((n0R!jX-$&2* z$j**mtHmO4#l(xVeEFX~4MpPVBmiuO&?l0&eX6GV^Z&6lfIRtOA>mK=xhWjh@FF#f zXq$l=Ghlqk;X#9WRdGql;d_7sc!H!63(`BdV!?%J%+E)ED~2&idVjA}yIm}Nsz@}U zWKD)KHFQP6=DgifBOB_h&~bL_gp3e)&yT~&jvX2;iwm;RAgXxUrxs>WL{N`BlYEm?b3|QggYNg)i?&HO>U|uM93JSXR)dUc~lHQCAcMk zv><402`=C_7*|fJ;N6=wF=lX}vcYoAiFl`R+wUOgS&?axS+}rHjpfk7Rs20>AQD-$ zdFanzj16k>y^`ja@u&Da+&a&kYhF0e;J*!k4V=Rqp#Dmpp$QLv)?l(oMAE-KZzKC% zbSB8o<%>yF zd`H#X%j@{lr%7a7jRtb2%Jn*>@f`q8+QPT>MuDcr)@{8&gieqGha?SyDec)DvOqz$ z1q&idirmK6{E|g*5D_iNcao1fq9LV}+&F;6&+bgyvm)|}On4f$5~Waf=>u#YDdieB z8^0`O>QJiMp+G=T^ez3=RP2m&LHYIXZIGVz*(!XBb=9_^)Wta~JM_U8;KjJu28j&> zS$e+t@KiXHO13m-w&huej8tadef#mc zlk9@eag`rbGXlc7ne44jl%qx114db)OW38Rv-47E?-~=>gn;8-=UcUO^fRptbT(pz zLW)Yo)pdjcx=lWe3$*5CVzEijrmZ}x%5tBbFd34vQou}T3$b^OkeY_H%Jd95s~u&t zgx0e_-P=-X8NU#sX1vwi_4w7EzRjJiLRe&MBmS5a_8O>}s%!C#_n`3q2<@IKMI zg1bro*?xPNbdsm<)AmJAS!|y$!P+Dcl+D4N$$qF~H7bl2c+h5Fj13`2rKS}Y$C;il z{119cO^4H_7J{(1SVv%`6J`tuoO&VOloV`2^8Mxev6fRFNHM02V~fn@4VZbZwZCoj z4fJ4&Q{e!|b>_0B(_o-D9(fNC7oL}6^I zxy@Vs07$fuAB{>12C=^|&<*`xHkW8J$U8!*R9FBB_W%6wn*Q<+M<5^|U0A;s$W9K# zt8tMg?B@nDe>)FFET`RCj}VqQl+GR)6CS02rykFAYAD#0KN=Gr3>L=cCSuN7VRmEX zq&9Xh6>}wu;-FBgMu#Iqg&dXEXY2oZcYkmD>ar}HA6K);t(tK0>yQUJ0y*nJIN}iP zg22CQTegOzaT-XdY^j47^RV?1jjJodW`geWw2x0C)wzz#mojqpG}IbO%^l*~Y!NJ_ z=2YLjB6&S5Ll$ZhB0lq2d!;QEORL8Mk%N9ATQ)rH!}t;&5OMS+M}xPRSyr8Ni$moO zG7K6$K@<7Zuu_nH68npn8+$guEh=o-@Ct8A#?$4W9JS$WAzL1lJJ-|+86QsfkWIe$OYF&iX@ zW+603`%%;khhjJhKVegK%oto4FOd5gja#K%<6)0b!hg*IqMm-k#yhBv?hw(w<~l=` zUzB~cI>1WezLH;WL?%lE(N2EuSx-@*sWG(9GxkuK2nA?I=6OM zyPT(})5O#XhL9W&XUn^&>7<*Fg9a1b^?(>c-!hioYqt6G*mtt&lbZNbGd|uTNcS1L z91Uunb*HA{;a>JA%xzU2EO`*|WaEtbE49U!xUp-^%`*)XfSe*0hq=y{mzV!lY=Hy4 zm(qF>JD+jOLHujWPaFj4=YRP~ha(Ui@Z*#!T^F60Yqqvuv-x(Mw~{{@ zZEH9Lx!&ADYs&2lzFn!OQvp6V7751a99=*}_W*;;%G7Ar-cI0waEFboZl{-d)d*S$ptq24Ef>RID0Y`z6azRio%)B2oic62 zvXmUuee38(f}*5#e*2n9PgGmTq^1xFLmMwoh_QlbWU(EyU;+VNMpG?_NJYFLM>L>U zCa_5ZM$r{qg1wip%m{BG!+b)zGQ2KBxa=tuAb?Kuyw`}clNxk-@o&4 zT_f-_XO)Q^dr8NwUkyCN)qz3Id}Zka><_KZK>!cTFhbYThwWuM6^3iF|5nOuc$N-x zns6KBB#*n51!X4P!ZB{A+HN0^>i_%TdtSmh;Mkv`o zW1{aujXt_%)cMr&XP5#EXtW5yQ!CN#M4PVJ4@@1xh~j*rU7e61C*}YL^AD1|X=HL^ zc7uVc%kP1v57&RjA6Xf}Am3nD;?K{lDno~da)rU!iYQ+-y{Cq#7U%H!UbotTa+5cs z45|7`*ts~MU;xIxVI9XxI`PC|YRlm)dD{yqWs}3WS3(q+5a_rV-j^%(l6+}2F?i#~ z4>5qZCGsa290+8c%^!7nar>cSK+~)gD~k-QO}_)wKCesV{FSFG!aNSYxeXmuRI#Lx zBcHJ3N#ROpuAF)6Le6eq@MSJKDb#MP;(?dv43<;R zit^Dsk?7@Y;+f$ztV;A)bYWH-j>WOvBXA8nq(s;t-gA7ke>hi$IS!1Nml|Xs_u)Hn zmj`vLBM$pkzwr+4q3TneXO?1B8_inVY)_x4N>BWMmu(vB_{UHrWuPdqqwO2csH!PZ zAt~7c;#U3=GG7^82{=#fr+LR@KGXgYV{Ey8|o~(M-VXiH5q&g#P$X!P2NfAJ)5V zR%qVBB99Uqe;gaC9NQdAd1z))kbD@znKkqK-wzx(65lxk*`|<43@GB?^5oxrT&TOJ z&A@BX_61+r%d(d?8I?0kzI$erZvoLyl4Kh`9mIW_q+pxNpTB7^%D?lJ2J|GSG-Gqy2}M$Ztji`#@2rUCr8YK%b{Nku83WQU@JwK7x{AtzhV@)4g5( z%Bb4$AI`N&ibqeFjHXpxo`FVZJ9S=SCv|U5Qh~SzV_u1TT3U}qsPHumk6SsU@z+(l zha6a!v8DpmQWIVUhN@hmeY3<8O7V+SB0>?Lv`JVhicE!h@Zhs?50aJ|!#VU6a7htm zDVLwoDFH4nDh&AXuWc>kLAz~+|M8|jwO-}Bi*rl}?f`sFP9LWd4VxiigC&l&S^bH7 z%f!t2#r~ht(9kKiX)obp2dW&6X z_T^8JR+6%G-8ioQPV|N`9f1~T$*Osytbx1(g_W-)(_@)}VZyqg%d%EKK2YIh>kz0} zt(`&XicU9}X8DDuhp-sKk!-G~QTDWhXxdt87t%+Iu1b}X9oMs=_xC^n_a{fo59~utfyTj} zsmM8mLR_fzJYf@f0u|zS9dnk6N34d+V}~_9dZt8}dH_ zF+7~|TEhF6LoOFE0hNtN3Usgii&~Ilyo5{)NjSGt?+555w4x}47?|Lci217`T23+< z93QE`a|c#+0bz-wAo2gMNcosPL-a`0Cc73C;8M_08iO^_qFUyG?ohwuE!RrsQ?<^9 z3)_nJq<3)EQ5Na(99i*tiUxPj>0ns`VE*Wl@T*2`QaKn)YM-&@;@mALEyo4*umm{V z;ortgfs0QbY7S_wYbmmNl@kcEpe*=hTOs+gV_8H`zxTO#X<5?B85>sGLiGd}g)k@g z!OKZu=;KUICk&$Z;w_UKigWBG+KuY|3bo`3Ln2RSpT;efWq-;(Yt9P()+%vb`a7_# zv!6JHiWg13nsRs`v57fR!}Ur6Zc#^6ykBzdZU9=fUdu%ehn!1_B2dN4x}UAzw$*O+tx_n{0NPE%y~ z^qOZ!T#9$YT#&2_Ku8afWelDtl;L>N!B`a&3%ZGmGre;1O9f-3*YN9}5k3KbF_@S? zj`G5H9%HvJSnAOx^?AZCHmafeeK>Bad%{Q47>%uL$l)1 z-J%Aoxom1327gu#t4u>8p6qG-IkrKP?XqN5wPDO{&=MosW!rX|d9vO>FdoqDN|X8p zs0U1Z%ifnU)%Ht<1R_T4pv}aJI>&Sy0pk@WzZ2NZgsdXtYN%)RRh|DZQ$f*kLGJNJ zaxnzyA_Lb~mLS|EqikiCdE;sozs_f{N(=(O**ePw4tWYKt<__n=_y8n+%;~3n#n64 zwk_5fDiZ$MEy}LBe(FR*u$2hUHje?l#jInRk~<(jKPL{WBN=s?jdXPqQ3H8_Lo^qx zF5NBoPy88jB}5UcWjSyDryj@KSz--EQlDy;VSI3PsI)w?iUjI43UNoS$ zhZSt%wo!?%2!go?6VXV1k?ZPgAl(HVR>9DkQlW=*H@#Z>Pp5Grhl!%`z}%P!&$Ez# zX=EjvQY*nkyKodXxUoS#2+{Hv28Qofbw~MW8EJs5jgK+LNc>6B`xW!CWwASNW@xGR ztjv;?5$8J~U)z0Pysvv&Rbc#ABjBu}BDZKG9d%&oRuz44y%%_vUBHCv*sgX5qrH5C z0a|n_8vB-CD$xhPdz7!+Y!%yBW&$!nw1xy@OiX$P-O+}amz5FncfS~`ln}~g@5I;5 zeEeNeS_1by8q)DX^(E*>{*Hk~4x%C*A;k~pAEKYt@*&~Ia;_O@&QoQM_(t$OWFpyh z2HCaXn;I+xSzSQiKFw@`de#G#uYGn`7Wluia&dn$Ii#_$^PQfnXD?N}=w@|G=HfB7 z-TRz*9CMEqL6DACbA_+p!)q|f?u%H2X~&$GoO$0~J<22#K3|(U-eA?_Z7}IDn`jSp zCkG1K*iI!09-t-_)gpiYVd2zav0i>Hf~3p&wtyxZenRBW*r=#s(s{l$+;R9V#1i?y z0h@qzwj7^)wHkQf`9}ff$tk$5i#DTuJrC<9VqYFMg|xZdMUs64IAFDKwYuz5DLQkv$9Wa+4 z9!(-3`Hs{oDysuu#t_9SYt1dh8&)ou^xVqd;{R!2pleE{EjnnlQ$50nr#FZrGU=rofMQt9sRm6y>93WMs;Ay+y%&JVhi@LVn6#b+! zuT#*Az%J405WMh36FZ~T5|{l$3BL|LSrb{bl1*RZT3LW=mG?~jZ5MCDsOBE)vzIjb zn5WbZ(DkOVn4Zf$&t$}UT1cV;SN}9Q8K#^H7Irkx;qGxBAC5fHK97@6zY@gNrzBC_ zF1goA6V$8jSUS}Y#GV7!>=tWpZr>h1QB(e~j~#XH7xH@|7CiuKn2d16vSVG1bu?O| zFU^tN4RO*ihjI9?XJwDGE;2&*(^|(${Zc1ngl5GTym9pblZE4ZHqufkJ>=B9u##!=}(GHY*4qFeAR&2dGM(k%lE&Ox4urCvJ6v z(*?%j%#NQm?$ZSL9Hgd{lIL7fMqL{D#Bv9`#1kotN|BlMx=(akCv8O|Hb6T20t{ge z^XW>*9ILbPeXgecc$$vFBLQp$=4u=oM{22KvmJ}&GI~3W#Cl|B0mUU%$7#q@Q-B2wT%>_+`slCwYMJ6t)0~5{vnq(tc}5#+9yenD~K1*Kh^5DpZ@Cpd)x8Iss|Kp z%We0-Ma9)p2~7%FA!#zqkz3`Q4;n9TXsdsx!|xrm5fXhkydRPzhU4*~2;>wvT*+5~ zH#+N$jIivqR)T!>qP;cFo=uUyR|mG1t)y2P(CbHMkfB6nRw%YuQ4mpR@C!@LC3QVP z6ln0CQ2|Nkzo^jV_BwX}Antczm(SO(N1<(+cvdg~M^;`{Cbbym6iIj9w&@Cb_s>8W z)leR=iR*z%3Nt4oqdXc1UZqu|Lv*yhF}WK3anrHrO%KA&S8Eho8D4Z?YZ>XPlVoIF z>gX~p-fovhKyst>l#BF^8gay4MCyt%toG&m2wei!<>0Q@{tZ$|o0BsHNJFK2#5{{x zH@v&q&0eIJra6S|4Ao>CwG8mQ<8|rGC0{~iYo<+N4qSR-Tb(H86aHCbK?TnlYfE38 z(yDcMn30j-of#+&RbMvJQ zY)+s8zONGOciV4Y2!rRicVr4QFU!XF*~OsqP$(Fqkp$!v7+|InR)}*nk&xOF7zlVD;9bDMAEz; z*m3tLOf}B?2kvi~wNJvcLJ@Qt2UwV%{KMSk3nlwqj2q8 z5FqwuzT+-;9HcdaT)Ds)LFL?@!=CAuUaj@CVe<4IV zt~uAk!DlkHP3KY+ts*0q4>NCQKS=mIMxX`h3pNL(n~^ASj^?MRl|m*ze&C=3LfYlD zQ9hvC5Noq7+X2(p;;`Pr_9ZrftedgcNv~l9Bf@erT)F90(+LPQj3--U%9Vg3*#s)I zw)Ik%0EyE=G29!YzLD!yG?l^~G1;o+A%XplZ2L{Ss-X#xP55XQT7{5he6v%&k-Yk$&T@Wg7 zIA2+B?a;`2su99pREQ9D_|dTM$Ktivo{rlO>i>-iHe@s2bMQH3c433j1E^*R39WUP z^YM8!i(hBHpYCLtDE(RoD`WRxuF+R*Ufvk^Q=)B3lluKbU+_MB^qgl5Ic}@-rgppWK68)T~!Gxsa{n+*iAa@~imG^$ui3+s2>iFLD8GzoWa}OW{4XmSVep zrL-+nZ3&0;g{Ne^X6KSb_2Jy)b4RPC_8t_m9i)1z;J@zmjhBo+Aab|oluReJQ|>>S zkU{|;yZ3?H=Bsnhc(T!jz>F|T&$jx1-|k`Ij&aOOi{jbtFWl|1vAnsJ*5`p`7SbSK zJ{=_}ALV-SpPKjiFTPy~jWw_x0!eo|LCTEnnp*r(D8qj#7?vSQ%rO0ncchwb@Nrsc z_2a;ax#400Cl`>Ct4!#rc2QFwTYaAzV|6^SlWbe4n_6f>;5!)*S3ss0zA*2u4bqg; zfV~=qkhAdv@Ne2YC#z!gdR<7CjvAp{o6pGH>&bNUAiF0;aiSxiZn_nt8z@NbFV(l+VDCxO@gUJHs!FEpM0mJnsu?U6c_J39a zc6OpoCj1^uYAwO>bzIf}O#s{@^l=$m^q3rJ**&tR&VeQu)F~E}{34_}1Gtee@tBCp%8aOi?b|En$Aq)=}baojcuT0SRVD@!2);GPgTR$QU z%^xftE2O9MftpLs-NYN5=fe72Lv(v92Cdc+>rt@i;545;23Gpnz!H#JWR2jxKAjN2 z8G0Pt@6@bJhu&j?Lj5%K$nWy*i-i)w=XwYS!NafjAsE85eH;nvp7LKljiC+X1K`05 z`!sO&`YXXj+Z_ss>hBY_SMH~QGYDk|beKAuxzo%zp}7Q`e)z6NZDYa&5eDFIm?;2} zeyCnoHe&Y2*4X{GU763>;XP!JGZ$SWr4aA7Ud~Ln+gp`DvWyzTrof+XNaVP7$^bPn zo5OgqY6Vic%e1xE;BlarKt!v;MZPw1*quqhvjBGvjZ;q`oas8ju|3R&05{^Wj^J|P zGOIcdPX;*A9-p{VIkoWpdOyNE9#k8Z|G;_uA+C?rN{$cvuBTD4`W?gi)OQPSp^Dh! z$Zgp_S4kM^1J=`hR1B!HVo=Cs^+|ERknd$-l%#pe#$v4a-X&=)nte7l|81H*-Lo#_ z-xJTNP^gPf^nE5Y)b!kT*ctOpyha*F{$q|*1%iiR#zG}l(ejL;fmw|(moGn%pD9OS zx?&&66nD18&4BbPHdS%3uJCpo@ExWRJ9q1E7te#X<@QrB_ z(lUM@ZOryL{k4j>XJhLZM0oS#i0bSSp6QhvRJs<$%cU`4`j{rLMJGZ}YsapP{L8kiM!|L zjOs@N^=t8>ZMdWO+wPev^P7}>#aX^CZ6JW>;3biwb+H+){!;>;~%bmy8`HN%=;{Mv(sU%D)*uNB5G5bcwcyg7K# zv{Ks;SHAhs_iw*ulw$-?583zAxD3yJjQxBF^)l%HBr`W=O|9druvCT%+{yh7)v+Z# zl0gPK@2N$$Tl288S6N`TZz_R?4Hz?a4Ps?-=YYv-YR^z9(Xu130$>OM@`HcDfZ35W zrn%Ap?Jnz}=L@cky?-H-Ir)Xe*17sfXR5qNI+L3YJnWW=xNG`8=8K-quX5&}c}%vI z&__vRDr19aN_W~>vVd{KP6m`cXl)4{A2o0Y4DyXSQ0=(v+|SQ&OSB1ylEkp|*wmx< zrDIdGisFIKn`w!dsc}kasm4;~r8{kg&wr!zoQK`w>fvDs90t^7EJ#w+@kq~3RNdwe zT15crwo&>$aRCCn_8xbHqb<-R?atzQC@DJXS#=nF)bJ*GsrLtgMp7poAyaOsvAt?wGf;$SM222+&^!4gZ-|?H ztYGEZO_on{0pq*`AY*y*KRyijz;0KO9Lta8B(#NUSFl~H??*DXc2+Li3e{U98RlT1 z(PW9+oS(HRR&a-ph}YF~WOlo>#%#sc0E}u1=Gn4IV2a${B^kUWsZ#8C*|r1HL20_| zAZd*HP$Q^j4{+byIAOyCK@1P*u*{10vG(%>`q#1tv#+p8MH|}s5wU;|tU`;Pxy6RV ze_vBT5jy%=8BC>M*Oc>I!jt`ZMCqsX$b}P76@w&QM&QM~OnxcjzJmj0E>>3-&s2%l zkj%#6MT%I+l9mi!4t5+~Q!ViMJClj9Bi_%l+IX@wTLCmx=!mAN_}kdkX7N&OVO281 zMn(?E%QOY3u2(GN@jVQOq4TsiY(0&GJ-n3k66ZuUMB}V+vpkVlJUR&UW`mhj7k-Jw zNPJdUYpE1pzdCf*JCDS$H&ey~y;A=3Vd%ej_`g`8Qz^QF8h4}IqCQo9Ry7LX%q^3u z84uP5dR<+;P@5Xey`-UNZjUJTePaIlrYXsyuHzTkX7$rgKn7c`ES|eaGSe!Kb2e`c z4lE{OTG0*~imVwT3V^yi7Yz)dqDO#dEuPwH&vAmH@v*+6cuT(4`LfN7q~NZaCxCxS1Sr#Hptt<0C9tkU1^3N)SobpFHv`%1EgX+Q?&JdNrmDWF- z+2+I(HihDyfGp0Dd1JwO0* zM)bQ4spNAu`D}9%%M1#zc}LkL7qzp7NQu_;*>^OPn9r+(*V6NsDg{eBs+%)&9m?LL z-e9CvYgM;q|JRsAtvLRsr{h#$A zIsz#M>Ua8oIfl8w-Ig@~zK{t@SuYubfxPp~p=1&oyIwgXX#oouHRDS4rF6#*kRF2s zw?PQY%W+3_dQMb}$5z|zUw2k_WaP3{L^Wk%IWCq#VF?NxOIN)fv)_)^Pz}dP&J`-a zvZM@{OqF;EG(>ZyAcB1_&OAENl*mlpL7sXu4xcbe0l(`qc#>&q-E4LNyTsE0V%D59 zNy*6S2X;;w@Tb?Q5%bX0|aS%C6viB)q9B*BX zcV}x`3eg3yo%)+lxAtvNMhrh8SR436;%`(r|L_&z8KcfQA)kAPVWC;D zyX`M2dE}Zw{r!YioSmBFRV>%H*G*vzmeya*NS8g7Pv`vXi-?|OKlWhHuG;506tkum z$^`r$PMU%h(8F7F`!i}#9WPcU`!m$uIPCd_{ZRew=vA0GWX4xdYu1n7)SK=%ia;`e zu~VftM}Fyse0S(<(4?bn`k9nDUs3q!jXF1XyLy*mxj#3O;<*yeg%;&BRAM~j(?n5) zdO?#JbVdbB4!5Z91|oLQU_q>F&n#vq`L>=9%UO4_XMJ3wU>{VdhZuc9(keDaSfF_;_3;4_y?Ltj942CLCI{vK&!7krh}{LzJ=XF#IMkld`eCd8Et}2v}tamiBI&70P z;F3(B<#KI9ct!PrC`Dd_4IqWj#opdcz6V-(iTp&iXfqQ0`pNw(b6H((jaFGoh8WZg zMufAP_Pe#3e9b?uE<@Yxskce?Pj0sZl~fliWpVC-ls9h$B@|0{IdhTaENw?M@oFz- zaLRT-e^ZCdft2Rt9QX(v!g#)23$_=QQ7gyKV>Tp`Y013zvL)G{sQ-yyxn(i=)z^4o zX1DN+4I_o!H?g8dMTxkmic+ntj1BWog+XhAF`UW47HC8JUspdklF6Q1jIq3PJfZyo zG+>&QxsYHA2SnEnmSn5*z|LTYH#6frA^LX+wfEx5}2ABxv2u}oK2@>;j7%C%_kW! zkdJw{azQ!)5V)#T!j3tRA!zZsWqWm_I1xKj?PQ6%{aIy^C+%S%eC*eEdj1S>?Htvi zBS?p}MtuwuO>#*Ao?Doo%kMQ&aH8PKkGLJ*LG%mpl%C#ismH>78?BraO#m%Sx_g2A zd}Fq*4fom!@O7EQIEx5FsL~A;v#zs>y{WUW?1e1gjk}DaI!(v4Xz&Pxk@HKtzqSbk zZ!vCGsQr(RJ$j?d=YY;11H^NyENUxdZJYpjKQh6lV!e?XkqH$le6(Qj>7^Sv?1Gyb zV^;9@W)LWdH)Cu=}XD5_DGodbf zcc#ZS^;o&{`sHvY?Cf~#=T&;^!yt^mBf^@5tYU7iDl9ieoCvFTtf1j}tY=ifXe|;_ zJEauX>PU4!hD$qyJjs$mrWDqM8A~=2DCXI=9>;^kHK6wzPt{lha*#KKa zidjOkEn~pNLeygfN~Xpq|IrmlRrk`QrGDPwotTE`Cgn}8D+jrNj}yiZMHqaj(l^MI zX1JGroMgq4r8m20#e&?7qVC;056VngSx`%>FpF;oe<`?m=;)kgGIlw{)-|G1aKD&)8>rXoD%<0=j5KTgj_fWZ_jq)Fg&-1t!vp~ z+a-5EVW-~<#?pUPm)#t{D^4UR0A3x^b5V{scHaX(Y~YtJU8dODm+msXp4woWHE2@G z;B4;kmYlBQC8mCiKI%cc(*^z0i$8s265ocGU^RDF{X#tk5mMAL)Nu?UBroY}iE}5>MixBeYRi1#qS)xxxFf@1+7@^P6eNMjhbO-z^{lLU+(V;2xS= z)gO2;o?2vjp^}_G0DR_IuWBl7^l$0jY%E8aZ_=c*qaYvxW82|H&+=@Fx_hKcz{4zcL2HgpcGRx zuBOr5ZPXz)J~1$$pcR{%SVp74l<_60&GVj7(s&d^8|0(oa|K%rQD3;3&Mh-sIdCU zI&P**5Q=Jw*hUg%D9j@fjet_La@?%>rWAr-bz5Ed4&3}5V#Nfsu2M3~(!@V9;p0PDKu>*632}1Jrbz83Eecr3P$B4U} zsUlK=Phw471I*}}3mPHkIjqn8S%5VHtcN~%eY@w>s`HEYhw5K=V*GH z;j^G{kVeA{`3JfARqw_3{4VTap*lW=x2^@^!Nfs3520kazcxOK_b!M4R)_@*N@i|0 zN#U8zs)r|6aU)LG{d!JdYW=-$BfQLQt1R^5p@|Ll^#_`%?HHj81}GS`=TV4J$E(P! zd!R=7E(II)d;G;_(wJ(e_fm-C6>lRxpG5&*4S(D)l zIeB%=TsAQu132zdw(wR7`^el!513gIa8qwcO9qPcwE&N|lv6HoY3K)rFWaTxee%|D zS&Bde&s4M#EGtZ*Kk$UsXub(^<+`MeS06|Y679YvehTxGpe$%!-&~$k!yVQ|y;S*g z5^!=835f4NTsaVvrzl!3{<_nLnZ&v>(Iwl|NA*0~ppncWT=RS<)t~u8XG}J$d=BCg z6XSr5^M@{S_6)2rldnv~{kOMIn*EKzBRd$b99%U0{E2Ef3b);6-dy6(z_t0&DSryQ z1@+sEFoe>VmmVo(UQpg-{dG@zHbefF1VyutlCEK_S3(XHl@SjBTOL%YPnvqVDXfWz{rI7)H6pFIkyH5|c%6)* zvJ|P3(i9@E9%)LuaUf!Juj|1#3PMi>5*k%brMSGYsshUtAnB`jy5)*i)!FOru{Z2f zko6%&bd)wTMgc~W^0K?V{U@P%1yYCU?jKZp+v8EctyyC~y#srRYO9oUZIM?oQGFv>1SyCzzoe>VgpkSE$N?9P25Q3V z1-%R?lv`PVd$*a-HXQCrm)TbPBsb+|ol8*rwJJbH$l{OATd}O08SgK2NqCwM8Z)yt zsJlY7isG4H-OU!NElXny{O3$wGf<7nrpIiot)$6$y>^B z+uV=@wcyfUoj4z~grBVZgEc}qO3)?_;)&uUuI2#3AFBk`vJ;o1Mh?2Qc`v>4ev)I7 zsb6J!)X4Jrv86yHmG}YD1zg%^Yep#WzPFNI;Pxt6KpSc}(xp0}$zj33R@e*ELeH1)W>LF<;ZQF^I6|0b@SJ}HPNx*6TuxF< zF=+_b4o+Ms*2np_V~@jnhvV-)X~OyaO8NbH&Wb?R3$#T#%A?U^ntn`yQ~#Uw{+`6` z=`!MFP{UoKP$^DXRUP!dc&LnPEP4pYLq}B*P=PkG$r5XCw9dge+clHIDw|$)yg>b5 z2iA~YW4D+HqO=y%)%K$z{#TNGfrEyZeY(j-A)&z5?aynW1u(f76{Xw-sq`Y7jGqC( zAPD#JBGN4(ds_dN1n#k1^Cvz+Q3F+cUvHB-0t-lNWlD!`ks=X|*SXnE$U%6?Bp?x8Rum`Y*4^`5h) zdgA=vbt%gW6cNn!!9cgufQ0C1-MaRM74`ZjzMmH-;D~9H%j1SEUD2i@Wu<)pbx+d` zKdNy*_E&A}_7!%)t2`25hxrg(#Z+%qvjQ}D`U2Q%;Mb$Mt5z%uB_o}CwkMRhj?J+b z^kN7TODSxK;|6JrwuJ# zJuTsV+@Y_VRQ$=%)}GxnZ_%#i{6Lg5m-G)HK0{%Rc=MN}+-_d#C0&)fm%MxonUBkk z(Qa*&J06g6f<#AA_}ca(t#Zb9)FOWjqBi#y^ijB0WrmWDUdD-Kn{Eocs=&+TFKJUB zfLTx)9^^f%3)Wj(8rj|u=Fc#J-1GjYCje5PMd!ktYZ@waYP5Cy->Mt;J$jwqhmraX zc2bhIj_$D_P*i{Ri}qHr*hWu7n>`KO;5a($yh0WV_J$P zp^E>GQX=%HuzaC?M7xcmE_!Ykh}P*0QW4IvX9kAgFKqkL!SS4{#Ja;CfAy}P>b?rY zGZ#kx(n5W~r*vZ8bHi;;-!lWlGr*#GqvMn6U(1EkUCx^PT=gJjLQVodU6xKf?T$cY z^$lDAa2dky0bK~bm9}mG@XENcnO?u=KMuuAGt)Z8A|9v*~|E2RSX!Ib+x|z^l*SaCcx;m^j6sLDCh6et{{13rh_w>hB0?1^ z&dE+pUJVAKvgR^;B878HWR^mboRtP3_K6n#P7INH!rNGpwjQ|-Vibv5knN5DqA|ba z=pXHI#^Ba43tyy_-$Vb693Ykhg&Kzh4NP^7s>c$J-2~oe`e4s>C5sdgzRmk7(G4}H zQF=K<2C?#|l&#pTJZ4slL@c=VGc?x|lFH=N`R`FxO53ifF+h)I>=kcb2G*k=WaC_i zfD5+o+;4!CnhM6by{M`+-;8=B-=3-TTVkj!j*8Q%q3_h%%Asw?*{Zmu;18JXMPOtI z=z5Qw6B@&o7;s#n4Xqm(*KM9pq^4|Q(WRAeAYnjWyVK7~sU!}Byc%j@3)RPM6sN_f z*UI~K+G%U7W{pv{q)V@EZZpIopeQMN!yN;6^*Ebwk$BHeA%?PftgMB(3KbE?_j)g( z;;>64&2AM7gx#g`e?vEcLu^zsP6s!!%To0hpAO~t3+!QlFf1`yc|Sk=0tvF0t3C@~ z+jJyzq=^TM(d}{$tU4+(F{$x-3Cazv+q7`u0x!Nf*T@h#6EvmJh7Z(R2$Lg6Uf4g+ z&@F5f4X4}VzL-E)vfozOf9b|%$5XVw<^F;F!b8q_9*^!(4dRL2I&=XkUE&>Iu)Kk{ zwvv_($k=l4fsS@|ug1FkdyI}vSPid@UvL;o9tu2;TVI_KSh5&~9)5@jpEE@CE#@yM zTp^v4t=i4D2~Ui06<{>`Nk-L3(iudrZ7;h$+Qv!qZ**#7^XmPdvavoL1a!W`R9A+s zlhbP^LpVAmKV475u!bwXoieGM! z(On%Q!>V{lX$(bjKDuG%3V-~J(ZW1jU33p-5D@@o;Kc(#u%d;*LpdK)Fs>TPjic{|0zOT*gqsA zC2<}D?SdnOrsN?-tR3+KR1WIaVg4hs>=%&z6#ASJ~SP5dB4ctq30;AQ66Vtc%x~us9YRW;b9* ztU|k;|C)W{!HhEo`fP!zZk)89NSx3ntM-!`djLt9N+V?NN9T99@2-=S#d>qVEHo=7 zG6^60=+E%kZguFJJVFPigLTi+zv>Iy5t0%~t-B*KYk*q%d)U2)bMj?*wJFTvkrv#d z3I9ai*r^qhi!x*Hiwi>x6+S%S8`MU)JIANFBuy=0udBED+L?j{Q~ixu^bcX93vk$& z0!?2K7@oKboOYmktgJETT=p$P`ttnm#Y-e9OL3^CMc}O1wANxaINo^eQ*|@v+JZdH zEOmmVt=w2iiaszUBPq|mWOq$EC?`k$0myei8`KptN-d1t0<>6GrndTFP^lqb#+#e* z)o$zmC~xz^QL|n;f>Zf?!&v~HgRAwq0{b{W4e zmuu9d&uuJ}(ZMsI#I(lE&t(MmM`H?AYi*D&2gXN=tyCP9YC~>hta1>93{Q5%DHpGz z(B>yTs-(&oYbnV_SFqV8cPH}8acsld;8pVMc4w1OwvVendr{siY~#p{FO=EV+>cM; z86#wTbh-Y^Wmzylx^1fH@?GWQdaQm0{aDXOa24MoD%}MGEjLI5AIWi5TtwjpSgKn` zkT;f%oka&cds%|H9GX0d3xHU!C`^-tr&(+_fQg}7tEE*5=sRTGiB)N*F}>H^YI8&N zzRFn~DHpGzbzqXhk=kCAKuJ1%w_M~*3BNUfSGL6p4VV2Qj5IewMO}H~;`(ep*z0p%QhLDYqkv59PWMrvb zJ@#jbs*efzNf)j;&uX|Q`bz`wy=t~SZHHq7P4aw7zj-ta(HB}V-KkrBTK=4LV3@w4K41pF1Wy_LQ6i5j%}lz22VBl?@U)B$PbAj<))*XdJ-lF*h@}v zIt%z2B)wMNVMZl6{)9{VKa`H9u$2gr`Z_s1?yHqV1$j@T&qNx)mi5cId=0 z>!sT~2d9LXLm!pcbVUxahE)tC7#e#Z^;_)K>lUGsRz!!ZlVcC zS><-?IMr#6-8TLv34{vyIN`aIRU#vRhbc!jP`dS56kY>Hc%^|KBdJ=-(9FVTSh}H4 zw}XitNshRbY9ExftDb|-6{0l00o;(A&7hxM)ct7>`f~OwUKg5vCkWEizo=69b+NM4 zK3AqzJ0mzq%;?T2=x;?)JIz9wXJc4~8sxQBd;0h~+e{bbS2q*$pU8|9Luoje`p$f1+~bpG zaxC6qgglr|!w#1Vzv`xn8D$6^wt=H;3->QYquu%L%47`+Wo~41baG{3Z4G5^WN%_> z4LLO+Fd%PYY7IO*FGgu>bY*fcMr>hpWkh9TZ)9aJOl59obZ8(nFfcecFHB`_XLM*W zAUHTQFg89ub98cLVQmcx<8efsw6tt>68&a)L^cG9&vIJ2W`Nbe1T5_lylvw~RWib+165}bA5DwpB@7FeU%tbx}wl^q~<<7Tqt{8g@gQgEgzIMPHj1Gpo zZ}#GTwFzZ&kB$Rebk0nlnvujfhKdvC^pV8Q1&^m9d3SZME`O{%1L_PWd3UKfUVVHw z3qIE`Nwcwo4DwvoxE&e8AK+j(S!8{nzW(1s0;EJ6Szv#0k~PPOQ&ZJ`X2-#YRUnht z?;{$QmH{oW8|RNi*g^1P3kVj@Jc*W&Fs$;VQQDbLIaA$2m1n>BT(howB@f|=nA6Qwb)G8Gg!A%Na{*g)8RJ`&1zg4e0# z|1Mm6HsBiELd|i22Hc0A-EvY!{UQQGfTcG}DfQ+IjVcuw5hbf7pQ-)WVsHw2-xZ5c zk*0L=1uooNqu&D=-e7aQnY7Q73;F$=lox`G0Hy~=o>|qeq|J*L3n4?o13gui3if?8Iu1C z>t5rHKl!wiD1RN?&W%IXTL-XG{WgPS=5DW`s!n(Z@XW4iWf1pEK+H{>2@L#w_Z%HFQCop*vsL?D6iHNa9z2_2 zeE>h-zFanbV0*IET6BAKX2@fpi@ke6kd!YA_Qw#@PJ3Ul_s@>NhY3-9}>Fax9RCp&Fb`fZfrN*^}P5!{#*-;4H6CCn>uBMi5{%*O<4$F zBmx~jvUgeq)K^3~=Kk2w3$0xFCNCxv$M}SKP1Vt_ViIA^%iQjY87^K5?Ii-p68Xn41$q#?_NLt zg~2<27R#XIgR#jq73h|Y*lxJKC*g23ta$Bu$LGj#4y7TTwY45au9QW0#opvs?k$^O zMyV%(c)3r;(Gb)XcA~*Rb#T~^71LmvUUi}3gKZkeY4gFve>Wr`#aptl|Hm=PsiX?v zB^yJ5Ob!W@ZM5Mux))SA<~Kn&;x>gE-cJ96FFnD;&u~31(8e5r)uLE#>_GmZtqR1BJ^x~p;ToR=e|J4o8BxdIxd$6_XoJBZRHW!6h z9D+zsJJ0Fgkuj}M3tUWwP__L;M({4<5)%8xAe_n9sQ$KlG`<^!jz1}W4l86`H}D<` z*O$AD^D$?gNUad{{&bxDWg@>}!>1gVHHXbuV5)>#V@*OM%$7m8qp{(8`w}QQxBtRf5nq_cTTC050oLT)j2)*|(_mA28eWE>f{`}5P14@$vcXn`x1%#}>{zI4qNf%|=PB3BTc zZ0oPnYPFFzzVt-WK=L*6DqkkVnP*9yQ>5aj$<;M#?AF-WhFqmd;q`n$g$0j{WyezM z#}=MW(eIX)LN9s5YleHeZ;!MSrg755Yb3694IME08uPUkU+Afu{|Dt*l**tPvM5;?L@i zRhj<2N-cg5S&T3Kcaa>G(7tFsgilLU*iK3`f%~XXz}Us3UzzfvenN9nH|mhq&4)&B zNwE)R*c;M=HA#*b7*lJ-_1G{^4!GRYS)TW?hXNmrm25cIxnw(~u$X7cN#opAt^M*# z>~VjzTFB#nMaBT9r92D}aaEUJi^L>;+vdnQUNs>m09@Q3|43GSeLJ%(q`pmEc5>wh zmsUXMCetQq^h>WRDRnI8vT`u>4Cphy(lfBAX!%n<)vg@MuWkjSJK3)x>moBpR8_x7 z<|cQ29a0PNz1TenNuZ4co0mr8(GbGYg>9zO18Y?)2{w|K^l^YV1)-T}@x(CUW$(0e z(Z(rXI$hhb0G(XKgxEu3j)+V9d(-sD@5Icj+;*-5ig!wiiIbk-iDvzm7ds*4eC=^` zxIy4q09D3@^~=C8dctv7)j(iT1}}q1wbO1)m+oenkh6bQ+m2dUC{TBddvCTXcMf4Q zwD^Q>bT!A69o$ofWreaoJqFD`tZmGq^aFJDC3Y{@2E<#6II`W8!AE6eBhydy3Sub& zN-f~{lXIxw!j#K4)(W2!wEo~0+jh*3*JDxciB72lmpa$K+V6Kne;)2%NYJNF+( z-3}XV1!BbK79T&L&M(s~cxWa<{?*-;|jZog-O#O29TF1qH=H5!09hAQm&Dc$IwrLcJN z<}?_i!$T0=XQvv~-pa1O1vS-=kZ~slu2Fp?RHT7@lYv91S{1_ow`NBRi;FMtE2)~^ zI&?QLtwHT=eqcREe7OxvirZxEjS{&(Uz?DaN@56b9}(czCoHEVYJETtB^ZGkkWSpH zjZh6^B-dfTF)(P9+GV42Ik|=~p^487*Mc-|rm=VHV=QsxZfFP1-Z7=*Jrmhb*^f*( z1}-X7>|~|J^V)tuN)H;Gk3@w4(C#jJ7qU^dnnMTKwviW0FPEJ3W3Ug`y7Gv8QR;Lf zCpaijkd0leVX1tT5y@r1q6c!Z1vc0QC|4w5`;D*>=-#%Ap;Nb4Z%lkAI;P$W9&=Da z*0`BHbd3a2>P87J+5BNse-`r|mpDV8lsd={BbAj-h+V%50=)t9xa_t#<3=1Ze2o}J z1E;Q-e+ntLDl1{@J)m^b*Lx;Weo9Aqi+`3cRWJ9??e$ErkNl3g`(`h3QSl+!^Uau^ z^5Wz3?2i}yt9+#n{UQPtMKGUp@4dOlL4Y}dhQ*)`F#P&$G2T>@^< z;uA7lvrT1wb9=E9(B~woaR>Xtx2pPaPRtf7g_a4Ra5J{AqV)1xj`i^u>CYP=m^i`D zMeL`xCiCn;7sHhpI)Bf$-#n2T$BU^Q-xP`J#s{m6=1HG{3N5#Rbdc)||GriDI8I0A z5~SZ=XdL?hxjMMX<`A=%D^QwUU*|~i91^bGlt$z>L>eb0rf2kvhVedIO3D@RNc)72 zP%~)-BM(@BBNVGvllyTrv)!x#03@H}8zxzmY0V5({WxW_M@l8ZlT;TdLS1!NuRUQm z^79Wcm?Cz`QVucRXw?U8{(e9_B#pXon%NCQ{hwE8=7j;fF&fW&{*7Xu*MfLylc2hX z;a~oz_`)~qf%bX4NyBoW4ciPR#D*^341BGij3HjnbIT+gor!3)Vr*8rH=;PfNzh^e ztNORNnsHAlzO-vGeDK+QN<#U9q9eA;s$^PP2V^7xWt7(UIY{fsRiSzZM*r)JNk`(u zDk`8o^SE`naUQHhcnjC<6%xI8ll-nDd{j}C&bK8=4p*t zr$w+-_ge#$?bHlQCW4lP@XA6o$l=#c*p1^n?H4Z!03Xer_W1tQlt((zlQ} z7FEc_PeI5}|KrIN8gYS*i!apsNid)5i@6Z~%JsB#{$fwU8BxY(gu@`(Z7L~$%5k}& zq-Stl_raW|9)I@hnzZhFFLH;7T3yH$O~I&so5j?c;&j`KbP-8HTM6lqlJtxYt^r0o zEni4!2KECB*vpS{O#*ijXZMh_l^)Yfx4RG?p)*>zoqbzkMgk6hG6=*EQWxg2D z)%Tk-eMBWLxP{rxzNW{ia<2msM*l^yAR+VP;>Vy#r`G-veJ5f$$^SnHUd1yI+bCHv zp;BVD)N@bUvyscQO2CnDx0nhy5(e6&^@y%m?XM0)lh%37UJrrea^zU4djRDiShr;q zw&SopHjP2xo?t5-`DUyU`32)7R9-e!{42cGiX)KPC^VEp=Kl-eEjA zeHn|6gYNGpI!vw8|BfMdK~I#l%QtatvfLr|gVrGt>#H=%1veJJinFCASaSmy{K+p` zKF0DZl`I#lrorpm@i6SmDQw;*zdXx-i^`T&Sr3Mk=RSe(juDY&P(J^1V7aGUz_G6t zVCJ*8ie)Cec9^H@;a!pX_jmNqfZ|N6uS>AE$=Ho}IN=*j$b>AFD|MV(UX<4&qP^!p zZS}SBVa(Hl(^6c8$tR#Yyu!T1qEkIf(q6%Ybcc29d>hp)- z*71fuXv=UryincVe)Iqm+~NB;umc73kkmSPz#mc1Cf*TaG9E3a5XRrrVu zFWs33jdy`tOI>xLgC%L)^x)9|nt&UEVi;SMP$^CN_y$`Bw2YiJB+IRdf&j)ETqrH@ z>?GmE4Fx#pqpqGJ?sEgOgj3Joi z*Gw(=<-Xi&W)qI#bAy~hdCjV*J`CfEWi6ZRq`qrc$o^!V0dNGJpElF-`rF|;I|OKm zkW~n|hO5hOSWa&%O!$k0t^MI?gC-Fs;&i5*CEa@=G4BK*x-<9$8#_&_H~c?AOcBq* zoZq;jzp_qc#DA)JU)*sPOG?S}6XepUC$_Ch{(jhHA&_$M5S2ZO-ayp!Oy@c=9`A7m zWXZ|ND6T}&|WV&Kj39`>z#a(uWGhYbsh7BAd9d-^t6p{>1~>5 z&awEhb;}$8qY!w-p^FO+g3PD2jaaPm9xRfpQg2argzhSJ?k{hsNwv5msgdLrU*Phh zO9$eAL&vuowWQX&dZ_k#BcPR*cJ>iXKhnZ`qdas&ndA_4T2jDKI?)dA`6R;SEe3eA zK*+Yv(eOZh@J|Ewk)hP<(Np)NoO6noSa@eBpBOSDYKYJjjx+Ne^7{mD14(Rn&eMIp z_;qNoxqQ~64`aurCb45?gtSe=G!47DAJ=;$VL$rJk`NlEK=MwIdHZd`j8f{W`L5m{ ztvV8+;_4cC=h*-vqFGc4?7ajj<0>?&^?xsi;U05ZH$~2im-&`Ke|7<7_f}dn`?i4& zXQFA%PQ-C}(}))|jIf-$kXV)XVAZ+F09{{p>1ooGYDAI3;)c^k)nmE(N%(V6R+`V|R>`s_8y|u;u|&Wo^{pH7 z`+vr%)((>moykKtO8=g9*>`Xsz<3-oM}p4gku@VdXy_YFMx1c^VtpR5Rh@yfgWwJ0 zFp{D=4pcQ}job0lx-PBW$8B#qrJmRNLJrGxa708S-`H)7@+q5;fm&d2pd&0p-B*`p z3Q>VL_-~M{BHzbj0it|!WL1Ek6Euk)&|vIXo6Bt z44629e}!j11YS!nxm1^8`UZIBPxzt4Dj&wM17FbkBtJtSD<#*2gQypW$HDpBYxI4| zOb%jB?m)Q<}SajsC^(*K6+8T3cOdM#sE9 zYErtaZ-eqWsqGwschj%9l;E8(7ViQlM%?(y5;@t(|1-03NVzb2YQ)&?;>#>(8&o%N z?~%9gCgl(XGdyrqmsRSMEi>>T?Kkx37q`xW4{d*^DS^RZP^&Rm=QMSnGT<>-LMNjfh>tpalh|p{q`jV;BlFYhk4bnyR{dvk*f9T}j$%6iKeg3&$hr z?9}Ktq;~#cva4mt8HOHl6?NQ5!06cLhi(Y+B15xqtn@s0A;3vZycL$(-YXU|c81WAJ2fmY)IXGGu{Q z*jH;|_!!W)?=!kJd*-UR(i@FEs~Z*&zv@ETZ^RGYz}d|sStR;FbWU=&_r=v7cC+!( zazSrEHh)Ri2FgG|a(^M6D5C$bk58bTYdNw#{pq(v2V=cqCIQVEAD+#d0nL>-gufX5 z4(igQ$onf*rfAf+NSw^628 zO<~haz*rsr%in3AVs%Su8U5)}P2%kjBv>C89zlTQstgFpYweU24B4x8u{$u-0)#Yt zCIdOHE9u1Qj`GZUQ|Wg0S{NR)WKhP7xd>Jy2n?SWP;?P=SBpHvKf&f4*O~ zd$gOvN{$@w*URsOm9@z|Zr?kV4ClJ<)H-?;pA$`e=S{mFFxRea^xo2Miz6DxD%)P> zNMG8U?NKncVGNsx_ZRHpzywD$>oG1CJow}x5{*RnQ^AYSH&BqwHF-#?al^;aWUa;acN;`4X{GQS*KJNh z7@LOpsJTKtnFM@)mBO5km3QL4IHHZv%SXam)mh0D_yyWpVwmG#-yM0rTpp12y_uxj zkK-8y=rJaZ!e^DALjC((>G|ORZoCjiqxJx?3o495y&bSgrN{n}liV+FuDp^!PC74A z>*;45DFUYDnUgSv0)f*{!FeqSOQYujdw~m>JW;I~g^^ImD9WGl-W!&1xGx~Cq~Bk( z%6)kNcgTML*J^V;n+_BZZnIt)}PlsONz4QPC?kG7lz+KDAM&W1?@lN+7Jk1OG?5-2#@Z(s`bZ!fEO6BEM{K zt}6gbK(xR4EcB7kfe3fOUzL;3TGZ?ntB?EI0!r(XA0=TQb?p5hRkh_Keo z#m3NgPn{w*FvX6}A^hj$n&4P*KJf2#8m&Ec*m++;AX;$b40h!zZSUyGWYZ5^Y6E5= zx}bQ}@VkoqF4Ph_HiqbGUF%Nyy49_U7r7rnw8LsCHs5c|D^TCDFIA9;tBOhYFlzD+ z?D0fv-ZF94*l@i37HJp*&l?~Abzqa`Ik-tkg5(M2ZX(eLoz~VdzmfW5#|OKH+seJj z{<-KQGRK8P??IgOKO%vO2DhMR2uTjU>! zVP%Ewbb1%w(D5#W=o7>XZnXT|a}?Uz_N@>&46u!PYp|!G01vg)aq~Fkd6c&GG1T@k zefyaV^C2v57}XbgXv#1PhAXcB2?0m9s2H!PVvRj0XfupQu;ge>Ftv8@eDjGt=0A~& zjAa4JtLSkA?;|w-Eut(5d_W|Oz ziYzodUo8b!Xm)VUbGh_MbL<|Mi(jR~)c^oB-J?wf!VqX%D4ISkTw#B_0M?L?p4u=T zxKJuBbN$ZS2e{o9JqXq4@!F6)==v;WORe8|h}n)ZifG+D_fSHTcxbvtNZIura-!b~ zQNOKuLNJ#xIcvhemlOS;x2UblbpcGP1NfgIVir260QYlwpn~g_v8B0VtFj{C*Bw~} zGQ%?|k7LN?niN6wAS0rCd|UpJJU=4Se!8e7))^cI@pDkOuM^lweRV7~{!6WAYao5( zo&B>lvZ~6bd9C=4kYOCeIN;)u+66ouCaJEZ=PrDD;}TjM|`0@h&h<(yeqcV-oq(n6*gI#PsniTl61%ELM2p*Tv%Nl`i7iF}Vr| zct*fbe8YnGy^ChHQ5FYA*rcxvH6I~8LVwls+KyU2lrZ7CLu0HO}~0n zW#uVN}ASI#mCrbL#?1Bu)LXE7XtVw7afW_-0jIt;Kq+<;^4 z{sB6C0uo)sl=>OJ0jx51AnSmI%NjFC$!mIje@UMc4MIKI?SrjkbOoYjaJ2yNb0*U= z-qx}kjgp7r28h~OqKV!Nf#i^h7mzEN`=YofTWJ4RpykcMa&Pd!-$0rp0skP&n$|`M z`N!W(v%%rn*q>)al}E>I6Qxuu^r@CohRVGzTRTO~uk2jCSE+HQk#ZF|rR3?C{XM5A z(!W9Qt{`LG3g8B;$$P|}ce|AZhdI3_9mtH$fF_<|l_;egVAM$*o+PcgH8%`pb|+^u zEkVIB@(YZY}h&en)x6fCWdsTP?0Q|$I=H|AdY<1#!r3Hy`;S|fqoD=) zFzXjhGzLB*zV|4}ZBgX(=q0ytK3}?q$vlm@6TtL&#IQB0o+lwdajbDMU zWYev-2c}&bnWbZl2gG3690K&4$Uus|ssUVs_5i7cu|E1R9in?XU2-Tmo5IoeqE z$QE)-x`&8SqeG;K#plhjNf18Hz4xLe!@$j@DNgHE?|6`R+cCcH$Z-EOsRj%)i8iFGP)PfG0xib7W;3vMPiWX;keB zq5Eh3>0?Ttzc2bo#HjviF;SoMb7n0&6cb_abhZv_sgFq<(wJ^NTeh@uoOSIuk1)9T z--dwg8VDe=ccYbi-8W^8wiT_Ad2i{TjvMS%88G6^K}D*I ziR+&x7eyRnHq?Kb2H2g`xk^r*HH=$FqwP&-8FItpDSu`>lwFi$7@@qM7xV+Hj1vI^b59HeWRs;j z8#L5r*tsVJ{D@$Wu@wJmgqqaJMVy-k7cg_69&wL?{W}G>U^`gO7Zfzv4zFQ>GGqDe zWbf(xpRR?3-`lUa1qha;?9@lQ^9aV&%x7$(5tv2r!q%N9|CJ0_@&Gr zdHw(@%#J+#ePFN$fPXR7|JdSB?4cqxqV*>-ieV+CNLYIy9cpHY+;QEmHt+U3{d~dA zn-jt`bQaa0%o%i)2GR=Si+?BVblv*;IPWoX(K?~&Go-uF)Cpld;-#tL4=T`yh1 zd0tBxS1B-^$=p}Lx8>ZY`EC^b4Mu7yl*aX0br@v+5wZt+z(2f0m5V9Oa+E}@J($m! zb62bZb#L>Z(%;+U6d(Wm3C+okNJ1|U&YtCS*syuN1{?^!yX{qG!Ad0oY;n9){$K~Q zTK8v65mG^a$(KS0vEeg(sH+o-c7zC-kK77HgF-c0@Oyu%7X=o;*C_lZ{pk-y!mZ(1 z%k+^o5LF{0f}$hh==A6OugR6|dy(~lly5SI^NmXbqcZ{o-cRMUxK90{8k9_6HvDHE zY*>}}>LU^?DfN%=O@&{GYGvuB3pD$Ugwt#-4p@)@Vg-$M;QsVM>;=>~2peQ|#H1>Z z@X>ncTSFwiw?o|uvePGEU*pk|d3Os=;hTr0T=^IzZW_6Ymi14xnMCa!4~K&`!&IA~ z)Zi=%W`u}2AmSy}%cTs(MHa>U+nu%{nZ_FZuG2;Vyz$60SuR?HRJoN^h4I+WFDyYt zx7t7k$Ld7&1-oB+kM`SKb9DwhH)2r;k9x{-1XVr)^IJ286+5pSchue~NxS&1>`0@# z9}=K6uYYCjhk;*Y4ih;IjEw(dx>fv=t%|$)Z=fsY^9T5F#pGU7aFa5U^WLiz=XWcM zZa}f>?2)H@_rY|x>wa|oR%eMXXI3@cvm;$gx%V$gEdT4IuXdhj`dT^~%dns#9oD63 zJ3dy?NR=wT#(amKos*be)|3fl5$!6E##fM`ew{A(HO9sIa27S%WGxdF2=5(cEw_3i zjvANjFS_~$@&8Oah@~&-6+Mgr z2k(Zkt(6JSEgxd7zg(f>c1on5X3!Hk2>2Ai-1;u6D!dEORW^K~zM02!_PQGn^5i3s z9mHZovI5tKuGAA{_MYiZ4sp6RmJ} zb0DK$gD(ZBhF*N8ikK4I{D4!@ieu%NxW9wSzN_iA_iP5!0Hknl&2Y>*Mq;{UmWSGBTkJtOrZPq?k^*$Hc@uW~Khb73S4BRht zY%H7F4F~K0OUzo+avIDU6NSEHuFmtqJirQ}aDd`PD#6^$2THy7;6w|@wSMDN*+a2m zRXAe0=ek}DS;BV61l^)(=l-r1c-UtJSvf34vGUml7+ zH{U(fU~hEf^Rr|1#6$&`>|A0}2yE!lQenAoQEP5Qi6*c{@Bo}OEIJ;JV(6BcVi(_E zuEz9cO60^hSmyCiIxdJ!8Vl|ep~bvApFpig49HPmc`7e&36o9Vq^B!==f_N;jFc_( z_dAKO*u;;K3ED2vx0YGFYW_g_OD(%%(vy)=%G6ZSph_IjPsi&IAI0@L4OXrAi`+~R zVcb8EX)W6kwVr5E+|;l0uUG}a{U%O!@PTTjkE!o#G^$|5=E(BnQnRe_v!&1r=94^R+-CTzY20%g55-@Cecu?sCphB zPo+3ztB-*N5)9vDDgG1v<0JM6NH8(G7B6#cMZa>-)LG)a{8Tp8akLehsfz9rdlQMnC zmKDL1tNFJSdZ;}zLpzQ(Q(i1{@pu_`E_dhVwofJWblG=g=O+1=Gmfx6tlghy1~o59 z*8LTbe1u9NFCyg52$QB>Zf7-xx%jIET6^H%I(GEsn&%+BLIilf@8n%g1=$jG*!Ckn zf8NzBJ< z%2!U1DFD~@6;nu>Ue*;PxUx1Czusxr!FzqC~X z+~5sjUOS^d*Mr?5tqaJ5hR&l-@XjN&*BrGM%&qO8>x8@&f>1-}hWZc%0R&wm6^A43sSM2*NQ3y39v)Zq40hlt+t(3prReWO5Z5@1?Gd zgxYy*>m-G^A@3L}3-Slzvrrg~w0D4#4-XxmwiPPcJ2D#Hd}@LdAOY4&b1@f`joHet z%u{#+!L@xQ`XDnw73!n9jAZMyZJJ&Zv5j7N@kst26_;g-NU|`Xd9mNodYWM@{b$~U z6L4`RJDbW-v#k^8dr7#&?vA1tfxar;>E&()9K}B;HjX5|$^d%KR1XvR`wKQ=5W;5i zdqE=&UnhAH6hHfBX1yBeeZZg3WHY6bHT@VEMi}2=UUX4MD28ji*$HEkd2>3n9w1EO z923|LHsg5)>Q;V>T+l1oE&Ss8S9@?pUXuAFQz#Kmq{xu23-%8(fU6|&L&AO9Z z&d{#ZGr7MFZL0UU&Evyo{2%7MSZb4jB~`a(_|vaSpv!V;N}-w*=`UC}7y7g~ChqYz z!b#k=C4F&?Yh#_366IL=K=5(u{Ql8LOB1h&t`P@`d^7qW-b}}#ir4|LI9{vq2q9k} zAazQ6%Ld+I)10BPe+hHI7&>1EVA|~;W83KNVzn6u&VwHt7rQ~YV3sexEr0$u`Fp?A z>=r0&Mn|PnafInZWsYMuF5hjRU8ySt5JrUyiUNKkZNW?Gum$&%ph%M}gdU8Y@sb+Tjn#vx!#u@9?8V8`x?g zdkEM5D}2yvW@K?=;#R#|fae^zO3$)6(>gZz;8WI407-uw>K}Vc3=Nz0T@v!h`Fz-O z3dqi?pBmJa0oZQMhY^b6IRIuO@s2av80JO5vK{)*a~IOLb3q&s3pKd=XiJ*8(V}F0eV*Jn)l~A z{|4{NsN)RaEvwuj*6s7~b5VuCfyF6|*~gML$idoftSS2~BK}ulDjTKNjXQDuDNTEi zK{F+rEaUjMs99K9pUIXOyp37w6Q%NnlA#|UBMB7Ng*xP@KcWw`Zm*a7HDdm(w^WiD zj@JR{;YA`#zk^H=PXi@ac#cYt#zO)_N{17=$-SR$F)UI#6SvtG??&xCuwUyNJsqc9 z>yZX~7zxe$WZTUmuSVP}@WPrKBE@sx<|DYlGKQEV{H%2X;4I^ByAG5~c->C0ria0> zni7l9cKsI=v1Lgb4#o80?2Q*S!$6&v0QfCze?@tkd=;P@-9 z{!UvAOHv-4)PdYmqpN~}Cr@?_cVOfmT#YJ;D)E=QsC1V0&|v}Yt|x0`5;gLj^WuA< zdEMpF96jjjYmGyWr@??b{>f?Nz+dko~(PVi(EAvV(%8OL64r9Z2n-5z4q`;q+Weg}UqShLu?%z%sgvqrd7N|* zyVhfdlzM9w6^S3Zo(|M%Te6!q<$47K<5U{ovdW&HuG zQUShBE&6n}EGg7H*>LvsLw^v$eF`}p;9h&nrLSFlKq1uWPy`O%T|89rCWvG4BPq6A z&wWuc@})W&`kE!5o)k5tg1TjI;nFJ1n!;kp%pS?U(K zsH~d|INKwy$c=gGhNjZN@(RBO8Va*aK$BJ|?_R6wa9fGwwVPmPBenRYF$E(6NO8{@ zUPp_r+daR z?bc<%i-qKq5UeTt3TrD39_!b<9NwkM!29C^ZKL*)K`|Y$ontng8=9nnF_#rA2^*>Y z92^7}!Hd5XgCscmiMx9c9vf$0k(KIFn;Xvh80S|TzfS4z7&h{H2N!qAq?m)>Y};Eq zAo*i*7PDkGlU{!57+`rhGkkT)J+7S&lwb6vw{Wt_C@m=O8rC#4h!34x`vsZBbykX- zw+#UKk~r496kDNoa%Y^cN%u~oUc?$|0?;~YmQ@v+eE*Bmw~5RcpY}-45Q4~hDB>tVRv8^O&_5DK2+P;%IlknPGZNq6~X5O58 z5Co$<2*6ktKl5&8kzE1$Y1XKtBa7(dTA1@G)v8Pb?+B8&MU)RMwA>ah^$?YU5ZVGi z=0UNHNr6a8dy_`3PFSV_M<_UdZ+ydA;%^NopOGRRu+y-Q_$)_#$waWY-&_!J=+cjq zjxJ|%;r*Da0re_+pUrwRQ04h#|08jUJZf92L(|5iVBg6C0<}KV&IV_5oMEIyi>KY@ z9QEt%bd8@c6W{H+B+{imCHLzNQu1AW;8lG@Q#)6a0Ogjl${JWODYdA$(vv9WfSFohg*(>z?%0c5a6hHjKS~nzf&>sv^u8%jSQ7)XU)E!jS_rS`(@6 z{&i$jkg8ZcPP~%W@n~!;mRBG1$uXso0g{AvU&w}Gw1^A1y22H`#ddEExx$PZ27*-| zKF51%N1Z=}P|Yg|z?-NX>f}WiqyAB?Nub;NLa0+uKR~3!~&ce&}P>{ts%uu>Ptq zZjk)^$>0*-@O(4VDXw9r#g9gja5=&wc4EpXV?5;ne_lf0cOL-pP{^dCS3$4S-Y*`} zZ;3!9su9fOC7p@3$~yg&GF;l4U-{eHRUgek;5dQkrqrY%Nkdq2SNYic0SL2ajEs$| z&^t^v-kJFgE5f5V1#oYF&6E7C!KGTsDQU??6|^&r)bNiB@(^)pApJff>pKbcG!*m9 z_=Nd{{)!A$mv;BT$P27SF}{x|SK(CPCG(t&@O83b%1}P~p(PCwu>z_7Rp3s1ZMj;bPdXw=`S zfE=}txUf(Cxir$!b3QALWF&_$G3%a!*yG+#(w4DBn?yG;G48xAMZ> zXBQ4A+mT|TR!nw?SE*MLW{tkHOWM)QEPvXV={<-^y~*o3MKsB^J@oW{CSEB3%P;b8j6+ka{nODp4~=Jp6oJgL(^Q>!q(WZk}s(0Yo+g#`UVDdxb6x+jyo-TiN^e7+_^_lDg<0*ol-Bmn)(6Uz3h8_gusX z<3UJ2Sg0XKP8i19*6@^im__*0Q-#8=t^(3R%{HhW9YK8r zHTYcK4Z}FG!s(-8^b0op;Isgy#j|DUuM=wYG@}Kb6ErPx6tgEs?-r9CA)%43gb+Tv zSKS)7^k?Ng7QCCg@>TTXRFrQsWrpYsSV`odZ@&B4j;!SwI^EB;kx=Jn+2>tEAlc0a0=mFNDRe|%s&CE+ z<$mZ5U4}Oq~GJ!a5Sq^}ZsrE%; zzergMVFTtNJ+c+DMH1e!7e5-77x*?>P3QAH?oOw5YeFznE&$%fUE>|wR8zZ)BoHAk z&Puvki|o@W*DV^&`vbLQ^v=Z}xGh5f2)?KRV+{WMnN8vr?I&$m%Yv-#WWl7q!Z-u$ znVP?MXP)P=?E+q)DtWW0bLMb&Ks7`84}%!}Mu*WiQt7u~IzS*ixgHe66v*mVHa!s%pNhv6LNSTqP)zAai1dH^St?uU zK1Q7s6!EF&(xfXw6uLXUwupmhcXG+OBUVy%%_9GJr$EWNrO@?SX}AB&%)OQY-`s98 zG3sjKb>uEA1wD_K%Mf_1EO_|O(6g?jBrhKOEumxpd{hQv2z^mudKuIB*gurERfprW zOal}OvM1di?I1+Sp-PL|D$x<>d4~s`)6%-T2%!EHTTI6aB&NBiRW^0=N`+GRf`*H9 zXD<bBRk69?HVqXhbZ3pQ7vah9jN2 zLF)F;0Ihv73G>O%C7AIQUV(vb?H?Ki;zp_(H+NvwhUgK1w`K|o@vH!q9Qgu%+MH&wO&HKNwhVmHsRf{MldkJdKn1H<4tBk`}g>uGdIxx%pwb_*9gU%fPkvKM^BO zm!jD(PAGU?#idQJBX7tZm)y-+t@f^3N+3)_aR&+BC26&Kl+o^Yb%b@2JL~Uis;E|E zyydJP@5P)p@(&Sx02{&I$iLU1vuLl|{daA0iuf#IB>vHXE2IF>68pf!t7x(=of*i> zN^OD=#`-a5QE*n!L4p|~KWQD?EffPq>AuttqOxUCcH*7#Cx!{tlo1&X{#DnJbiMc1 z*old~_6k|WO%NwuUa&8$lw@QyGoEfoHmW#b`sMJb=)J}7=^-jrLEld#7!p|iJIk~r zUt9Z)6}O?-t&WJlTp&Nx%Ex?}!*R;lXUCS_-6v^o_Y%9IWQf zl4y6@CetbK)u~ipWhQ%XV|#X|T-2F{?@ytLZq#!CZVd_uk2*FKq&|v$9C)!vXPS=I z%X2lr=c0xr`1^oo)~M+f8kCudr+4+mHkkoGCh;vs`%%wf@)7pGJs|)EJ@jNWXMJ(+Ho-n?fvE4q_7OqpF2w*ypOP+h9sB= z7xF>pd+A3|5nTb5sO+xD_2AJ3{24(ta8k}=Fbn8RHK{KxB~WT>TMdY5R2e;Of-*<# z71@g@CR+cpoFu6$xDUo`6`$O%5ng9{H7v;DdN*$#^G7R|G$gQyK2ZWd zoo5MCZj>SVZ43yfX-!Tvl|-huc z&pd2q+u&x8DFVy%WkWFuiRVg0IlVn3+?MlQXAj&hqQy0tgtCvvo{XEOS7$W<9Z)19 zs0n*I==yw>hqLS+_Q=uc283k~HRVST=UO6^}g?i|H8$!HHoSlis?H z$W7w9H?w&Ok^OrO;AukiA0cNX9=_)C&c5)kT4SoOw9r0!G0fWR) zp%&)p&%l(@@9l(k7qKo%L;@J{>YgK6gCyzb9*&te1m&O8*_pm)O*%)L6o3W4{bC2kf1|@SUVNDJhKiV* zN%x6d+X3~+;mPE`cbVGDI(?`Pfm}5iFoBkMbMsC|FD-bsb8cX)^6OCvn4J%(%Bjz5 z%FOi#3H2aK4-O=5L)P9HUl-fa=-piG?(}2^QaKxp=wUHQYf>_D0#+ZhP@{EyzhckX z3L5WP!H)Sx%}{TYm~Ac6kTm!^iV5}DgouYo&C&qhaC0!^2YDOTq7=yj_>CMCLXfgT zWQcF@pI`c?fH`rGeeBzjif z^_$Sb!Khd5B>}C)ASy&pUelXHZf}}A>r$2wJ}v$5VpU#L$o>^og#cyVHZoo0Ll!$H zUv~Kjv{5eWX=QfeyO!kz zh?E2Z|9H&_#--L5tv+iQ#gQ|f;)lliy4?7rVsyNN0?ADmx9Lc!^OuOrvslCR(;MB= zL|HZ5sL-poAh^=jzuT1VG_7Dnv%PkX@@e` zHdfGJvr@Zs&(5E%Dg3F+4xdc+Bgau2uy_7gPMc<2f^VPE^_NE1rAtX^yIL-; zd}bm{HoJVw(G?~a;DcFv#v-z0gjNZ&oL70Yi}%M9|JPBTEQ0l0GwvK;%bezh#pP6E znq*T$HGaecdq%Tonng5+D0_zErn1pAY`{~ORZu~3>wT4^>LYG5>)g`pnyXTJOLEHI zxYX4w)U!7$DqjCi4;l<61Y-{!Ie+!WL)f{WcG54}(jSdf%ovYoN*=}O%s#DG3Vx0FxM3K{Ek^tKHjA1Vq>cUL zs0Xd;PwH9GwX|mx!~RZy`N~338Sq`{Pyuuo1s^$9RSL!5m_wcCmHQ!6_1r}v_EBW~ zFF`3KuskHys`D1J=YFZ_3#g`ERcKqql95hb^wX;t@WT59<0&;@A3ns60plr}29bvW zPJ4-}gmFK@*8c~*Z2`rPj_%r~oJE{Sc_wU@*ksc5Ulm0xWZ-Rq^Ie_IIA>Mbqi%G~ zD-)%6nUwVup;guQo!znA#fp}*I_~TH)N%;u9Dyp;r0QoT^)>q=-|y&noLD_VsoxEb zFfr*lq>b-hj98Hauwk6!P1D^c@V>O46^pP|(UvO_+0`In0 zb-~#xgvbdzxUGqOcrvNV|@MZ`57~Mt*9iRH+p< z`lDb!Yin2F_<(~>(dzKRf-Q?TlNa(44Ap&i`%<8_iGs?gv6)s+=~t-4 zuejiQ1I{{0nVXHon@r%J8 zV9R6O2>XToEq=xATK3m)#Lic3FDNwV6x3nGdp)4G*J3#$s1m9`WKPZfIBPhU$K&C? zNtRB^I0fklA&x0LBjV1&TF&AQf6>JdK>aUVQ`hXbm2a|KlL&JJy=>UYQmLA0Td8zl zLSpX}CZznAT_GQ4Mgd$)rpqP)+y3$L;-aSg>9g4u}L(GBDU)2s@2cm z`DyP-*8xhTF24(f8d2fTL$fMVZEY})!g&8Bii<|TR_+w{`^;g~WZ(iIk-tJN7n1Nj zwB~ux!V_1pqk=+Pt;#?6?k;!D2gcLmI8h2=_?v$9(|zv_8dEtS*4Vqq*}wQKF>vL0 z)&Rm{nbfF!PQDfmoS1(6B9H&;JvT2x*OGL#b=4YYb(*NuFzoQ_=U;rK*Q-Os( zX3{a~N-6)x;!e(EU=hVF@m7b_3Sf%L)sRS)G#GBZg0ERkA@M!Z_K-?t|6f1ORU~%o z>o}XJNspjG1~~`T;Yh_*@=At70~}FPYb0x6(!LtQPlh4gqfub$?fRKg?!5m0kxsGH zFslD`)(F_8V+zP$u4v#gY-NKFX(l);dQ?VJ{`MH|1}x*b^d&;zEMo2whgN3PPP`{G zak^!=<)}t`l+VZe72$6pC>*w?a`==EhUW+WfH^{)X#E?~k(BBc&7m&Fa06h6ey|0I z#e)`rWz|^oKg#GE`S>%&`4&+B&GulOS$a5i-N%mEW$>E$LorI$6h6@-Z?RPi1U$G@ zjWQhyOlp;XEg=^srzO+bqG zf;AF;y7If_M_*C<6ZGfoqYf@IJD1O%5lM$Jn(JeumTL&DmcZD^cRu)&fL}EU0fMAO zyTkt~HYE! zOAqA*FY?|lKM%}NjmS37GV59K6CM}N=$hPOh9l*Zk_EvEFGzpfZjlh88oimme*EoU zNj1lZ^mS)?o@Qcf;``ivt(Pd}42U;aWz5d6AE`L*^Tq28ls}?^^-2Xkf8p9kUSe_~ z@3lwT!}R~(W}ByTp*SWiP%X$a;I@#LT8Jo1?+6~StUJ)XA{DjDD zU(xOUSP3c_GGiIW$!}fEgu+tOYE$k~QXuF5SW?H&ouHT|^Je2YnEoOu9mY7HxzLKX z2-YlshMW2hz{m?J7tl%Rb2osfCM%!3GZXO49UGGq>Ds7{n>t`gnTq#2_t8ZU{RU%v zBJ3=2Pqx-OIerJdU?H1cHsILun=D(FRD0@GZD6AiP`Xs*7CB|OGicU|D+%*S6Zif6 z374TZHOu;_?K|!%a=4a5G!=dEhFAiArV5F;=z}QDOeNU0=!pB^A=P2eYee>QqQT%)*N*HsbCrW;6r(VVY^b-v9|r;Hsj*#EzA#O_n@yR~ z5SC0R>O22N%DcNcKKZHb%@-W4P;MDG+@=b92Wg+HF*XpW54)wr?I-@{Uf%n?tM zPuD<>uJI87h_vNrM(qHd!NeQc4}{OFuDXrBl|K8wM$-I;X?e}@nt$z5bZA$Zn^J4X z{WHO&s2pKCp~XGYZ%ITMc~g!^D)#j&i)RUv)e#sZDiCXjQHkVvblTu@gA+^loS}Ag zbp?*TRc?zXjy-NrMNohraRmaIS+P4)N`Fqgi}3qVQ3ezZkYTi}#Pcg?Az& z0>==f{c6m3+if_|0x(Z?us`_nZ~*{?P{;szb%0B zUX-0gCL7llOTq~x2nZ8kFXwD1sp-$7WyG z-iG2b!Q2$-yD||^j44XCnjUNN@Ke}XQau2=ih}?pA?&V%HeaSj;KZyeM>;3f-2KuS z@hhd|_=Lg$A84yO&I0Z06r#VTn*7y8-f1c|73`o0<-Hu1eSIyy70JfTuH$8Qv$Y)GyxesZD_Ag@6023aDsNvFGeI_tea{uR6ec)DTV&K$9G^`TctxR zWvd!zI_`F-W8ApfjI%w#{(h^ekqCk>z(@GeW>E*Q^(-$XX zSL~WhvhB=4{mA!Mk4heWurnX~Eun8*4doo=AVPZBOPj1*XmR?hu{c!Z>vqv%-PnRs zU-d-;P%aJyvbP}As#)9r!u>M(|=4jVti*yjs*+j%FIh+QQ3e8 zZ$t>WyHfkE?5&{YXTDSlG^1}Hri#%dsTpkHg>xN;ha!L<77d#WKe72h17L?%ItbFh z$^g@o6HqsJ&!lM2gEr&6`QKk77@CvmkXLXc@!h}o4gl0xg#LBTGK0gS~-Br$2QfvRCqMs2=#I_fq z=^*hkO299^Qt(cg1+MXr1-oZ)!TI>AHR7SQ-6KC?F*s=IUz`z!MmS=jjtjt7l?X#c zIOkR4K9i$)t=NRRe?uxgeJm|cZZgv8yMV+rMGiRH4Ou&5py`~~4OBlYV!%O>51FBu z*mr*9X;ePO!yCfJ73c{24`DhTHIDn=B+xq#ysrlRhId@W`zCaxXAb||-6`49ji(iu z@S5n1@-7=4K?$li*Wu9jfWKnp1Cut8C$aJzgAm! z<92XW%QU;K#8@}61Es&Sd~9RTLmKyX(&7r|mb!Nka@M&G`6NW7SyZLE-%KbkqB-#a zG7PkfYf`l&=WfSq%sIA(7lr*FT!BL~{~*c`BgbB1OG|E3-C##Fym4#z55egjmEtC< zo~-&EVYY#FUf1*Dzrs@|_>@~s;;EG0y%3OMLDj?RGiNm$+RK{T`PY=Gmt%E)s8a@mDd2^mJt?g&u&^ zE;r-EnAxiw6&51Vj#jQ6O*XLU6WjlJP zuY+4ui}nON&?Z>fwA(Su2x2;*rgNg^B}y6sdM$>j%LB4lS_iKMaGWEos(I7r!##3h zKJ%5D)Yy@i3os1XJZ~>D>TA~JrN&_E;^M6|wUbp4^r5X8k{0Pt!EbjI-74Y%+{lOM z{1aDBTJWxaRyKj$lq@B6@Z)jSU=xth>j^Z^6LUwc=-HooC0uigmV%r5L({U}m1ATU)LDd<-EKF+l(=Q3j!VCAhk#m87~Y2_LDQwH19jK|RF zOc%(xd}1||Am5X0xhC*$Zh__&o1y1PJAb-I*h3r;)fn{`76!Ad3ZfsX#ha%tWwArX z7s60!*gRWpm&3MMisPn3_J4n@Dn?Ll%I!MXJ2g*phRTpvAF#3ENZ*LMp6^k@GQ@+u zxR{Ev4vBPS+ma~8ZrXm+#zAU1uPiY#CHc z5wxb@x-H?ktQFc7RH;C5f{}X!B=7?bqb?4!Z%c)nb-3lUl?`dc32uoG;HMP!)V2H! zV-qW8cAL8(_>6s~24g`ef;D>KZ6wIaT>Mj%x7R`9ND`Be4ps*wpgJCDB#FnoG}22$ z@2V?zWdxLQ;w-6b=renuridAc3Y1!J8f9JN`rSNW&8>b-ie|mq6!;FJ@N!8^p$y>g z@2AFEw(xTiS#RfjZat+JF@ppqX^nZEP$lGDvJK8d<`5AP81>ubBYw?ebR+lZy?$BM)5KCVZP z?2&HwobF@ODNAE2(|#nEB#%=1Ox2KWL-!E^^1T#`8;AWA-o$UcJ~L4a=AnB;ImlT> z)|G!@f_G9i@YE{rP3c$L;|uSUL$LA#GJ2sPZ&5){BJ>iFPNK%_OO}(wF;B0s^m5h} zvGZg=InXIkpfya$g1Y64muH1Q$y zN;^woX3YOnKG?FMZsA#@;%jUeb)CX&DTjd`Tw3uE_hoz-!Q(X7?^V{dQ{H z%6^*NC2mFPi3KaIe2njjb@ugGG!dX$f={HBy32S? zo|A4_{V10&-^l#_^6keh?VW&twdpxxxRopE8oGz>Rpi}+fHro?no_A{#g;WAtoivL zT?^Q?eSb6B`|Q;^%gKjhAkp|n4F6qUC4;UbM10ZW^EUAC)_=0cL%tB>_M<$oSgx)J-^9AkEjW@uO~J-856=@F)#ZWws`GCJKb` z_C3sQX=91IgUj{m-a!gNwvxoSK?R>7lav2;bBH)Ia>TL?>z5f#`JOMlGq_D;p7t4t z5>Fc#A*(wm{p;pof~Sl8wQ`SNkMTs5IQf91F%wv zOpb_EjwV+)2fb*SR0&1R)>VL_;2?m|Z(C9mIEx3B^dYmyi~_0nmLZD@6NH>Wno11) z&~Km{%M!)h>sV5;FcT1mIvtndF`#Vl_s5yeQHM;lt%8olt5**uavEHjm4q0`oY-$u z+S65CEJmmOd#g$Pqanrukqln+q0Sdru0Hu?UC!Hy!d8ftr&6i&*I*2q#6&>91FL0_ zx#&b2>;z5n&w04Q-W z1(QWKIIf?z>@xl0PEe1M7ml_AI^Ii+?0R7mQ=&g1nS^Ga9X|^FT~etZ(2ah6&XJwM zU9;+^$~I65pVhUO^#`sG>|C{~^c~LV-)HhXN`H`q+LA5^z{tk$$$(%` zfi{60oZM<8tNelmvLi%Hy|=d+;eyqW_X-_!pJCucGwZgoR-?KVv3gp8KBq-3&xZpV z?>!^XHY~*Oxr{J@Ab;O>OiEIo#6)h>1u@~?ay$PM5uukQ+?SJ4ZOZh0`Ncem!VkZg z6l@h@)o1CWw0H=+H8E4Gl-HwS&a!+CPTYo_2*+hk(fulO@#(Oas{SC)?Gd8ZWFnMS zU4;FsJHjt|RL^n2klq5(!*u-H6Zg3OmY0_xWvw?}VYz+5eHQv=BtF#i6rn`_ zHsmS>xfAF*C1=BdPFD)=x6=7opw!UL)YG(eiqJ}~SpTf)A^LZMs37n;TV|8 zCgWzg?c%K3;?3eGEWFRxy4pmlO|o3h}7I=uC>q?L4NTVG- zpIv?+XEqHaesjzR*pkR0h@yvxrSxOfAIJv^7o`C_#bAVNvSJ6@>EIs}n}l=}Jd*0; zCq`VlQ11!CI3=;gSJ`h%S;-Yo4|X8WSv5=>_W^lmN!#bi^Q`2BeT*&DqBsLEPqVrY z%B^&cWd7KI>-^WX0Hi|Q&?-K*GUR3dKNK#vH&6^ zj87WOlgynSc)OASgTot} ze~7aTtcVom+S1fXZU8y>;C(J8jH94)h-jA`sz)^%Z=Z9~>f(RCL63{7IKBvMD>%aw zmV~+@s4AljJ9<~>0PL1S7d(|95j_tpVwIsy2fAKC-qzCL7;X4G&{^wvR{EbvD{qh> z4^`@R^R(rdLxAx$4LI%4rY;36zl|nB+G0I9O$nAPN`jj<+bx*i(~4-asRzL5sSA+# zp+nhe%1M^b$>t?P4G;$@76H0z8g`DB8*4J zcW-T3W9it6~k!nz|#@}nr@0m z2qYavo!k!U9Ey6f)8R`^8&U8vCS&wk_Xk@Wm~BxTH9nQDVA<>mt~|tF;E`e9z`-XX zZ$1M{o^)xA%e?FjAyYyKBw^cA+e9^y`T69pgtlM}fNXp(ZF+q@>xVF-mHX>COR3ZQ z{3Z(eVwxY+aq3#rE8a+kueMutN+5w<%z(vFBll2WK z>W8_49X`am>63ZB!}jr*Kt70r2kI(M*n%;CU6JB>yZdN-nO&8T;&4JlQ_jqx5n32Qs$?CZ8I3YqfG)NFsAg6{ua+1U#T zhz$Jkf;p;tLSs@3ZJ>vj*&|uKkSzwA_BFxkW-|DPB<e(8&zHDmJIH(!2umb*1M|AE+6wq@c(IWK~ zozb!^REve#N}hOJfnmTjAoQ@9=QJ%6b#nCAz(vEcPCFKH= zcgNp<8wX+GOzN@o$os3TFMXQs7j_@UjEu+>uh|Ok@GIwC7FVp`0N&hYTe}A%o(-Pg zc!LCjB)jZ+%Vy~DicMljyF3xa;G;#QkR)oKME5OjuRbv{`<`w#XhIDv?B*d4R}f}O z$EQR??nR$q3{xYY_`DcFmC?}cbiWjnBJs#fWJVH;Ghc`c|-CNk46se6`-7t)xa%FhJTHOudhVI)P zx2(8Akk?_10E&!);LL7uh|k6dMW&7$@z9S7{mPVn6mq7M-jyg%xEdu3oojIJ<|OCf zfufP}Hd%lasN0ccU|s1fse0wTXdJ(}k`@v^h@Z)+bl_I=tEx+VQ}&&27u_c_JyZ(z zqClRa|J;2cJ$RLLu|jW5bE2ytCOq4wikyt~?B>D9+fkR>mLmDZVkO>`AP0B~TparX z218F3rg+om>Ql00-3Kg+yyYYE*#C-$ID!P}iYv>9$fiK4>OEyBtQIyMph$jm7BiR% z@Vt!&Kmvm(#Q%8W*-pZq>p$rHcX82dgxIeM1G-U?+{4&2{?psK9}Gy*V0ULc*p9CL z2~X^#c_HMA-9GRs+%fPJY&Qr{gYe4&cS|{}E*i;jqJ+zrChc}mk63X{@JH`EFBb%2 zQ-=$-!XNrbDc752&J*|iC4x-J+5p-lZs6qcBtcR=HCoaBkpX?@52K@50HwWf`-LID zavYaf%tp-Unh;QBdvcn}XwL6vushO!o*cDxWlWdEZR3k*ONOi1)Y^JOU8zk8%?83Z77 z_Z=b4tM*RY7E2Fv_Jl_mx!}-GETI69qbzVZ_dSx>P7+-9kJh z%h3&&{i~;q99^V#doVkVW=i*-I6=4+tHTJM(%0p2_xeo`vOkSWWQ2uKhBN2<@I4V3 zZ{iXMHfyY$ay&ZeWKPy%=MuU=xwtpM}X-+`qr^EOmMA|36q~&^bu_2hD0y zCx=jJL8W}VycQ(=9u`xcHs8icWE4z57stK2^>9i|N=d$QfNMsEjgLV;Z=Y^OOn>Lx zuIeN*{8z?!PNn zl{7}SIMF-Yud`6bud8t)q~#!(laJq^nk^uSTauJh?}`TuiBI6FkVGy}t^Gi1jYSDV z@37upMK?3IuCrDr}WRC?^|EGATnms^k9Ltrdw>ymxM#*=$Yzzg%M zC$?h4w{C_6=ZnLB^^UV$XVdS3;<2zz@mQ!N5@zqT#8T5`#D3-sIvVYqC(Dw->g1>P z@nYWby*7sH&HKpsg168MdbupapV!CfpvxsM$_5|zKQDG{5}O`)+l4T~{gZM46?FVM zKA-_-X2aKkCI;t{-!yQ;m1+v6&fjlH3y0xjBm2nzMGwf8{Xc6|a|c>Z-a3WX~TXim8t&l9<_fRDmb{t;u#1d9vqdC5J~9oc&yD7ZNpJa6e<>5NL56IS4r88}$m)-reujZ+>55Lcj7}G<3&mA zl~L@+dtqeik$QyE>W4C?{SpqIcig0Y^zbSYBoc>X@Lx)4BCSb|K41adC*y_09xqg4 zB1xgyTBhFE@D$dizAsz@8hpk>zKz_=W>YiV`Dw8AQ^16E(IISo8DI!wEv>9rQeF0A z@fU8yWG4n(n4^An(lkA#6+`HZV^z(r=~k{(Y0cxyPD)wE@N z`q1}f#c^r@hJzai_Xkxe9#c&NHa-kswiqbsawBF}r+JCZf=n!*g-bu)T&HbZRx$5& z^vGW(IB=>(8zBPt>+`&JIszzGlo!=k@!A&SfE~Psza1|O7*OGmQil6TK5az6y%iFw zoo8OMUy-o8`Bo4YA64k=e}L(lY+eB)1;dye`;eSCoatM|uN@`xVFV+t zm!5K4{v41XHg&%+^XKSD=<#>{z5j$si>4V<^cj^`YObj8fYhS~tQB7(c;B_kB-Ks) zZBIvQnazd^#@s>%#VaxMHdJR=j!XduQ`yh%1mxV-W4bk9{u}s1C`;OF|A@Kf1;0KrbZbu4i0&Qe!D#w6iWLcbCV%oDnJb zO6F-v?vz~UWD5XQQs)iY)3BKqpWq2jI%Xp0pQE{xIPbNjr}pQ$Ks!wjN1tkTQ{BXy zrz!%?cS-ePCXLPJs=G3nQFodFgaaK9*{$DC+tOU^x5lA}Ba-PGrM7EM)Wj`sBN`)LUeZI< zIfJ`lf=K0i0(|X8)|yU#!Q3$^Hl6L?GF=AS=s`F3d2`?K|MXV#VcZ z12ob|Q%mLGTG+!7O=``bo!$PUo`^ySx>H@{0oU-S+svmj%2JY*pReD48O9Z|ecBn% zcq^%eOIqD>SQ}TJSN2^{A|En=@-YP-OzaQbOBs>au|WN-?B>k~M2*3hZ1K0-dHZ8g zS|gVEQ3$XrhsO8SN0cm#_vDTintqxJ1M^;S^y_sLwK)Ka11L`Y4R3y;y_c*>Td9)Su!+$$llt zI<2p+dbEFt(4jh3@A0M@$WqzzTB8}m))hrDH&wj%=DWgw{N_$eF!@XogL-j;HtP3< zI;?#m#3)hFCcOTz#0$c%JH^7CnUj^nlH4c$)DlELhNh%R_-17!I1A@HeNZ(oNA|rz zN3$*j-da>I-_m0wSlu#^cB|%Yr}$xDfo;zP*^br!pQLCWv|0!iZQI9n|5bK^onZm9 zxO2t)VHq=-sme9Jx!w`X)Odaz$Cqotl;qr%ZnANuW+rX^A3_g*tvkQ(0mgk$02QHz zd-(yIVwqWzokKf*)#lz@t8~f7wJPPRIyjsUS3RKj7FlQ^*KSX31Y04Aa^M6|CR`ME zjt|!c&`U*Ln?3A}fo;(vdWXza%sW#)NZqp2>$l;7$hyWX=If-t1>@=B20Qt zgT@eUoCMERgdu6Ih*z^RVLwatzp{*yssY>v;z&bkc*_7he3w&INlVk#EB#5G81hJr*C^7K2sMupZVA>z$xGlCa$vuQ#H=MP~b7p3i(ZAe? z0or>|k@IUrZh~msvNN|lFY59w9wvlo%XM2Ik^C07Pm05~MRG;icDAoOAcGItO%Ezv zC>{RCZE&MS&>W@@thxz2orSnnv$ua-UtHg9)zl9>;4_ZtKT4@%x!DOG8?f! z=7YgTi5h2EW10=Ue&d8$89U^7uHY=F{`z4F-qH6{A)T@mxncH872$~??jDXme_y1) zn^5+|OH4^2CF>3NeAQri#)dT!2DG+f0koi?S@70WCxZFJel{)h?Kz2oM_Sf$a|k2C z0a&fPXDaom5lF1;A?|ft`pQ5&K1zf-avDs5dGnE!0G@cd#e}ub{_7C!UU{%PHU5U&`6>O z41Fc^R&+?~G*xevj?lhUpivrM+5S)^9Ez2)e8~%mn^Drj=Z*UvYrS4EJ-08T4L<19 zs5QAvT-t1?v?H#sItnGdK7-g>d>M4Nj?Ze0cI<#zynf9bEbn~>)(nwR2(Dl==@czR zZdX80?_3mK^h?_$)wz!?xp+}vmlFQ9j5X3bSz7g#GHRieG>Bg}z;;02{T{man}ZSx zh2&x}{m6kELbMk*swgMrmF*Yh(F$jdkEi|+*+SyGrqA}~kgKUoR0iVSiF9l8a8>ha z^<_xBGzW7lo$VXkkvF!2K{j@@x5I|>qAW}ZPj3Lkl==Tg_f_ThkCt0v@H96!8>dl_ z2!BtY`PT$*7}XgEuEs?4?2i>Zt1{wPg+QZkCJ#C-Y{oO<5U}hKF1b;p57Rd8vIj?G ziOAFzzx_@xUdQS$dl)#jXk5ApzL}A_uh`;RgQh=7gpiJ-nRC)5I7QGu5W2&c5H$)W z`VXx_h5Ws4kwkd_k*+jJd{sm}WDE_Bx*iu`9$|rHVy$HkTK)bY{)0?YxE?79YF49- z0P9fp!a>_buJbb=d4j7mF9kJ*YaH9LO;%R(dxmW4kfDiJTrTJsyw7p#Q;$`BGM(D! zaOft4;Id;dXDItRvsU8`Yfh*d88#iu#T(*4l>`j=2!$Zzp-r(q5eX6f8 zkG-`H5mgd;$f?Ksf__!~>l?9hkv@r?CCj;#ndto+_%HP-#sNp$)fG=g@QI{4`v-#Q^`hN4_<0chm$S%u&QSam-%5e4r*nNC*m}96e*m#rub_SDHa^! z5;|nCiF(#0sf^tc!;d&QG7uQzUGV(+Jwx2R1^B+=3o;X*isyZl%7gwQ{I4Hqe$kBG zX80Y7dJTxe-Y>;K8WDeEe(yu296l-)uCT3NsinOn&KD6yfy)T+^5LczgfXH@NI zw-_?fvcQ>HfrnlhhqAD4V^1SaAC4ZXVo3ALSm!^*6V+&`6IPVRAFZ=JaEqcV0+*lc zg+$v|y1IVix8r`z;%cXk)0A0Z)jcsE1ve=pQeTN#q|_bj5qn)}o)i;njzzkB}%Ien{k7oEx} zmGt?H*7W?uOZyK zNry4Ea1v%%`tX=$nNNh9eOBN8n@?<0l8~>%?9t0vATo!cTG^?Q9Udq)RoMmQ#R9cP z@d_^mwkn=VqpuL10eg1KKA-4cOyk>~3Mu&)Kw|4FDvP*@Q=af4`MS_7+y5Eu_Zibp zg{3glM%MbkOHQ$2Q~^hR$c3D?txaNy2%HIYIE6$!bi^i(h4$FU;o8b^JTgxDgR!b7kUX=Gu1r!k8c;8bcXs+>*G0(VNlK|?a7yN*zIBRj|?mQSLC1x zdDr7HfWhOk(8Jro)rfAibxMJCpDlDv(7+UhHU&Ph!k19X;Mbiy8{G&-Fi^`OTdB~pG3uuAoZpu&QA%6p_C#Vz0A zGhwlQxRKktj=gYu*tZ(HV0$yip{lZWZ;qMoDRLi#zd##vpdU~?SRFt+*RqKUJ282fkh*omVyu^>yiOd4^878o+NQE$ zX5yq_a&2;-#ML0!fnyC#R+==qOt+6$Iw1!n91=VF;@b88iJvN-2oUmD{tiWD0JNz} zK>sK?Jl@Ag0KiwO47ed`MtyhHeZnRX2805N9*i+%nwnH#Yek{ z$HqX4kSGh8ztzx8r~@v|U;}wMl*Z3Si#cgr=lb$Pf3TomR^*p3g_Xm|WnhY&E-?W# zjz&fag&>z{{3twv8qD*%M|V~why6uso!j#QFbH_)VWqhhO;9Z>m|fe2?0$oipGJpR z;uG}`T~%5d?FZ8eHk6^WV|ONEE^SE;Q?A=Y6%(VVB%sCEKYPo#rE^6g*)@dKTtI3u zoJ-!u@K|KVoK!@n4iSae!;56_Tph01u#EF_y*9lRzu}6I2dl-imWFo=0KH{F;{lJA zra5!a)*=xA1o8*sq~e1m8J(ACC90L)HyM`?+h*P3{catOZ^F4v<~yBj)zaM3NR#Ij{IK;rYkJ_3=>!U-P|BLamQ^?OaD&U1azCl!}kRESKhl^yH276 zUqAuhQ}!cf{D;}HfH|jx95f~Yn8XQXK4iqmz}+eWMq)Hbo*_2-)ij^kq7GXtAGPw( z>5958Bct0`QwrC4HgQ0o`h9olP25jNOdNjXl}lP-lEA6|FBft=oS1L{**ruO#U(aIuIVgHwU4MBkL8C~h>kz}?&C9x#9a_loO z&V-W%Ea`Xo1k4)IVfohzpbqkf8+}Eds{XA63yI2+<8i@-6MXxPNbeS;FmQ8@A5hGu zyEAGOxsTr9`8%WG(V(+84eZ#1(*?xRC6S8NWk7`~O9(Qa;Kj`&_>lv$T7CSQ9%>G6 zA+KvaG{9IPHdc{7SZ)fiBW1)mmPG?;2Lmz?N{S$~0k@mys*`kGWGTaqF62>$+okGB zBvEJtgVD%1&hQwmnF3P}w5~O6z#(a9VEVjYt@NA$s3#;8T8NX~k#YR6YXx993Qb;j zLXtGp$&`-Q=Dpu{)|CQy2eTg{MuZ_Me^909>a%3^l{2M+V%$L|%IMz51WzuYy>;4V z`NQa-hT6%`=n}^R;TRx{<#}Toj+mcKt@52$5Jp`QlXO$druG$loMmCG75@24f%6ikf_SlzorBmCRIQJ>pX&vL5LXZB1)M!k#o(FkaF-~ zeM+uiJ)3_Wn&I#6#X#=6Mm^$~)V+DX5SfYfUZ)M9KoUa)H^>`b>ZnZRW$XK*VYor9 zNu4|j!(IkB>JCj;aKSR6S-_6ZB}Q!jh{Bil2P3VsrzEy)1E5Rz00&l@O%b52-Ec24 zhh@p70lD~=j<5zEw0s2|IQOx+kps66j$j2M;}^Q~!0(iciTuj^?3M(Z#Jw~hhx^Kl z>4qS_cW6fg>D(kx-p|S7jb;@-19BID-0NokO=nqPHED3E?ilca0`fnn?(CZ-pe5>Nv!Ive_Gl_yapZ@pnm z8Vu?uKspW!yc0$4bu9#kJyJhsT-VN=oI1YgLzH$D*>yp-CXh%LqnJe{c0Fl0#}{)8 zS%36b9X-Z27C~WAp#j7kRcrgjJgp@#2M}5i4T{a--pIF5vy8X!Lu_oLkJadT+_RN8 zsTeHkC+%2`r9aAzy-?lXsK_Ov_W05*_ESgTv>9llfxz6c4M(N}?`hVLu@s0ep?fk* zV0}%4a*uSI|s`}!UMM^x+#GYssMs`mz_Xslp zVlP%+sGlY4W(ezH5=#oNOc+xLeXeAm#$#|Enfa46bLc@hO98actwYFmIp-NbX5<~m z{eb2l-LAQFbNBP(BQQYOCrL!-O%U3)uMiV%S7 zb^_&BOS9m{)UJ?aV4aGG9i8_9~ThJtd z#BzdXdKSYf13j^XJw%y9e9dN%WknK{aCcmUneG!xpB%eg{i%v*>?Ys}b=;AfR})Ow za+aUoyv*}%Ft~xkT3$A=I4*wey`eZ*VWsao?I|i=U0ayq4Qn!LK;8(M@GY^^meDxa zHSr!ij5#~_si|Uz7AVvaecZJ%;zT!D6S(&!O>~rC+PqFY5Ho6C>^?WV=CqJ;Ix63`ad{B@>S>?Xn$Z%%j%zDK49L%_e=?;>w1s4cjLD;Ql& zYZS;dfRE4Ul6wSfjdR#lPDwYe{klJwVuUJR+ z#cm=2Q-nNl)lKV{C4X}!eA^Hi^;gV{7c{K{yg4a zEx1w9OU8ov#5gGrhW7>UqkDwP$L$I}=`ID`DC7cnl3~|#CzMqfJrV8^QvSw(&2qx8LH?J48UcXZw>0Vk;gSxBX+U6s?)`A;0hbo<#bHg5sD}CSfL}_?%sZ;8OwFzpi@EF=3BN z(xBh{L;apKQ@=6$N2@n=tc9|EC)s!u_=><(X>9?Hx)1k4%0c*KW5c4;s(#JxAR6H8 zrFZ3%E;uQJL`cly%hxzl7I}tFM~crBc#7(8PI>XGddtT3&qHfW>FB~!E z;jy~E<*?GWniglpqLl_(^?}$4IsYXd_soN`Sv8>fu};*#y=za#x2m?WNB|WVx|O3e znptQW+*ns>yQz?g!zd%Z#1xQQ@;!l{A`c4Cu*sVEiho|Ktr9}nU0!!Nd%^SVv@S0dMUjfv_KpAxj=)ZlPt}^+8+iK()IaDiwf)V1!bA> z5wnY_F@@y-oGS6G-dor4p&&PyRl*z2@}82;-a`!q*+Czz+V}3-eT)uXR4f^$;Chp- z;DTtZ?c&lGp#2r`+uqhs=b%YZpl{^jZ37phZuIsbzha#(Gf2l4O;veMsLjT#8I=x_ zcyLZ>v$$M(ur>!P0KO$1m~5xzXYOG_>B!MMv6j+yJKAWdb!W}?2v-VI)xlB=chXN0 zX(~V*I^^0d2Nl}n6jgVBU>zA&enQX^-B3#N6uYzS#Z061BgVc>l7Oas2NksZW7I&F zKn|uFhl0^RLp_w+h62^jey^&jxqu<^A~(g#mZb3pk`R&>razs&i3mGjs;+ZZjDdER z9cWz;alAooDmQ)$fGON7ke6K&*t{AT9ikoMexL&Oj5Un6&PNOJ1jT%K>QP{OkyALx z$DU$jvoL18JzX zlhvMin@?qPExPE|97rXIu#xP)#SS#w+&v{?7;GlAail}N#&Jj_rMDdssaVQn4FXSA zQ?liBUQOC#5P6lEJo@qD!HooJn{hxsK#w&{VzBP)`cr!^*z{qnL|ybc@v zJt==OEqBbT4XXCd-5(z+IU+XYAtN7pUG!B!U$v5rL>$_D$WjDJ$q7!vb}GXm)0Rn^ zv!wuP``q-U%+|ywoDYKlcNH#SWA08sb^c_GI8PmEh>c!(43*E z>gKK5FEsN-TM}4YXIE8a(+su_(H@H8%|Z%}SD{Dlmw!oBPn1%k5_iO`JKGjf<$5{ot(4lX_^6w#gb)yTR zGGkYsdE#UHMU_IZJ&w8A3W4h9&$KxkRM{n*<$p<+&u|?P#B=m6p|VjD0h|D3-oEe^ zO1$=vd3bi(VX&*Yvg8O%ayB$YeW{c4|I0vUeXQb}rd zS42z^)*#B~Uk@p}=k*{s*??FZMu%XW zmkLU^jWBi1*MCA=+LobhiO!g6Xi?kcv}LZP@n`_t#=Y%9S5GfLW6q$2!-tI-CcU<} z<#H9^pYboM-1YF?lO?9K)wusfR6aD%boU-ZBL^6Qze*hZ3`$_)ZwM_~nKW58?8n|mCndmQ`WF>H zovb$V^RQO$h}oI1HkHq1#uB2Z;?%$*I|{9%_2#5sAXrH-)HqFNY@wwF@qPrSlZk#` zFPOF6ai97Qa;A0?UW__x()~_i7SqFxDleF6u0n|AHhOh5pB&EiEgxDyo`EH$IcYQJ zU?B9H4$w#KWZFtWo`UaX%AV!=dto*~$oETR#cP81+|0PX0j&cBgWeqT$OVz~6+fB` z#p7hwS82d^4V7FYj4=vO&A7Q??VuLwS=*I*dbNv~VPz0y%Zb?v)UW!BDJNd*E2>fo0-fc% zM1K|Q0CyXYM^yko=%qzul&TJiG zq@+_Li~*AR+K_}Q0yUQz(M&&x1Ruu$763Y%oPF=W&)qhs|FK*AvxGer3^`5kxs`+o z7KpS~f=?|T2fZ6@2<8~Z#~3OlY{BT38@mhQ^ob!&58||ZbQ1c&6-7@n{cwRpbM5!^}CsMWIZ zU6Jc8sci?#VQT)@H?T$uUs z2h^nz)z_B`y33`Ju{oq4*`9*WSvF8PFuM}y9mloH5FZU5R59We^AjgJd9$=V=EYRn zYVLsNDZ~oi7vxFerFk9;UR>3<3C)3>~JufNcPk zc>kFxB@Vg93hqv`vl;H~Ik9k72IugR0KBaOD5SCKO>u@tsExB5M(__EZs=?bmp{r3 zv6Zh`WWNvDd^rEe{&4)+U+BCCU9aH((A)jr5(f?`E3IGW6titY{fi_m^<=%TnH(jJ zoJ!huEYMO1XQXdIdm61-OF0b-kaVnPc>qR4@tl){SIegJOAT3VC5awMny|t}ik6Zo z|zd-xo0 z`SgO~%d{)fmLV{J{=Zh4S#92$CrC}Ti_a)A+{v{0PQ)lM-R#xaZ?$wP^2ml^MO!5c zS1CcVoptbY2<1Q_;@P7?y6ZZ=F!RG6q0=h|t78XJBO+n-;LdVPf_>$7Pq4{0RF*@Y zaHjVYBK7YtQCb=iYIk{gL>5x7jIj<1=#RB4!_{w#;djnx`gYaxv~R#!;r`+lf9zs}j@R zbifyo{v%wb1fM|{snLRCkXhk=^>1#+=J%<0jkBYbIA#c|S_Px7spl@g!o_lak;L}q zqeE)!Y7{}BJGhssavwzU?US&B7FMRu}(s&G)HfwuLTpg0wKmE;~mR23ceyhdXVVk~0J6PDS=*B-R z`neLT5<-tc(&_4E&lzg*pN|09R%z?m7b*cp`~* ze7HBg*fIEw3OQFeteMfx$w!9zqCr};Bx+`-^j{41bRahY*hjIY?T_kL;jv+{Kq(K9 zYDPqwH@ZnBaLyle2~$2SHeFn)p})p@E=WktuXzUsQY_vw#YQYskQE7@CQ^nH8yd9gepui*Y~ ziatr>PeoO75mnYXkeH0KS-H0_ij0FkmZ~vz|F@|Z)6G2IdD~?c2)e@qpxF~&9oC{t zOrjMwzaih<+b7r|1rfu(kYEm^iH~XZcNp8A+ZEdy+IuZOGXZuwd@<@~2GR?iqDqD= z`8eak!3{l5VZ=&NHFinz)Yi%(h)e#Vf)pF=q8o1OWA^{Ee{}y{)FCDBSk^G>h?ZW8 zoZPry6ubB-=poGBY+dUpAW;PS!eM2cg>GUwVWDs;m0#q-lU0Qt1F`g|KAY}ri|cCI zE*_{ZIyNd+=h4QMJ2K2bj;~=iWcwNV#Ck2?5&OQrM!^2xx{yj0Ht{-fJVJHQS z_$qc=m>V)bBr!hI-lf9>ngXQu2$zGRXKbnPy7AE4nX}*i=n0BYU}65kXYz)2e0^5H z2AAHoq}1C)ghJKYW-J=ihpH#OpR)c7gD}!Y5f}JDNg~$)0;Ai~ceLqFNAw1Ff<9j5 zd8V6xTef=|_=s}02_uji`KYL}J^k33qe9)F@q#(Qh%0jIh0F+uuWH(APR3Wn^mKM+ zHySU-mi#D@;6Blx(rLp-EA`5Z;TZ`M9unV;WiOVvNR*T%eXV2F;V#4ZuTLSe%)3=G zs8&jDm?xN>&0L%B1a>5Jk+r|JT#Ba?$M0+cf+k3hVd)@gZ`UU)CH`9giOumtW&%uw z>>ELi<8PjZ2X+tDbVM%}1)#MZx98U*4L%?-X-Mt8bu@(9Fw7!Y@-**>(b#b~|0SWX zBJ$s;9z~ct58Z76K*i9KD^-+59^wAAl#MQ!zM?}%H0jWRj5Cjesuz-xT-kjk?&DZU zA99QAww7VsLzQdF892NpmDyglUKr*7B|ktAs{4s|{fO#KCnI6>5pd7n>VF>|8r}D~ z<^jm2uq494X){|y0hZP9iBVO`UqXh~@Gxb+%e~7RGyGU!Q^C1nHeACDO-`KvBt@xM z6H9J=WzafAe0io#E1L1}S*ExtjG8-cF0`3Q>|b3%@Ss8k4N-dDtuQw2TK#5l#W0VyF6CT#kjzGLC3AbTkw&24Ar`mG9sy*Zk;VCQzf_Js-z7$`L> zt(A8MuA7W=u{mv3mTqmOYB}%<^}sjO4Y0SN3&B3wvj78X^P>**NqxmCP=YF1+D7%) zFT~iQ=Tn{ibU{c?l+Ff*K5BxxPgxt9ireauW(m$%x3i_kLo7uGIr1pXJ$6LtX8eNx zZPyyiqvDG()WRKCQ;(-$uAsY9l9-qYO9hv%KftvKo(qqS}UR#1_*mKYS~TUS5un_A8Hpo-j9L+_`|+#~{6OBY!f1tWAZ)z~~Eg zq7)5*mzB6*jfB}qBfgKk0#XK*wuT=qrvGw_OZ<*f2moT;AvYlnWC4#+D@8X}?V43x(% z^zYt2MqUwms{?n8{`@PFfrD{FM^)6986cf>8y{jx@Z2a&pFpV(RhC#FnbhWqfX|*9 zc`j+w6$Y6P{;gl%R)PzOVMyc@w#)igVtg?e$~}i89H>Aqg{>@G)gJxF+1fo5vA&Fc6z3EsNGcIg^7$V?O=;%i+J9E2G0GNPz+e;y|_N9P8)S`f|y z`4^Fh%ZkRtC6U?eM^oZe^YOM!ixVeOu$uh*)WNevvts%(Tvy6|r}!sU*<2+664+4y zLV)?pGk61~{KJ;z&m>r2nAW=9?7!4+0eo}EwIrZR5%aOu$?6z?{1_!KQH z?=!aqh}}E+Ged!%>6t3V*1$pA@ehBB=*iB}L+F|KNmu_dfKL4&677k8%Nk)*scG-p zpZ4=NmBuV>2a6jFuQm5#HbUBko`(W3OMN6`+9%>^LgA`0mZ%&3_48N4D@G}O=;!Oo z@{=r1!5r``ZE(5j8%8ELR(-ql&=6B;xO(8UT=cxV zbB9*$mF+8_`b)5#P}debP=6nPI`k=)pqAMiilXeqpK!dxgmuf9HdM)u&_h7~O886q z?`7x)JnA``(XqQ=UG@uA*z>~|G3Bhlg*h4b2%P6ZF7IhH+Yz1|t){cK0TG6PBvRq` zjrloF!z;85yN+GXh~Em0LRnj? zd*FP_CDXPIQOl1e2zYk#YO!K?10 zJI@INWHw~_OLLIPT{YX^OvfAO%9gp>{R3H%Pg_twjK3%Ilzc@cknP zx|S-)Fd!*17zm1%P#%)Vs1BA1ttIota(WttG)wri&DJom*i{OXe|JU*4c@7k)Ddas zh>Ii4+=kz4)gr$660{E%%GA!p*~Q7!&=&T;k-d=>EGHWQ1Hu0t@bJ)!S=zXmI?;>S z7`m8>m>S!gn9|Fb+L^mp5HK@v^6~w@iBO;0SeuUAU~{qPj(o^M+im&~!MiyVvx!y6 zbQj(v^veoLJ|X$omvw*G!-)CHYJbVSvUhqS4ZVx99-htSK$yUfJsQQ9uW*)TP&yZ6 z6ehTO1?P?iPA(Mh_oR@Jd|r({=v@ALWuT_AHMydM#r*C>e%Q!X#5;C1#SC?A+~X~8 zq3~h5+ArQSx^jD-_*>Yl?y_)Luovx=LHW?-3Kx?*Hi-x;yi;Cn6}|k?W%t-xGkHL^{JB z6f_xlR4H|zia`h2xQlUvMh{9|iKJ-%oAm7eKhiUCu>X(rFYnC~OLXQgk%PJV2a3H0 zk$qfLW$;Y#K-21R)Kq{x%23!&{w@@2&HE;@hi?vED!~uV7y?v9qt>qvVY}qk) zAq*pPL-`%pGYNSKENxWNf~@$~o**`KHP{E0A(!L~;D;nMXN+sVhdr0@X9Z3t4O0M5 z>gQ;$cdMU}(M6pJ`K=z9=nAJ<|D`c{FpZMD&Z=-0IwRDSH%n08cHOOp!j!#{{woDd z+st*IJ%zo&iT5h~-$ds4{}Gv)f%$(#?r*yj;8f4S(`rD9+h`odr@!WV9M)WXy zXg^B`O9>bnTRU1*T<{IPOd*W&e+;4q^{nFw@tR6V9m&AVeSE?inqwV`R8;O z!hS(aJ$d3L|FhE|j!ApcahcDmgJ^&DZeTB@#;{TRUb+!sE8|mP`<0AVv5q3jzKIoY zvXCdw2rrVVUXeZzW8--IzlqNI|06mpJ10By|2ttA2^blfnOT_rXBCc6Cq-Z88c`5B zT7_y}NENNuUW10i(NQn(Q8TNZR0`!~XfzVDK_d=D_G=ayTNPJYOHn!(z^a;C2O z8eMqtkfvm@68Q7Hso4?ev}$*nd^V6@X#D5`giA3>#%u=)^3sI!>n4LT6zJx{GcJGRODnNl& z^X+MrXiI|a*NEFV6&Eq$11N}3Wmk9C1b@<}9-n1lifm%^xw=0YBnH0uTUCFWHrJjn zXG0Ta4kZF|FBzk*=;m);=8-r0NV)JJCe<2dC(NPSQBUqD*kEfjHKc^8okf%#!X1H{ zyx-A$dEsBABsTVz;;T(3hs+ijd!qYIO#KSId6= z>u?UyjQ>?t7c0LGV5^Z6&z!~pclpv8!iPu^q@o4=TItMWXU?}U{CD#PK)~GruHPUb zHrw$N5=Szl4KNaO5a<6-;eZevLjc8xVBZQ%zR&EVK*K)R<+1GD%;GBjU;tLrOufDy z&#Cr-v1MWaXBiifI9zn6yQkMn%RYmqhy;#LFVBvvSziLRQa<5SN3)JsxFGxU*xe9I z{QSeQ@glyh&x^cPg_~y~Q7ESXfr0d5cJH)>jM3hI0KB~vY)JmCc&)%|o+^0k8mDAD z%=|4W*H6sUtf&`_5e2uEo#)?iTkBJ$#eda#8dKl5Km&rzMU2qKL&t_Jjn82;8TU(T z0<~>PZYeb=iydSe@G4L1pbLi1!!+pAcrk&{LgyE)N^fTheVrBtbou|9(efNB#ZySL zfkXejmA6>EgIYLLVB5Ef3`Yxo(G%|!ZJ8>=;t;(oBgQPop8D#ZzGd)s4K!Zx7Rd#E_8L4x|!Go6rZ$jpAHSQ-x1I%vFd%dfjG?Wb8YYUq_aa@V&$7sm8wd9^npQe-l0m%=8>v zgq!ju$xY#ah+PuLkZ03~Au1=;5 i3zU1CWPU&=?@3|`O+G}W5CL0q583QpsUY5 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