Add GitHub Action workflow for TPM corruption testing #5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: TPM Corruption Test | |
| on: | |
| pull_request: | |
| branches: [ '*' ] | |
| workflow_dispatch: | |
| jobs: | |
| tpm-corruption-test: | |
| runs-on: ubuntu-latest | |
| steps: | |
| # Checkout the PR code | |
| - name: Checkout PR code | |
| uses: actions/checkout@v4 | |
| with: | |
| path: wolfpkcs11-pr | |
| # Checkout the old commit that has the bug | |
| - name: Checkout old commit with bug | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: wolfssl/wolfpkcs11 | |
| ref: 1a7f7d71b98dbffbfd4ad77f0c77c8c573a2c5d2 | |
| path: wolfpkcs11-old | |
| # Setup wolfSSL | |
| - name: Checkout wolfSSL | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: wolfssl/wolfssl | |
| path: wolfssl | |
| - name: Build wolfSSL | |
| working-directory: ./wolfssl | |
| run: | | |
| ./autogen.sh | |
| ./configure --enable-cryptocb --enable-aescfb --enable-rsapss --enable-keygen --enable-pwdbased --enable-scrypt \ | |
| C_EXTRA_FLAGS="-DWOLFSSL_PUBLIC_MP -DWC_RSA_DIRECT" | |
| make | |
| sudo make install | |
| sudo ldconfig | |
| # Setup ibmswtpm2 | |
| - name: Checkout ibmswtpm2 | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: kgoldman/ibmswtpm2 | |
| path: ibmswtpm2 | |
| - name: Build ibmswtpm2 | |
| working-directory: ./ibmswtpm2/src | |
| run: make | |
| - name: Start TPM server | |
| working-directory: ./ibmswtpm2/src | |
| run: | | |
| ./tpm_server & | |
| sleep 2 | |
| # Setup wolfTPM | |
| - name: Checkout wolfTPM | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: wolfssl/wolftpm | |
| path: wolftpm | |
| - name: Build wolfTPM | |
| working-directory: ./wolftpm | |
| run: | | |
| ./autogen.sh | |
| ./configure --enable-swtpm | |
| make | |
| sudo make install | |
| sudo ldconfig | |
| # Build old wolfPKCS11 with bug | |
| - name: Build old wolfPKCS11 | |
| working-directory: ./wolfpkcs11-old | |
| run: | | |
| ./autogen.sh | |
| ./configure --enable-singlethreaded --enable-wolftpm --disable-dh CFLAGS="-DWOLFPKCS11_TPM_STORE" | |
| make | |
| # Initialize token | |
| - name: Initialize token | |
| working-directory: ./wolfpkcs11-old | |
| run: | | |
| ./examples/init_token -userPin "wolfpkcs11-test" | |
| # Create corruption test program | |
| - name: Create corruption test program | |
| run: | | |
| cat > corruption_test.c << 'EOF' | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <dlfcn.h> | |
| #include <unistd.h> | |
| #define CK_PTR * | |
| #define CK_DEFINE_FUNCTION(returnType, name) returnType name | |
| #define CK_DECLARE_FUNCTION(returnType, name) returnType name | |
| #define CK_DECLARE_FUNCTION_POINTER(returnType, name) returnType (* name) | |
| #define CK_CALLBACK_FUNCTION(returnType, name) returnType (* name) | |
| #ifndef NULL_PTR | |
| #define NULL_PTR 0 | |
| #endif | |
| #include "wolfpkcs11-old/wolfpkcs11/pkcs11.h" | |
| static CK_FUNCTION_LIST* funcList = NULL; | |
| static void* libHandle = NULL; | |
| int load_library(const char* path) { | |
| CK_RV rv; | |
| CK_C_GetFunctionList pC_GetFunctionList; | |
| libHandle = dlopen(path, RTLD_NOW | RTLD_LOCAL); | |
| if (!libHandle) { | |
| printf("ERROR: Failed to load library: %s\n", dlerror()); | |
| return -1; | |
| } | |
| pC_GetFunctionList = (CK_C_GetFunctionList)dlsym(libHandle, "C_GetFunctionList"); | |
| if (!pC_GetFunctionList) { | |
| printf("ERROR: Failed to get C_GetFunctionList: %s\n", dlerror()); | |
| dlclose(libHandle); | |
| return -1; | |
| } | |
| rv = pC_GetFunctionList(&funcList); | |
| if (rv != CKR_OK) { | |
| printf("ERROR: C_GetFunctionList failed with 0x%08lx\n", (unsigned long)rv); | |
| dlclose(libHandle); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| void unload_library(void) { | |
| if (libHandle) { | |
| dlclose(libHandle); | |
| libHandle = NULL; | |
| funcList = NULL; | |
| } | |
| } | |
| int create_aes_key(CK_SESSION_HANDLE session, int index) { | |
| CK_RV rv; | |
| CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; | |
| CK_KEY_TYPE keyType = CKK_AES; | |
| CK_BBOOL ckTrue = CK_TRUE; | |
| CK_BYTE keyValue[32]; | |
| char label[64]; | |
| CK_OBJECT_HANDLE object; | |
| memset(keyValue, index & 0xFF, sizeof(keyValue)); | |
| snprintf(label, sizeof(label), "test_aes_key_%d", index); | |
| CK_ATTRIBUTE keyTemplate[] = { | |
| { CKA_CLASS, &keyClass, sizeof(keyClass) }, | |
| { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, | |
| { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, | |
| { CKA_LABEL, label, strlen(label) }, | |
| { CKA_ENCRYPT, &ckTrue, sizeof(ckTrue) }, | |
| { CKA_DECRYPT, &ckTrue, sizeof(ckTrue) }, | |
| { CKA_VALUE, keyValue, sizeof(keyValue) } | |
| }; | |
| rv = funcList->C_CreateObject(session, keyTemplate, | |
| sizeof(keyTemplate) / sizeof(CK_ATTRIBUTE), | |
| &object); | |
| return (rv == CKR_OK) ? 0 : -1; | |
| } | |
| int main(int argc, char* argv[]) { | |
| CK_RV rv; | |
| CK_SESSION_HANDLE session; | |
| CK_BYTE userPin[] = "wolfpkcs11-test"; | |
| CK_SLOT_ID slotList[16]; | |
| CK_ULONG slotCount = sizeof(slotList) / sizeof(slotList[0]); | |
| CK_SLOT_ID slot; | |
| int i, created = 0; | |
| const char* lib_path = argv[1]; | |
| printf("Loading library: %s\n", lib_path); | |
| if (load_library(lib_path) != 0) return 1; | |
| rv = funcList->C_Initialize(NULL); | |
| if (rv != CKR_OK) { | |
| printf("ERROR: C_Initialize failed with 0x%08lx\n", (unsigned long)rv); | |
| return 1; | |
| } | |
| /* Get available slots */ | |
| rv = funcList->C_GetSlotList(CK_TRUE, slotList, &slotCount); | |
| if (rv != CKR_OK) { | |
| printf("ERROR: C_GetSlotList failed with 0x%08lx\n", (unsigned long)rv); | |
| return 1; | |
| } | |
| if (slotCount == 0) { | |
| printf("ERROR: No slots available\n"); | |
| return 1; | |
| } | |
| slot = slotList[0]; | |
| printf("Using slot %lu\n", (unsigned long)slot); | |
| rv = funcList->C_OpenSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, | |
| NULL, NULL, &session); | |
| if (rv != CKR_OK) { | |
| printf("ERROR: C_OpenSession failed with 0x%08lx\n", (unsigned long)rv); | |
| return 1; | |
| } | |
| rv = funcList->C_Login(session, CKU_USER, userPin, sizeof(userPin) - 1); | |
| if (rv != CKR_OK) { | |
| printf("ERROR: C_Login failed with 0x%08lx\n", (unsigned long)rv); | |
| return 1; | |
| } | |
| printf("Filling TPM with AES keys...\n"); | |
| for (i = 0; i < 1000; i++) { | |
| if (create_aes_key(session, i) == 0) { | |
| created++; | |
| if (created % 10 == 0) { | |
| printf("Created %d AES keys...\n", created); | |
| } | |
| } else { | |
| printf("Failed to create AES key %d (created %d total)\n", i, created); | |
| break; | |
| } | |
| } | |
| printf("Successfully created %d AES keys\n", created); | |
| /* Do NOT call C_Finalize or unload_library - this could cause | |
| * metadata to fix itself upon closing. Use _exit to avoid any | |
| * cleanup handlers that might "heal" the corrupted state. */ | |
| _exit(0); | |
| } | |
| EOF | |
| - name: Compile corruption test | |
| run: | | |
| gcc -o corruption_test corruption_test.c -I./wolfpkcs11-old -ldl -lpthread | |
| # Run corruption test with old version | |
| - name: Run corruption test (create corrupted state) | |
| working-directory: ./wolfpkcs11-old | |
| run: | | |
| ../corruption_test ./src/.libs/libwolfpkcs11.so | |
| # Stop TPM server to flush NVChip | |
| - name: Stop TPM server | |
| run: | | |
| pkill -f tpm_server | |
| sleep 2 | |
| # Verify NVChip exists and capture it | |
| - name: Verify and capture corrupted NVChip | |
| run: | | |
| echo "Checking for NVChip in ibmswtpm2/src directory..." | |
| ls -lh ./ibmswtpm2/src/ | |
| if [ ! -f ./ibmswtpm2/src/NVChip ]; then | |
| echo "ERROR: NVChip file not found!" | |
| exit 1 | |
| fi | |
| cp ./ibmswtpm2/src/NVChip ./corrupted_NVChip | |
| echo "Corrupted NVChip captured:" | |
| ls -lh ./corrupted_NVChip | |
| - name: Upload corrupted NVChip artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: corrupted-nvchip | |
| path: ./corrupted_NVChip | |
| retention-days: 30 | |
| if-no-files-found: error | |
| # Restart TPM server with corrupted state | |
| - name: Restart TPM server | |
| working-directory: ./ibmswtpm2/src | |
| run: | | |
| ./tpm_server & | |
| sleep 2 | |
| # Build PR version of wolfPKCS11 | |
| - name: Build PR wolfPKCS11 | |
| working-directory: ./wolfpkcs11-pr | |
| run: | | |
| ./autogen.sh | |
| ./configure --enable-singlethreaded --enable-wolftpm --disable-dh CFLAGS="-DWOLFPKCS11_TPM_STORE" | |
| make | |
| # Create test program to access corrupted state | |
| - name: Create access test program | |
| run: | | |
| cat > access_test.c << 'EOF' | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <dlfcn.h> | |
| #define CK_PTR * | |
| #define CK_DEFINE_FUNCTION(returnType, name) returnType name | |
| #define CK_DECLARE_FUNCTION(returnType, name) returnType name | |
| #define CK_DECLARE_FUNCTION_POINTER(returnType, name) returnType (* name) | |
| #define CK_CALLBACK_FUNCTION(returnType, name) returnType (* name) | |
| #ifndef NULL_PTR | |
| #define NULL_PTR 0 | |
| #endif | |
| #include "wolfpkcs11-pr/wolfpkcs11/pkcs11.h" | |
| static CK_FUNCTION_LIST* funcList = NULL; | |
| static void* libHandle = NULL; | |
| int load_library(const char* path) { | |
| CK_RV rv; | |
| CK_C_GetFunctionList pC_GetFunctionList; | |
| libHandle = dlopen(path, RTLD_NOW | RTLD_LOCAL); | |
| if (!libHandle) { | |
| printf("ERROR: Failed to load library: %s\n", dlerror()); | |
| return -1; | |
| } | |
| pC_GetFunctionList = (CK_C_GetFunctionList)dlsym(libHandle, "C_GetFunctionList"); | |
| if (!pC_GetFunctionList) { | |
| printf("ERROR: Failed to get C_GetFunctionList: %s\n", dlerror()); | |
| dlclose(libHandle); | |
| return -1; | |
| } | |
| rv = pC_GetFunctionList(&funcList); | |
| if (rv != CKR_OK) { | |
| printf("ERROR: C_GetFunctionList failed with 0x%08lx\n", (unsigned long)rv); | |
| dlclose(libHandle); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| int main(int argc, char* argv[]) { | |
| CK_RV rv; | |
| CK_SESSION_HANDLE session; | |
| CK_BYTE userPin[] = "wolfpkcs11-test"; | |
| CK_OBJECT_HANDLE objects[1000]; | |
| CK_ULONG count = 0; | |
| CK_BBOOL ckTrue = CK_TRUE; | |
| CK_SLOT_ID slotList[16]; | |
| CK_ULONG slotCount = sizeof(slotList) / sizeof(slotList[0]); | |
| CK_SLOT_ID slot; | |
| const char* lib_path = argv[1]; | |
| printf("Testing access to corrupted TPM state with PR version...\n"); | |
| printf("Loading library: %s\n", lib_path); | |
| if (load_library(lib_path) != 0) return 1; | |
| printf("Attempting C_Initialize...\n"); | |
| rv = funcList->C_Initialize(NULL); | |
| if (rv != CKR_OK) { | |
| printf("CORRUPTION DETECTED: C_Initialize failed with 0x%08lx\n", (unsigned long)rv); | |
| return 1; | |
| } | |
| printf("C_Initialize succeeded\n"); | |
| /* Get available slots */ | |
| printf("Getting slot list...\n"); | |
| rv = funcList->C_GetSlotList(CK_TRUE, slotList, &slotCount); | |
| if (rv != CKR_OK) { | |
| printf("ERROR: C_GetSlotList failed with 0x%08lx\n", (unsigned long)rv); | |
| return 1; | |
| } | |
| if (slotCount == 0) { | |
| printf("ERROR: No slots available\n"); | |
| return 1; | |
| } | |
| slot = slotList[0]; | |
| printf("Using slot %lu\n", (unsigned long)slot); | |
| printf("Attempting C_OpenSession...\n"); | |
| rv = funcList->C_OpenSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, | |
| NULL, NULL, &session); | |
| if (rv != CKR_OK) { | |
| printf("CORRUPTION DETECTED: C_OpenSession failed with 0x%08lx\n", (unsigned long)rv); | |
| return 1; | |
| } | |
| printf("C_OpenSession succeeded\n"); | |
| printf("Attempting C_Login...\n"); | |
| rv = funcList->C_Login(session, CKU_USER, userPin, sizeof(userPin) - 1); | |
| if (rv != CKR_OK) { | |
| printf("ERROR: C_Login failed with 0x%08lx\n", (unsigned long)rv); | |
| if (rv == 0x00000102) { /* CKR_USER_PIN_NOT_INITIALIZED / PIN_NOT_SET_E */ | |
| printf("MIGRATION BUG: Token state not properly migrated from old version\n"); | |
| printf("The PR version needs to detect old-format tokens and set WP11_TOKEN_STATE_INITIALIZED\n"); | |
| } else { | |
| printf("CORRUPTION DETECTED: Unexpected login failure\n"); | |
| } | |
| return 1; | |
| } | |
| printf("C_Login succeeded (migration handled correctly)\n"); | |
| printf("Attempting C_FindObjectsInit...\n"); | |
| CK_ATTRIBUTE findTemplate[] = { | |
| { CKA_TOKEN, &ckTrue, sizeof(ckTrue) } | |
| }; | |
| rv = funcList->C_FindObjectsInit(session, findTemplate, 1); | |
| if (rv != CKR_OK) { | |
| printf("CORRUPTION DETECTED: C_FindObjectsInit failed with 0x%08lx\n", (unsigned long)rv); | |
| return 1; | |
| } | |
| printf("Attempting C_FindObjects...\n"); | |
| rv = funcList->C_FindObjects(session, objects, 1000, &count); | |
| if (rv != CKR_OK) { | |
| printf("CORRUPTION DETECTED: C_FindObjects failed with 0x%08lx\n", (unsigned long)rv); | |
| funcList->C_FindObjectsFinal(session); | |
| return 1; | |
| } | |
| printf("Found %lu objects on token\n", (unsigned long)count); | |
| funcList->C_FindObjectsFinal(session); | |
| funcList->C_Finalize(NULL); | |
| if (count == 0) { | |
| printf("CORRUPTION DETECTED: Expected to find objects but found none\n"); | |
| return 1; | |
| } | |
| printf("Successfully accessed %lu objects\n", (unsigned long)count); | |
| return 0; | |
| } | |
| EOF | |
| - name: Compile access test | |
| run: | | |
| gcc -o access_test access_test.c -I./wolfpkcs11-pr -ldl -lpthread | |
| # Test accessing corrupted state with PR version | |
| - name: Test accessing corrupted state | |
| working-directory: ./wolfpkcs11-pr | |
| run: | | |
| ../access_test ./src/.libs/libwolfpkcs11.so || echo "Expected failure: PR version cannot access corrupted state" | |
| - name: Test summary | |
| run: | | |
| echo "=== TPM Corruption Test Summary ===" | |
| echo "1. Built old commit (1a7f7d71b98dbffbfd4ad77f0c77c8c573a2c5d2)" | |
| echo "2. Created corrupted TPM state by filling with objects" | |
| echo "3. Captured corrupted NVChip as artifact" | |
| echo "4. Built PR version of wolfPKCS11" | |
| echo "5. Tested accessing corrupted state with PR version" | |
| echo "" | |
| echo "This workflow provides a reproducible test case for developing" | |
| echo "the TPM corruption repair function." |