diff --git a/.github/workflows/trustzone-emulator-tests.yml b/.github/workflows/trustzone-emulator-tests.yml index f9acc6744e..43b551bc59 100644 --- a/.github/workflows/trustzone-emulator-tests.yml +++ b/.github/workflows/trustzone-emulator-tests.yml @@ -8,7 +8,7 @@ jobs: trustzone-emulator-tests: runs-on: ubuntu-latest container: - image: ghcr.io/danielinux/m33mu-ci:1.5 + image: ghcr.io/danielinux/m33mu-ci:1.8 steps: - uses: actions/checkout@v4 @@ -27,6 +27,56 @@ jobs: run: | ./test.sh + - name: Clean build tree for PKCS11 test (stm32h5) + run: | + make clean distclean + + - name: Configure PKCS11 test (stm32h5) + run: | + cp config/examples/stm32h5-tz.config .config + + - name: Build persistent PKCS11 test app (stm32h5) + run: | + make PKCS11_TESTAPP=1 + + - name: Prepare PKCS11 persistence directory + run: | + rm -rf /tmp/m33mu-pkcs11-persist + mkdir -p /tmp/m33mu-pkcs11-persist + rm -f /tmp/m33mu-pkcs11-first.log /tmp/m33mu-pkcs11-second.log + + - name: Run PKCS11 first boot (stm32h5) + run: | + cd /tmp/m33mu-pkcs11-persist + m33mu "$GITHUB_WORKSPACE/wolfboot.bin" \ + "$GITHUB_WORKSPACE/test-app/image_v1_signed.bin:0x60000" \ + --persist --uart-stdout --timeout 120 --expect-bkpt 0x7d \ + | tee /tmp/m33mu-pkcs11-first.log + + - name: Verify PKCS11 first boot (stm32h5) + run: | + grep -q "pkcs11: first boot path, creating persistent objects" /tmp/m33mu-pkcs11-first.log + grep -q "pkcs11: created persistent PKCS11 objects" /tmp/m33mu-pkcs11-first.log + grep -q "pkcs11: success" /tmp/m33mu-pkcs11-first.log + grep -q "\\[BKPT\\] imm=0x7d" /tmp/m33mu-pkcs11-first.log + grep -q "\\[EXPECT BKPT\\] Success" /tmp/m33mu-pkcs11-first.log + + - name: Run PKCS11 second boot (stm32h5) + run: | + cd /tmp/m33mu-pkcs11-persist + m33mu "$GITHUB_WORKSPACE/wolfboot.bin" \ + "$GITHUB_WORKSPACE/test-app/image_v1_signed.bin:0x60000" \ + --persist --uart-stdout --timeout 120 --expect-bkpt 0x7f \ + | tee /tmp/m33mu-pkcs11-second.log + + - name: Verify PKCS11 second boot (stm32h5) + run: | + grep -q "pkcs11: second boot path, restoring persistent objects" /tmp/m33mu-pkcs11-second.log + grep -q "pkcs11: restored persistent PKCS11 objects" /tmp/m33mu-pkcs11-second.log + grep -q "pkcs11: success" /tmp/m33mu-pkcs11-second.log + grep -q "\\[BKPT\\] imm=0x7f" /tmp/m33mu-pkcs11-second.log + grep -q "\\[EXPECT BKPT\\] Success" /tmp/m33mu-pkcs11-second.log + - name: Clean and build stm32u5 (TZ + wolfcrypt) run: | make clean distclean @@ -44,7 +94,7 @@ jobs: cp config/examples/stm32l5-wolfcrypt-tz.config .config make wolfboot.bin - - name: Run emu test (stm32u5) + - name: Run emu test (stm32l5) working-directory: test-app/emu-test-apps run: | TARGET=stm32l5 ./test.sh diff --git a/config/examples/mcxn-tz.config b/config/examples/mcxn-tz.config index bfe4e6db6b..c3d3d1f2d2 100644 --- a/config/examples/mcxn-tz.config +++ b/config/examples/mcxn-tz.config @@ -32,23 +32,23 @@ PKA?=1 WOLFBOOT_SECTOR_SIZE?=0x2000 # Default configuration -# 40KB boot, no keyvault, 8KB NSC, 64KB partitions, 8KB swap +# 40KB boot, no keyvault, 8KB NSC, 72KB partitions, 8KB swap WOLFBOOT_KEYVAULT_ADDRESS?=0xA000 WOLFBOOT_KEYVAULT_SIZE?=0 WOLFBOOT_NSC_ADDRESS?=0xA000 WOLFBOOT_NSC_SIZE?=0x2000 -WOLFBOOT_PARTITION_SIZE?=0x10000 +WOLFBOOT_PARTITION_SIZE?=0x12000 WOLFBOOT_PARTITION_BOOT_ADDRESS?=0xC000 -WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x1C000 -WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x2C000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x1E000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x30000 # Alternate larger configuration for debugging or ARMASM -# 128KB boot, no keyvault, 8KB NSC, 64KB partitions, 8KB swap +# 128KB boot, no keyvault, 8KB NSC, 72KB partitions, 8KB swap #WOLFBOOT_KEYVAULT_ADDRESS?=0x20000 #WOLFBOOT_KEYVAULT_SIZE?=0 #WOLFBOOT_NSC_ADDRESS?=0x20000 #WOLFBOOT_NSC_SIZE?=0x2000 -#WOLFBOOT_PARTITION_SIZE?=0x10000 +#WOLFBOOT_PARTITION_SIZE?=0x12000 #WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x22000 -#WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x32000 -#WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x42000 +#WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x34000 +#WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x46000 diff --git a/config/examples/mcxn-wolfcrypt-tz.config b/config/examples/mcxn-wolfcrypt-tz.config index 60c88710a8..fd8abd12ad 100644 --- a/config/examples/mcxn-wolfcrypt-tz.config +++ b/config/examples/mcxn-wolfcrypt-tz.config @@ -34,23 +34,23 @@ WOLFCRYPT_TZ_PKCS11?=1 WOLFBOOT_SECTOR_SIZE?=0x2000 # Default configuration -# 192KB boot, 96KB keyvault, 8KB NSC, 64KB partitions, 8KB swap +# 192KB boot, 96KB keyvault, 8KB NSC, 72KB partitions, 8KB swap WOLFBOOT_KEYVAULT_ADDRESS?=0x30000 WOLFBOOT_KEYVAULT_SIZE?=0x18000 WOLFBOOT_NSC_ADDRESS?=0x48000 WOLFBOOT_NSC_SIZE?=0x2000 -WOLFBOOT_PARTITION_SIZE?=0x10000 +WOLFBOOT_PARTITION_SIZE?=0x12000 WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x4A000 -WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x5A000 -WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x6A000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x5C000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x6E000 # Alternate larger configuration for debugging or ARMASM -# 320KB boot, 96KB keyvault, 8KB NSC, 64KB partitions, 8KB swap +# 320KB boot, 96KB keyvault, 8KB NSC, 72KB partitions, 8KB swap #WOLFBOOT_KEYVAULT_ADDRESS?=0x50000 #WOLFBOOT_KEYVAULT_SIZE?=0x18000 #WOLFBOOT_NSC_ADDRESS?=0x68000 #WOLFBOOT_NSC_SIZE?=0x2000 -#WOLFBOOT_PARTITION_SIZE?=0x10000 +#WOLFBOOT_PARTITION_SIZE?=0x12000 #WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x6A000 -#WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x7A000 -#WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x8A000 +#WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x7C000 +#WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x8E000 diff --git a/hal/stm32l5.c b/hal/stm32l5.c index 6bfb02a1ef..34b1c2b21c 100644 --- a/hal/stm32l5.c +++ b/hal/stm32l5.c @@ -400,11 +400,17 @@ static void clock_pll_on(int powersave) #if TZ_SECURE() static void periph_unsecure() { + volatile uint32_t reg; + /*Enable clock for User LED GPIOs */ RCC_AHB2_CLOCK_ER|= LED_AHB2_ENABLE; /* Enable clock for LPUART1 */ RCC_APB1_CLOCK_ER |= UART1_APB1_CLOCK_ER_VAL; + /* Enable clock for USART3 used by emu-test-apps on PD8/PD9 */ + RCC_APB1_CLOCK_ER |= UART3_APB1_CLOCK_ER_VAL; + /* Enable clock for GPIO D (USART3 pins) */ + RCC_AHB2_CLOCK_ER |= GPIOD_AHB2_CLOCK_ER; PWR_CR2 |= PWR_CR2_IOSV; @@ -423,6 +429,16 @@ static void periph_unsecure() GPIO_SECCFGR(GPIOG_BASE) &= ~(1< -#include #include -#include #include "system.h" #include "hal.h" #include "uart_drv.h" @@ -33,12 +30,9 @@ #include "target.h" #ifdef SECURE_PKCS11 +#include "test_pkcs11.h" #include "wcs/user_settings.h" #include -#include -#include -extern const char pkcs11_library_name[]; -extern const CK_FUNCTION_LIST wolfpkcs11nsFunctionList; #endif #define LED_BOOT_PIN (9) /* PA9 - Nucleo - Red Led */ @@ -134,84 +128,15 @@ void extra_led_off(void) GPIOB_BSRR |= (1 << (LED_EXTRA_PIN + 16)); } - -extern int ecdsa_sign_verify(int devId); - - void main(void) { - int ret; - uint32_t rand; - uint32_t i; - uint32_t klen = 200; - int otherkey_slot; - unsigned int devId = 0; - #ifdef SECURE_PKCS11 - WC_RNG rng; - Pkcs11Token token; - Pkcs11Dev PKCS11_d; - unsigned long session; - char TokenPin[] = "0123456789ABCDEF"; - char UserPin[] = "ABCDEF0123456789"; - char SoPinName[] = "SO-PIN"; + int ret; boot_led_on(); - - wolfCrypt_Init(); - - PKCS11_d.heap = NULL, - PKCS11_d.func = (CK_FUNCTION_LIST *)&wolfpkcs11nsFunctionList; - - ret = wc_Pkcs11Token_Init(&token, &PKCS11_d, 1, "EccKey", - (const byte*)TokenPin, strlen(TokenPin)); - - if (ret == 0) { - ret = wolfpkcs11nsFunctionList.C_OpenSession(1, - CKF_SERIAL_SESSION | CKF_RW_SESSION, - NULL, NULL, &session); - } - if (ret == 0) { - ret = wolfpkcs11nsFunctionList.C_InitToken(1, - (byte *)TokenPin, strlen(TokenPin), (byte *)SoPinName); - } - - if (ret == 0) { - extra_led_on(); - ret = wolfpkcs11nsFunctionList.C_Login(session, CKU_SO, - (byte *)TokenPin, - strlen(TokenPin)); - } - if (ret == 0) { - ret = wolfpkcs11nsFunctionList.C_InitPIN(session, - (byte *)TokenPin, - strlen(TokenPin)); - } - if (ret == 0) { - ret = wolfpkcs11nsFunctionList.C_Logout(session); - } - if (ret != 0) { - while(1) - ; - } - if (ret == 0) { - ret = wc_CryptoDev_RegisterDevice(devId, wc_Pkcs11_CryptoDevCb, - &token); - if (ret != 0) { - while(1) - ; - } - if (ret == 0) { -#ifdef HAVE_ECC - ret = ecdsa_sign_verify(devId); - if (ret != 0) - ret = 1; - else - usr_led_on(); -#endif - } - wc_Pkcs11Token_Final(&token); - } + ret = test_pkcs11_start(); + if (ret != PKCS11_TEST_FAIL) + usr_led_on(); #endif while(1) ; diff --git a/test-app/test_pkcs11.c b/test-app/test_pkcs11.c new file mode 100644 index 0000000000..e933f4aed7 --- /dev/null +++ b/test-app/test_pkcs11.c @@ -0,0 +1,595 @@ +/* test_pkcs11.c + * + * Reusable PKCS11 secure-world integration demo for wolfBoot test apps. + * + * The demo is intentionally verbose and linear so it doubles as a reference + * for TEE integrations: + * 1. initialize or restore a token + * 2. create/find persistent key objects + * 3. sign a payload with the private key + * 4. persist application data as a PKCS11 data object + * 5. restore objects on the next boot and verify the signature + */ + +#include "user_settings.h" + +#ifdef WOLFBOOT_TZ_PKCS11 + +#include "test_pkcs11.h" + +#include "wolfpkcs11/pkcs11.h" + +#include +#include +#include +#include +#include +#include +#include + +extern const char pkcs11_library_name[]; +extern const CK_FUNCTION_LIST wolfpkcs11nsFunctionList; + +static const CK_BYTE test_token_label[32] = { + 'E','c','c','K','e','y',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', + ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ' +}; +static const char test_token_name[] = "EccKey"; +static const CK_BYTE test_so_pin[] = "0123456789ABCDEF"; +static const CK_BYTE test_user_pin[] = "0123456789ABCDEF"; +static const CK_BYTE test_so_pin_label[] = "SO-PIN"; +static const CK_BYTE test_key_id[] = { 0x57, 0x42, 0x50, 0x31 }; +static const CK_BYTE test_pub_label[] = "wolfBoot PKCS11 demo pub"; +static const CK_BYTE test_priv_label[] = "wolfBoot PKCS11 demo priv"; +static const CK_BYTE test_data_label[] = "wolfBoot PKCS11 demo blob"; +static const CK_BYTE test_application[] = "wolfBoot PKCS11 demo"; +static const CK_BYTE test_object_id[] = { 0x50, 0x4B, 0x43, 0x53, 0x31, 0x31 }; +static const CK_BYTE test_payload[] = "wolfBoot PKCS11 persistent signing demo"; + +/* ASN.1 DER object identifier for secp256r1 / prime256v1. */ +static const CK_BYTE test_ecc_p256_params[] = { + 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 +}; + +struct test_pkcs11_blob { + uint32_t magic; + uint16_t version; + uint16_t reserved; + uint32_t payload_len; + uint32_t sig_len; + CK_BYTE data[512]; +}; + +#define TEST_PKCS11_BLOB_MAGIC 0x314B5057UL +#define TEST_PKCS11_BLOB_VERSION 1U +#define TEST_PKCS11_SLOT_ID 1UL + +static void test_pkcs11_dump_rv(const char *label, CK_RV rv) +{ + printf("pkcs11: %s rv=0x%08lx\r\n", label, (unsigned long)rv); +} + +static int test_pkcs11_ck_ok(const char *label, CK_RV rv) +{ + if (rv != CKR_OK) { + test_pkcs11_dump_rv(label, rv); + return -1; + } + return 0; +} + +static void test_pkcs11_log_blob_checksum(const struct test_pkcs11_blob *blob, + const char *prefix) +{ + byte digest[WC_SHA256_DIGEST_SIZE]; + word32 blob_len = (word32)(blob->payload_len + blob->sig_len); + word32 i; + + if (wc_Sha256Hash(blob->data, blob_len, digest) != 0) + return; + + printf("pkcs11: %s blob_sha256=", prefix); + for (i = 0; i < (word32)sizeof(digest); i++) + printf("%02x", digest[i]); + printf("\r\n"); +} + +static int test_pkcs11_find_one(CK_SESSION_HANDLE session, + CK_ATTRIBUTE_PTR tmpl, CK_ULONG tmpl_count, CK_OBJECT_HANDLE *obj) +{ + CK_RV rv; + CK_ULONG count = 0; + + *obj = CK_INVALID_HANDLE; + + rv = wolfpkcs11nsFunctionList.C_FindObjectsInit(session, tmpl, tmpl_count); + if (test_pkcs11_ck_ok("C_FindObjectsInit", rv) < 0) + return -1; + + rv = wolfpkcs11nsFunctionList.C_FindObjects(session, obj, 1, &count); + if (rv != CKR_OK) { + (void)wolfpkcs11nsFunctionList.C_FindObjectsFinal(session); + test_pkcs11_dump_rv("C_FindObjects", rv); + return -1; + } + + rv = wolfpkcs11nsFunctionList.C_FindObjectsFinal(session); + if (test_pkcs11_ck_ok("C_FindObjectsFinal", rv) < 0) + return -1; + + if (count != 1 || *obj == CK_INVALID_HANDLE) + return 1; + + return 0; +} + +static int test_pkcs11_get_attr(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, + CK_ATTRIBUTE_TYPE type, CK_BYTE *buf, CK_ULONG *len) +{ + CK_ATTRIBUTE attr = { type, NULL, 0 }; + CK_RV rv; + + rv = wolfpkcs11nsFunctionList.C_GetAttributeValue(session, obj, &attr, 1); + if (rv != CKR_OK || attr.ulValueLen == (CK_ULONG)-1) + return -1; + if (buf == NULL) { + *len = attr.ulValueLen; + return 0; + } + if (*len < attr.ulValueLen) + return -1; + + attr.pValue = buf; + rv = wolfpkcs11nsFunctionList.C_GetAttributeValue(session, obj, &attr, 1); + if (rv != CKR_OK || attr.ulValueLen == (CK_ULONG)-1) + return -1; + + *len = attr.ulValueLen; + return 0; +} + +static int test_pkcs11_log_obj_attr(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE obj, + const char *prefix, CK_ATTRIBUTE_TYPE type) +{ + CK_BYTE buf[160]; + CK_ULONG len = sizeof(buf); + int ret; + unsigned int i; + + ret = test_pkcs11_get_attr(session, obj, type, buf, &len); + if (ret < 0) { + printf("pkcs11: %s attr 0x%08lx unavailable\r\n", + prefix, (unsigned long)type); + return ret; + } + + printf("pkcs11: %s attr 0x%08lx len=%lu", + prefix, (unsigned long)type, (unsigned long)len); + if (type == CKA_LABEL || type == CKA_ID || type == CKA_OBJECT_ID || + type == CKA_APPLICATION) { + printf(" value="); + for (i = 0; i < len; i++) { + CK_BYTE ch = buf[i]; + if (ch >= 32 && ch < 127) + printf("%c", ch); + else + printf("\\x%02x", ch); + } + } + printf("\r\n"); + return 0; +} + +static int test_pkcs11_provision_token(void) +{ + int ret; + CK_RV rv; + CK_SESSION_HANDLE session = CK_INVALID_HANDLE; + Pkcs11Token token; + Pkcs11Dev dev; + + printf("pkcs11: provisioning token\r\n"); + + dev.heap = NULL; + dev.func = (CK_FUNCTION_LIST *)&wolfpkcs11nsFunctionList; + + ret = wc_Pkcs11Token_Init(&token, &dev, (int)TEST_PKCS11_SLOT_ID, + test_token_name, test_user_pin, (int)(sizeof(test_user_pin) - 1)); + if (ret != 0) { + printf("pkcs11: wc_Pkcs11Token_Init ret=%d\r\n", ret); + return -1; + } + + rv = wolfpkcs11nsFunctionList.C_InitToken(TEST_PKCS11_SLOT_ID, + (CK_UTF8CHAR_PTR)test_so_pin, (CK_ULONG)(sizeof(test_so_pin) - 1), + (CK_UTF8CHAR_PTR)test_token_label); + if (test_pkcs11_ck_ok("C_InitToken", rv) < 0) { + wc_Pkcs11Token_Final(&token); + return -1; + } + + rv = wolfpkcs11nsFunctionList.C_OpenSession(TEST_PKCS11_SLOT_ID, + CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, &session); + if (test_pkcs11_ck_ok("C_OpenSession(SO)", rv) < 0) { + wc_Pkcs11Token_Final(&token); + return -1; + } + + rv = wolfpkcs11nsFunctionList.C_Login(session, CKU_SO, + (CK_UTF8CHAR_PTR)test_so_pin, (CK_ULONG)(sizeof(test_so_pin) - 1)); + if (test_pkcs11_ck_ok("C_Login(SO)", rv) < 0) { + (void)wolfpkcs11nsFunctionList.C_CloseSession(session); + wc_Pkcs11Token_Final(&token); + return -1; + } + + rv = wolfpkcs11nsFunctionList.C_InitPIN(session, + (CK_UTF8CHAR_PTR)test_user_pin, (CK_ULONG)(sizeof(test_user_pin) - 1)); + if (test_pkcs11_ck_ok("C_InitPIN", rv) < 0) { + (void)wolfpkcs11nsFunctionList.C_Logout(session); + (void)wolfpkcs11nsFunctionList.C_CloseSession(session); + wc_Pkcs11Token_Final(&token); + return -1; + } + + (void)wolfpkcs11nsFunctionList.C_Logout(session); + (void)wolfpkcs11nsFunctionList.C_CloseSession(session); + wc_Pkcs11Token_Final(&token); + return 0; +} + +static int test_pkcs11_open_user_session(CK_SESSION_HANDLE *session) +{ + CK_RV rv; + + *session = CK_INVALID_HANDLE; + + rv = wolfpkcs11nsFunctionList.C_OpenSession(TEST_PKCS11_SLOT_ID, + CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, session); + if (test_pkcs11_ck_ok("C_OpenSession(USER)", rv) < 0) + return -1; + + rv = wolfpkcs11nsFunctionList.C_Login(*session, CKU_USER, + (CK_UTF8CHAR_PTR)test_user_pin, (CK_ULONG)(sizeof(test_user_pin) - 1)); + if (rv == CKR_OK) + return 0; + + if (rv == CKR_USER_PIN_NOT_INITIALIZED) { + (void)wolfpkcs11nsFunctionList.C_CloseSession(*session); + *session = CK_INVALID_HANDLE; + return -2; + } + + test_pkcs11_dump_rv("C_Login(USER)", rv); + (void)wolfpkcs11nsFunctionList.C_CloseSession(*session); + *session = CK_INVALID_HANDLE; + return -1; +} + +static int test_pkcs11_find_keypair(CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE *pub_obj, CK_OBJECT_HANDLE *priv_obj) +{ + CK_OBJECT_CLASS pub_class = CKO_PUBLIC_KEY; + CK_OBJECT_CLASS priv_class = CKO_PRIVATE_KEY; + CK_ATTRIBUTE pub_tmpl[] = { + { CKA_CLASS, &pub_class, sizeof(pub_class) }, + { CKA_ID, (CK_VOID_PTR)test_key_id, sizeof(test_key_id) }, + { CKA_LABEL, (CK_VOID_PTR)test_pub_label, sizeof(test_pub_label) - 1 } + }; + CK_ATTRIBUTE priv_tmpl[] = { + { CKA_CLASS, &priv_class, sizeof(priv_class) }, + { CKA_ID, (CK_VOID_PTR)test_key_id, sizeof(test_key_id) }, + { CKA_LABEL, (CK_VOID_PTR)test_priv_label, sizeof(test_priv_label) - 1 } + }; + int ret_pub; + int ret_priv; + + ret_pub = test_pkcs11_find_one(session, pub_tmpl, + (CK_ULONG)(sizeof(pub_tmpl) / sizeof(pub_tmpl[0])), pub_obj); + ret_priv = test_pkcs11_find_one(session, priv_tmpl, + (CK_ULONG)(sizeof(priv_tmpl) / sizeof(priv_tmpl[0])), priv_obj); + + if (ret_pub == 1 && ret_priv == 1) + return 1; + if (ret_pub != 0 || ret_priv != 0) + return -1; + return 0; +} + +static int test_pkcs11_find_data_obj(CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE *data_obj) +{ + CK_OBJECT_CLASS data_class = CKO_DATA; + CK_ATTRIBUTE data_tmpl[] = { + { CKA_CLASS, &data_class, sizeof(data_class) }, + { CKA_LABEL, (CK_VOID_PTR)test_data_label, sizeof(test_data_label) - 1 }, + { CKA_OBJECT_ID, (CK_VOID_PTR)test_object_id, sizeof(test_object_id) } + }; + + return test_pkcs11_find_one(session, data_tmpl, + (CK_ULONG)(sizeof(data_tmpl) / sizeof(data_tmpl[0])), data_obj); +} + +static int test_pkcs11_generate_keypair(CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE *pub_obj, CK_OBJECT_HANDLE *priv_obj) +{ + CK_RV rv; + CK_MECHANISM mech; + CK_BBOOL ck_true = CK_TRUE; + CK_ATTRIBUTE pub_tmpl[] = { + { CKA_EC_PARAMS, (CK_VOID_PTR)test_ecc_p256_params, sizeof(test_ecc_p256_params) }, + { CKA_VERIFY, &ck_true, sizeof(ck_true) }, + { CKA_TOKEN, &ck_true, sizeof(ck_true) }, + { CKA_ID, (CK_VOID_PTR)test_key_id, sizeof(test_key_id) }, + { CKA_LABEL, (CK_VOID_PTR)test_pub_label, sizeof(test_pub_label) - 1 } + }; + CK_ATTRIBUTE priv_tmpl[] = { + { CKA_EC_PARAMS, (CK_VOID_PTR)test_ecc_p256_params, sizeof(test_ecc_p256_params) }, + { CKA_SIGN, &ck_true, sizeof(ck_true) }, + { CKA_TOKEN, &ck_true, sizeof(ck_true) }, + { CKA_PRIVATE, &ck_true, sizeof(ck_true) }, + { CKA_ID, (CK_VOID_PTR)test_key_id, sizeof(test_key_id) }, + { CKA_LABEL, (CK_VOID_PTR)test_priv_label, sizeof(test_priv_label) - 1 } + }; + + mech.mechanism = CKM_EC_KEY_PAIR_GEN; + mech.pParameter = NULL; + mech.ulParameterLen = 0; + + rv = wolfpkcs11nsFunctionList.C_GenerateKeyPair(session, &mech, + pub_tmpl, (CK_ULONG)(sizeof(pub_tmpl) / sizeof(pub_tmpl[0])), + priv_tmpl, (CK_ULONG)(sizeof(priv_tmpl) / sizeof(priv_tmpl[0])), + pub_obj, priv_obj); + return test_pkcs11_ck_ok("C_GenerateKeyPair", rv); +} + +static int test_pkcs11_sign_payload(CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE priv_obj, struct test_pkcs11_blob *blob) +{ + CK_RV rv; + CK_MECHANISM mech; + CK_ULONG payload_len = (CK_ULONG)(sizeof(test_payload) - 1); + CK_ULONG sig_len = (CK_ULONG)(sizeof(blob->data) - payload_len); + + mech.mechanism = CKM_ECDSA_SHA256; + mech.pParameter = NULL; + mech.ulParameterLen = 0; + + rv = wolfpkcs11nsFunctionList.C_SignInit(session, &mech, priv_obj); + if (test_pkcs11_ck_ok("C_SignInit", rv) < 0) + return -1; + + rv = wolfpkcs11nsFunctionList.C_Sign(session, + (CK_BYTE_PTR)test_payload, payload_len, + blob->data + payload_len, &sig_len); + if (test_pkcs11_ck_ok("C_Sign", rv) < 0) + return -1; + + memcpy(blob->data, test_payload, (size_t)payload_len); + blob->magic = TEST_PKCS11_BLOB_MAGIC; + blob->version = TEST_PKCS11_BLOB_VERSION; + blob->reserved = 0; + blob->payload_len = (uint32_t)payload_len; + blob->sig_len = (uint32_t)sig_len; + + printf("pkcs11: signed payload len=%lu sig_len=%lu\r\n", + (unsigned long)blob->payload_len, (unsigned long)blob->sig_len); + test_pkcs11_log_blob_checksum(blob, "created"); + return 0; +} + +static int test_pkcs11_store_blob(CK_SESSION_HANDLE session, + const struct test_pkcs11_blob *blob, CK_OBJECT_HANDLE *data_obj) +{ + CK_RV rv; + CK_OBJECT_CLASS data_class = CKO_DATA; + CK_BBOOL ck_true = CK_TRUE; + CK_ATTRIBUTE tmpl[] = { + { CKA_CLASS, &data_class, sizeof(data_class) }, + { CKA_TOKEN, &ck_true, sizeof(ck_true) }, + { CKA_APPLICATION, (CK_VOID_PTR)test_application, sizeof(test_application) - 1 }, + { CKA_LABEL, (CK_VOID_PTR)test_data_label, sizeof(test_data_label) - 1 }, + { CKA_OBJECT_ID, (CK_VOID_PTR)test_object_id, sizeof(test_object_id) }, + { CKA_VALUE, (CK_VOID_PTR)blob, (CK_ULONG)(sizeof(*blob) - sizeof(blob->data) + blob->payload_len + blob->sig_len) } + }; + + rv = wolfpkcs11nsFunctionList.C_CreateObject(session, tmpl, + (CK_ULONG)(sizeof(tmpl) / sizeof(tmpl[0])), data_obj); + return test_pkcs11_ck_ok("C_CreateObject(data)", rv); +} + +static int test_pkcs11_load_blob(CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE data_obj, struct test_pkcs11_blob *blob) +{ + CK_ULONG len = sizeof(*blob); + CK_ULONG expected_len; + int ret; + + ret = test_pkcs11_get_attr(session, data_obj, CKA_VALUE, (CK_BYTE *)blob, &len); + if (ret < 0) + return -1; + + if (blob->magic != TEST_PKCS11_BLOB_MAGIC || + blob->version != TEST_PKCS11_BLOB_VERSION) + return -1; + if (blob->payload_len > sizeof(blob->data) || + blob->sig_len > sizeof(blob->data) || + blob->payload_len + blob->sig_len > sizeof(blob->data)) + return -1; + + expected_len = (CK_ULONG)(sizeof(*blob) - sizeof(blob->data) + + blob->payload_len + blob->sig_len); + if (len < expected_len) + return -1; + + printf("pkcs11: restored blob payload_len=%lu sig_len=%lu\r\n", + (unsigned long)blob->payload_len, (unsigned long)blob->sig_len); + test_pkcs11_log_blob_checksum(blob, "restored"); + return 0; +} + +static int test_pkcs11_verify_blob(CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE pub_obj, const struct test_pkcs11_blob *blob) +{ + CK_RV rv; + CK_MECHANISM mech; + + mech.mechanism = CKM_ECDSA_SHA256; + mech.pParameter = NULL; + mech.ulParameterLen = 0; + + rv = wolfpkcs11nsFunctionList.C_VerifyInit(session, &mech, pub_obj); + if (test_pkcs11_ck_ok("C_VerifyInit", rv) < 0) + return -1; + + rv = wolfpkcs11nsFunctionList.C_Verify(session, + (CK_BYTE_PTR)blob->data, (CK_ULONG)blob->payload_len, + (CK_BYTE_PTR)(blob->data + blob->payload_len), (CK_ULONG)blob->sig_len); + return test_pkcs11_ck_ok("C_Verify", rv); +} + +static int test_pkcs11_log_key_attrs(CK_SESSION_HANDLE session, + CK_OBJECT_HANDLE pub_obj, CK_OBJECT_HANDLE priv_obj) +{ + CK_BYTE ec_point[160]; + CK_ULONG ec_point_len = sizeof(ec_point); + + if (test_pkcs11_log_obj_attr(session, pub_obj, "public", CKA_LABEL) < 0) + return -1; + if (test_pkcs11_log_obj_attr(session, pub_obj, "public", CKA_ID) < 0) + return -1; + if (test_pkcs11_log_obj_attr(session, priv_obj, "private", CKA_LABEL) < 0) + return -1; + if (test_pkcs11_log_obj_attr(session, priv_obj, "private", CKA_ID) < 0) + return -1; + if (test_pkcs11_get_attr(session, pub_obj, CKA_EC_POINT, ec_point, &ec_point_len) == 0) + printf("pkcs11: public attr 0x%08lx len=%lu\r\n", + (unsigned long)CKA_EC_POINT, (unsigned long)ec_point_len); + return 0; +} + +int test_pkcs11_start(void) +{ + int wc_ret; + CK_RV rv; + CK_SESSION_HANDLE session = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE pub_obj = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE priv_obj = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE data_obj = CK_INVALID_HANDLE; + struct test_pkcs11_blob blob; + int ret; + int key_state; + int data_state; + int result = PKCS11_TEST_FAIL; + + memset(&blob, 0, sizeof(blob)); + + printf("pkcs11: start\r\n"); + printf("pkcs11: secure provider=%s\r\n", pkcs11_library_name); + + wc_ret = wolfCrypt_Init(); + if (wc_ret != 0) { + printf("pkcs11: wolfCrypt_Init ret=%d\r\n", wc_ret); + return -1; + } + + rv = wolfpkcs11nsFunctionList.C_Initialize(NULL); + if (test_pkcs11_ck_ok("C_Initialize", rv) < 0) { + wolfCrypt_Cleanup(); + return -1; + } + + ret = test_pkcs11_open_user_session(&session); + if (ret == -2) { + printf("pkcs11: first boot path, provisioning token\r\n"); + if (test_pkcs11_provision_token() < 0) { + (void)wolfpkcs11nsFunctionList.C_Finalize(NULL); + wolfCrypt_Cleanup(); + return -1; + } + ret = test_pkcs11_open_user_session(&session); + } + if (ret < 0) { + ret = -1; + goto cleanup; + } + + key_state = test_pkcs11_find_keypair(session, &pub_obj, &priv_obj); + if (key_state < 0) { + ret = -1; + goto cleanup; + } + + data_state = test_pkcs11_find_data_obj(session, &data_obj); + if (data_state < 0) { + ret = -1; + goto cleanup; + } + + if (key_state == 1 && data_state == 1) { + printf("pkcs11: first boot path, creating persistent objects\r\n"); + if (test_pkcs11_generate_keypair(session, &pub_obj, &priv_obj) < 0) + ret = -1; + else + ret = 0; + if (ret == 0) + ret = test_pkcs11_sign_payload(session, priv_obj, &blob); + if (ret == 0) + ret = test_pkcs11_verify_blob(session, pub_obj, &blob); + if (ret == 0) + ret = test_pkcs11_store_blob(session, &blob, &data_obj); + if (ret == 0) + ret = test_pkcs11_log_key_attrs(session, pub_obj, priv_obj); + if (ret == 0) + ret = test_pkcs11_log_obj_attr(session, data_obj, "data", CKA_LABEL); + if (ret == 0) + ret = test_pkcs11_log_obj_attr(session, data_obj, "data", CKA_OBJECT_ID); + if (ret == 0) + printf("pkcs11: created persistent PKCS11 objects\r\n"); + if (ret == 0) + result = PKCS11_TEST_FIRST_BOOT_OK; + } + else if (key_state == 0 && data_state == 0) { + printf("pkcs11: second boot path, restoring persistent objects\r\n"); + ret = test_pkcs11_load_blob(session, data_obj, &blob); + if (ret == 0) + ret = test_pkcs11_log_key_attrs(session, pub_obj, priv_obj); + if (ret == 0) + ret = test_pkcs11_log_obj_attr(session, data_obj, "data", CKA_APPLICATION); + if (ret == 0) + ret = test_pkcs11_verify_blob(session, pub_obj, &blob); + if (ret == 0) + printf("pkcs11: restored persistent PKCS11 objects\r\n"); + if (ret == 0) + result = PKCS11_TEST_SECOND_BOOT_OK; + } + else { + printf("pkcs11: inconsistent persistent state key_state=%d data_state=%d\r\n", + key_state, data_state); + ret = -1; + } + +cleanup: + (void)wolfpkcs11nsFunctionList.C_Logout(session); + (void)wolfpkcs11nsFunctionList.C_CloseSession(session); + (void)wolfpkcs11nsFunctionList.C_Finalize(NULL); + (void)wolfCrypt_Cleanup(); + + if (ret == 0) + printf("pkcs11: success\r\n"); + else + printf("pkcs11: failure\r\n"); + + return (ret == 0) ? result : PKCS11_TEST_FAIL; +} + +#else + +#include "test_pkcs11.h" + +int test_pkcs11_start(void) +{ + return -1; +} + +#endif /* WOLFBOOT_TZ_PKCS11 */ diff --git a/test-app/test_pkcs11.h b/test-app/test_pkcs11.h new file mode 100644 index 0000000000..f0abd9862e --- /dev/null +++ b/test-app/test_pkcs11.h @@ -0,0 +1,12 @@ +#ifndef WOLFBOOT_TEST_PKCS11_H +#define WOLFBOOT_TEST_PKCS11_H + +enum test_pkcs11_result { + PKCS11_TEST_FAIL = -1, + PKCS11_TEST_FIRST_BOOT_OK = 1, + PKCS11_TEST_SECOND_BOOT_OK = 2 +}; + +int test_pkcs11_start(void); + +#endif /* WOLFBOOT_TEST_PKCS11_H */