diff --git a/.github/workflows/storage-upgrade-test-tpm.yml b/.github/workflows/storage-upgrade-test-tpm.yml index d7011d0..a59b31a 100644 --- a/.github/workflows/storage-upgrade-test-tpm.yml +++ b/.github/workflows/storage-upgrade-test-tpm.yml @@ -39,7 +39,7 @@ jobs: uses: actions/cache@v4 with: path: wolfssl - key: wolfssl-${{ env.WOLFSSL_VERSION }} + key: wolfssl-tpm-${{ env.WOLFSSL_VERSION }} # Setup wolfssl (required dependency) - name: Checkout wolfssl diff --git a/.github/workflows/tpm-object-upgrade-regression.yml b/.github/workflows/tpm-object-upgrade-regression.yml new file mode 100644 index 0000000..5648e6a --- /dev/null +++ b/.github/workflows/tpm-object-upgrade-regression.yml @@ -0,0 +1,174 @@ +name: wolfPKCS11 TPM Object Upgrade Regression + +on: + pull_request: + branches: ['*'] + +env: + WOLFSSL_VERSION: v5.8.0-stable + BASE_REF: 1a7f7d71b98dbffbfd4ad77f0c77c8c573a2c5d2 + TOKEN_PATH: ${{ github.workspace }}/tpm-upgrade-store + METADATA_FILE: ${{ github.workspace }}/tpm-upgrade-store/tpm-upgrade-metadata.txt + +jobs: + tpm-object-upgrade: + runs-on: ubuntu-latest + + steps: + - name: Checkout PR branch + uses: actions/checkout@v4 + with: + path: pr-branch + + - name: Checkout base reference + uses: actions/checkout@v4 + with: + ref: ${{ env.BASE_REF }} + path: base-branch + + - name: Cache wolfSSL + id: cache-wolfssl + uses: actions/cache@v4 + with: + path: wolfssl + key: wolfssl-tpm-upgrade-${{ env.WOLFSSL_VERSION }} + + - name: Checkout wolfSSL + if: steps.cache-wolfssl.outputs.cache-hit != 'true' + uses: actions/checkout@v4 + with: + repository: wolfSSL/wolfssl + path: wolfssl + ref: ${{ env.WOLFSSL_VERSION }} + + - name: Build wolfSSL + if: steps.cache-wolfssl.outputs.cache-hit != 'true' + working-directory: ./wolfssl + run: | + ./autogen.sh + ./configure --enable-md5 --enable-cryptocb --enable-aescfb --enable-rsapss \ + --enable-keygen --enable-pwdbased --enable-scrypt --enable-debug \ + C_EXTRA_FLAGS="-DWOLFSSL_PUBLIC_MP -DWC_RSA_DIRECT" + make + + - name: Install wolfSSL + working-directory: ./wolfssl + run: | + sudo make install + sudo ldconfig + + - name: Build IBM software TPM + run: | + git clone https://github.com/kgoldman/ibmswtpm2.git + make -C ibmswtpm2/src -j"$(nproc)" + + - name: Build and install wolfTPM + run: | + git clone https://github.com/wolfSSL/wolftpm.git + cd wolftpm + ./autogen.sh + ./configure --enable-swtpm --enable-debug + make + sudo make install + sudo ldconfig + + - name: Build wolfPKCS11 base (prepare reference) with TPM store + working-directory: ./base-branch + run: | + ./autogen.sh + ./configure --enable-debug --enable-singlethreaded --enable-wolftpm \ + --disable-dh C_EXTRA_FLAGS="-DWOLFPKCS11_TPM_STORE" + make + + - name: Build wolfPKCS11 PR branch test binary + working-directory: ./pr-branch + run: | + ./autogen.sh + ./configure --enable-debug --enable-singlethreaded --enable-wolftpm \ + --disable-dh C_EXTRA_FLAGS="-DWOLFPKCS11_TPM_STORE" + make + + - name: Launch TPM simulator + run: | + cd ibmswtpm2/src + ./tpm_server >"${GITHUB_WORKSPACE}/tpm-server.log" 2>&1 & + echo $! > "${GITHUB_WORKSPACE}/tpm-server.pid" + sleep 2 + + - name: Prepare TPM objects using base reference library + working-directory: ./pr-branch + env: + LD_LIBRARY_PATH: ${{ github.workspace }}/base-branch/src/.libs:/usr/local/lib + WOLFPKCS11_TOKEN_PATH: ${{ env.TOKEN_PATH }} + METADATA_FILE: ${{ env.METADATA_FILE }} + run: | + rm -rf "${WOLFPKCS11_TOKEN_PATH}" + mkdir -p "${WOLFPKCS11_TOKEN_PATH}" + rm -f "${METADATA_FILE}" + ./tests/tpm_object_upgrade_test \ + --module ../base-branch/src/.libs/libwolfpkcs11.so \ + --metadata-file "${METADATA_FILE}" \ + --prepare --verbose + + - name: Kill soft TPM after prepare + run: | + if [ -f "${GITHUB_WORKSPACE}/tpm-server.pid" ]; then + TPM_PID="$(cat "${GITHUB_WORKSPACE}/tpm-server.pid")" + kill "${TPM_PID}" || true + wait "${TPM_PID}" 2>/dev/null || true + rm -f "${GITHUB_WORKSPACE}/tpm-server.pid" + fi + pkill -f tpm_server || true + sleep 1 + + - name: Upload NVChip artifact + uses: actions/upload-artifact@v4 + with: + name: tpm-nvchip + path: ibmswtpm2/src/NVChip + + - name: Restart soft TPM + run: | + cd ibmswtpm2/src + ./tpm_server >>"${GITHUB_WORKSPACE}/tpm-server.log" 2>&1 & + echo $! > "${GITHUB_WORKSPACE}/tpm-server.pid" + sleep 2 + + - name: Verify TPM objects using PR library + working-directory: ./pr-branch + env: + LD_LIBRARY_PATH: ${{ github.workspace }}/pr-branch/src/.libs:/usr/local/lib + WOLFPKCS11_TOKEN_PATH: ${{ env.TOKEN_PATH }} + METADATA_FILE: ${{ env.METADATA_FILE }} + run: | + MODULE_PATH=$(find ./src/.libs -maxdepth 1 -name 'libwolfpkcs11.so*' | head -n1) + if [ -z "${MODULE_PATH}" ]; then + echo "Failed to locate libwolfpkcs11 shared object in ./src/.libs" >&2 + ls -al ./src/.libs >&2 || true + exit 1 + fi + ./tests/tpm_object_upgrade_test \ + --module "${MODULE_PATH}" \ + --metadata-file "${METADATA_FILE}" \ + --verify --verbose + + - name: Upload logs on failure + if: failure() || cancelled() + uses: actions/upload-artifact@v4 + with: + name: tpm-object-upgrade-artifacts + path: | + pr-branch/test-suite.log + pr-branch/config.log + base-branch/test-suite.log + base-branch/config.log + tpm-server.log + retention-days: 5 + + - name: Cleanup TPM simulator + if: always() + run: | + if [ -f "${GITHUB_WORKSPACE}/tpm-server.pid" ]; then + kill "$(cat "${GITHUB_WORKSPACE}/tpm-server.pid")" || true + fi + pkill -f tpm_server || true diff --git a/src/internal.c b/src/internal.c index dac23e3..af1ce91 100644 --- a/src/internal.c +++ b/src/internal.c @@ -169,12 +169,76 @@ /* Determine object id from object handle. */ #define OBJ_HANDLE_OBJ_ID(h) ((h) & 0xfffffff) +#define WP11_TOKEN_LOAD_FLAG_ALLOW_REPAIR 0x01 + +#define WP11_TOKEN_STORE_NEEDS_REPAIR_E (-3200) + +typedef struct WP11_TokenLoadReport { + int removedIdx[WP11_TOKEN_OBJECT_CNT_MAX]; + CK_KEY_TYPE removedType[WP11_TOKEN_OBJECT_CNT_MAX]; + CK_OBJECT_CLASS removedClass[WP11_TOKEN_OBJECT_CNT_MAX]; + word32 removedFlags[WP11_TOKEN_OBJECT_CNT_MAX]; + int removedCount; + int labelFixCount; + int rebuiltCount; + int flagFix; + int truncated; +} WP11_TokenLoadReport; + +static void wp11_TokenLoadReport_RemoveAt(WP11_TokenLoadReport* report, + int index) +{ + int i; + + if (report == NULL || index < 0 || index >= report->removedCount) + return; + + for (i = index + 1; i < report->removedCount; i++) { + report->removedIdx[i - 1] = report->removedIdx[i]; + report->removedType[i - 1] = report->removedType[i]; + report->removedClass[i - 1] = report->removedClass[i]; + report->removedFlags[i - 1] = report->removedFlags[i]; + } + + report->removedCount--; + + report->removedIdx[report->removedCount] = 0; + report->removedType[report->removedCount] = 0; + report->removedClass[report->removedCount] = 0; + report->removedFlags[report->removedCount] = 0; + +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("Repair report remove entry -> remaining=%d", report->removedCount); +#endif +} + +#if defined(DEBUG_WOLFPKCS11) && defined(WOLFPKCS11_TPM_STORE) +static const char* wp11_StoreTypeName(int type) +{ + switch (type) { + case WOLFPKCS11_STORE_TOKEN: return "TOKEN"; + case WOLFPKCS11_STORE_OBJECT: return "OBJECT"; + case WOLFPKCS11_STORE_SYMMKEY: return "SYMMKEY"; + case WOLFPKCS11_STORE_RSAKEY_PRIV: return "RSA_PRIV"; + case WOLFPKCS11_STORE_RSAKEY_PUB: return "RSA_PUB"; + case WOLFPKCS11_STORE_ECCKEY_PRIV: return "ECC_PRIV"; + case WOLFPKCS11_STORE_ECCKEY_PUB: return "ECC_PUB"; + case WOLFPKCS11_STORE_DHKEY_PRIV: return "DH_PRIV"; + case WOLFPKCS11_STORE_DHKEY_PUB: return "DH_PUB"; + case WOLFPKCS11_STORE_CERT: return "CERT"; + case WOLFPKCS11_STORE_TRUST: return "TRUST"; + case WOLFPKCS11_STORE_DATA: return "DATA"; + default: return "UNKNOWN"; + } +} +#endif /* DEBUG_WOLFPKCS11 && WOLFPKCS11_TPM_STORE */ + #ifdef SINGLE_THREADED /* Disable locking. */ typedef int WP11_Lock; #define WP11_Lock_Init(l) ({ 0; }) -#define WP11_Lock_Free(l) +#define WP11_Lock_Free(l) (void)(l) #define WP11_Lock_LockRW(l) ({ 0; }) #define WP11_Lock_UnlockRW(l) ({ 0; }) #define WP11_Lock_LockRO(l) ({ 0; }) @@ -514,6 +578,7 @@ typedef struct WP11_Token { int objCnt; /* Count of objects on token */ int tokenFlags; /* Flags for token */ int nextObjId; + WP11_TokenLoadReport* loadReport; /* Active load report (internal use) */ byte userPinEmpty:2; /* Indicates user PIN is empty * 0 = not set * 1 = empty @@ -583,7 +648,8 @@ const WP11_Ecc_Curve DefinedCurves[] = { /* Number of slots. */ #define slotCnt 1 /* List of slot objects. */ -static WP11_Slot slotList[1]; +static WP11_Slot slotList[slotCnt]; +static byte slotInitDone[slotCnt]; /* Global random used in random API, cryptographic operations and generating * seed when creating new hash of PIN. */ @@ -592,6 +658,8 @@ static WC_RNG globalRandom; static int libraryInitCount = 0; /* Lock for globals including global random. */ static WP11_Lock globalLock; +/* Tracks whether global resources are initialized. */ +static int libraryGlobalsReady = 0; #ifndef SINGLE_THREADED @@ -1043,11 +1111,16 @@ static int wolfPKCS11_Store_GetMaxSize(int type, int variableSz) #ifdef WOLFPKCS11_TPM_STORE static word32 wolfPKCS11_Store_Handle(int type, CK_ULONG id1, CK_ULONG id2) { - /* Build unique handle */ + /* Build unique handle (legacy layout preserved for compatibility) + * Layout: + * Bits 23..20 : store type (low nibble) + * Bits 15..8 : token id + * Bits 7..0 : object id + */ word32 nvIndex = WOLFPKCS11_TPM_NV_BASE + - ((type & 0x0F) << 16) + - (((word32)id1 & 0xFF) << 8) + - ((word32)id2 & 0xFF); + (((word32)type & 0x0F) << 16) + + (((word32)id1 & 0xFF) << 8) + + ((word32)id2 & 0xFF); return nvIndex; } #else @@ -1477,6 +1550,11 @@ int wolfPKCS11_Store_OpenSz(int type, CK_ULONG id1, CK_ULONG id2, int read, /* Build unique handle */ nvIndex = wolfPKCS11_Store_Handle(type, id1, id2); +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("Store open type=%s id1=%lu id2=%lu read=%d nvIndex=0x%08x", + wp11_StoreTypeName(type), (unsigned long)id1, (unsigned long)id2, + read, (unsigned int)nvIndex); +#endif maxSz = wolfPKCS11_Store_GetMaxSize(type, variableSz); if (maxSz <= 0) { @@ -1508,11 +1586,27 @@ int wolfPKCS11_Store_OpenSz(int type, CK_ULONG id1, CK_ULONG id2, int read, /* Try and open handle */ ret = wolfTPM2_NVOpen(tpmStore->dev, &tpmStore->nv, nvIndex, NULL, 0); if (ret != 0) { +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("Store NVOpen failed type=%s nvIndex=0x%08x rc=0x%x (%s)", + wp11_StoreTypeName(type), (unsigned int)nvIndex, ret, + wolfTPM2_GetRCString(ret)); +#endif if (!read) { ret = wolfTPM2_NVCreateAuth(tpmStore->dev, &parent, &tpmStore->nv, nvIndex, nvAttributes, maxSz, NULL, 0); +#ifdef DEBUG_WOLFPKCS11 + if (ret != 0) { + WOLFPKCS11_MSG("Store NVCreateAuth failed type=%s nvIndex=0x%08x rc=0x%x (%s)", + wp11_StoreTypeName(type), (unsigned int)nvIndex, ret, + wolfTPM2_GetRCString(ret)); + } +#endif } else { +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("Store read missing handle type=%s nvIndex=0x%08x", + wp11_StoreTypeName(type), (unsigned int)nvIndex); +#endif ret = NOT_AVAILABLE_E; /* read for handle that doesn't exist */ } } @@ -1521,6 +1615,12 @@ int wolfPKCS11_Store_OpenSz(int type, CK_ULONG id1, CK_ULONG id2, int read, /* place handle into pointer */ *store = tpmStore; } +#ifdef DEBUG_WOLFPKCS11 + else { + WOLFPKCS11_MSG("Store open failed type=%s nvIndex=0x%08x ret=%d", + wp11_StoreTypeName(type), (unsigned int)nvIndex, ret); + } +#endif #ifdef WOLFPKCS11_DEBUG_STORE printf("Store Open %p: ret %d, max size %d, handle 0x%x\n", *store, ret, maxSz, nvIndex); @@ -1741,6 +1841,11 @@ int wolfPKCS11_Store_Read(void* store, unsigned char* buffer, int len) if (ctx != NULL && ctx->file != XBADFILE && ctx->file != NULL) { ret = (int)XFREAD(buffer, 1, len, ctx->file); } +#endif +#ifdef DEBUG_WOLFPKCS11 + if (ret < 0) { + WOLFPKCS11_MSG("Store read failure ret=%d", ret); + } #endif return ret; } @@ -1774,6 +1879,13 @@ int wolfPKCS11_Store_Write(void* store, unsigned char* buffer, int len) tpmStore->offset += len; ret = len; } +#ifdef DEBUG_WOLFPKCS11 + else { + WOLFPKCS11_MSG("Store write failure nvIndex=0x%08x rc=0x%x (%s)", + (unsigned int)tpmStore->nv.handle.hndl, ret, + wolfTPM2_GetRCString(ret)); + } +#endif #else if (ctx != NULL && ctx->file != XBADFILE && ctx->file != NULL) { ret = (int)XFWRITE(buffer, 1, len, ctx->file); @@ -1783,6 +1895,11 @@ int wolfPKCS11_Store_Write(void* store, unsigned char* buffer, int len) } } #endif +#ifdef DEBUG_WOLFPKCS11 + if (ret < 0) { + WOLFPKCS11_MSG("Store write failure ret=%d", ret); + } +#endif return ret; } @@ -2234,25 +2351,49 @@ static int wp11_storage_read_alloc_array(void* storage, unsigned char** buffer, int* len) { int ret; + int length = 0; + unsigned char* data = NULL; - /* Read length of array. */ - ret = wp11_storage_read_int(storage, len); - if (ret == 0 && *len > 0) { - /* Allocate buffer to hold data. */ - *buffer = (unsigned char*)XMALLOC(*len, NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (*buffer == NULL) - ret = MEMORY_E; + if (buffer != NULL) + *buffer = NULL; + if (len != NULL) + *len = 0; - if (ret == 0) { - /* Read array data into allocated buffer. */ - ret = wp11_storage_read(storage, *buffer, *len); - if (ret != 0) { - XFREE(*buffer, NULL, DYNAMIC_TYPE_TMP_BUFFER); - *buffer = NULL; - } + /* Read length of array. */ + ret = wp11_storage_read_int(storage, &length); + if (ret == 0) { + if (length < 0) { + ret = BUFFER_E; + } + else if (length > 0) { + /* Allocate buffer to hold data. */ + data = (unsigned char*)XMALLOC(length, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (data == NULL) + ret = MEMORY_E; + } + } + if (ret == 0 && length > 0) { + /* Read array data into allocated buffer. */ + ret = wp11_storage_read(storage, data, length); + if (ret != 0) { + XFREE(data, NULL, DYNAMIC_TYPE_TMP_BUFFER); + data = NULL; } } + if (ret == 0) { + if (buffer != NULL) + *buffer = data; + else if (data != NULL) + XFREE(data, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (len != NULL) + *len = length; + } + else if (data != NULL) { + XFREE(data, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + return ret; } @@ -3351,6 +3492,11 @@ static int WP11_Object_DecodeTpmKey(WP11_Object* object) return BAD_FUNC_ARG; } +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("DecodeTpmKey start class=%lu type=%lu keyDataLen=%d",\ + (unsigned long)object->objClass, (unsigned long)object->type,\ + object->keyDataLen); +#endif /* Extract public size */ XMEMCPY(&pubAreaSize, object->keyData, sizeof(pubAreaSize)); idx += sizeof(pubAreaSize); @@ -3373,11 +3519,25 @@ static int WP11_Object_DecodeTpmKey(WP11_Object* object) object->tpmKey->priv.size); } else { +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("DecodeTpmKey priv.size=%d exceeds buffer (%d)",\ + object->tpmKey->priv.size, + (int)sizeof(object->tpmKey->priv.buffer)); +#endif ret = BUFFER_E; } } +#ifdef DEBUG_WOLFPKCS11 + else { + WOLFPKCS11_MSG("DecodeTpmKey TPM2_ParsePublic failed ret=%d", ret); + } +#endif } else { +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("DecodeTpmKey pubAreaSize=%u exceeds buffer (%zu)",\ + pubAreaSize, sizeof(pubAreaBuffer)); +#endif ret = BUFFER_E; } @@ -3387,6 +3547,11 @@ static int WP11_Object_DecodeTpmKey(WP11_Object* object) /* load public portion into wolf RsaKey structure */ ret = wolfTPM2_RsaKey_TpmToWolf(&object->slot->tpmDev, (WOLFTPM2_KEY*)object->tpmKey, object->data.rsaKey); +#ifdef DEBUG_WOLFPKCS11 + if (ret != 0) { + WOLFPKCS11_MSG("DecodeTpmKey wolfTPM2_RsaKey_TpmToWolf failed ret=%d", ret); + } +#endif #else ret = NOT_COMPILED_IN; #endif @@ -3405,6 +3570,12 @@ static int WP11_Object_DecodeTpmKey(WP11_Object* object) } } +#ifdef DEBUG_WOLFPKCS11 + if (ret != 0) { + WOLFPKCS11_MSG("DecodeTpmKey returning ret=%d", ret); + } +#endif + return ret; } @@ -3477,6 +3648,8 @@ static int WP11_Object_EncodeTpmKey(WP11_Object* object, byte* keyData, #endif /* WOLFPKCS11_TPM */ +static int wp11_Object_Store_Object(WP11_Object* object, int tokenId, int objId); + static int wp11_Object_Store_Data(WP11_Object* object, int tokenId, int objId) { int ret; @@ -3530,6 +3703,9 @@ static int wp11_Object_Decode_RsaKey(WP11_Object* object) int ret = 0; word32 idx = 0; RsaKey* key = object->data.rsaKey; +#ifdef WOLFPKCS11_TPM + int hadTpmFlag = 0; +#endif ret = wc_InitRsaKey_ex(key, NULL, object->devId); if (ret != 0) { @@ -3537,37 +3713,100 @@ static int wp11_Object_Decode_RsaKey(WP11_Object* object) } #ifdef WOLFPKCS11_TPM - if (object->opFlag & WP11_FLAG_TPM) { - ret = WP11_Object_DecodeTpmKey(object); + hadTpmFlag = ((object->opFlag & WP11_FLAG_TPM) != 0); + if ((object->opFlag & WP11_FLAG_TPM) != 0 || + (object->slot != NULL && + object->slot->token.loadReport != NULL && + object->objClass == CKO_PRIVATE_KEY && + object->keyData != NULL && object->keyDataLen > 0)) { + int tpmRet = WP11_Object_DecodeTpmKey(object); + if (tpmRet == 0) { + if (!hadTpmFlag) { + object->opFlag |= WP11_FLAG_TPM; + if (object->slot != NULL && + object->slot->token.loadReport != NULL) { + object->slot->token.loadReport->flagFix = 1; + } + } + object->encoded = 0; + return 0; + } + if (hadTpmFlag) { + if (tpmRet != BUFFER_E && tpmRet != BAD_FUNC_ARG) { + return tpmRet; + } + /* Fall back to standard decode when TPM blob is unavailable. */ + object->opFlag &= (word32)~WP11_FLAG_TPM; +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("TPM decode failed for RSA object (ret=%d)", tpmRet); +#endif + } } - else #endif - if (object->objClass == CKO_PRIVATE_KEY) { + if (ret == 0 && object->objClass == CKO_PRIVATE_KEY) { unsigned char* der; int len = object->keyDataLen - AES_BLOCK_SIZE; + int decryptRet = 0; + int triedPlain = 0; der = (unsigned char*)XMALLOC(len, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (der == NULL) { ret = MEMORY_E; } if (ret == 0) { - ret = wp11_DecryptData(der, object->keyData, len, + decryptRet = wp11_DecryptData(der, object->keyData, len, object->slot->token.key, sizeof(object->slot->token.key), object->iv, sizeof(object->iv), object->devId); + ret = decryptRet; } if (ret == 0) { /* Decode RSA private key. */ ret = wc_RsaPrivateKeyDecode(der, &idx, key, len); +#ifdef DEBUG_WOLFPKCS11 + if (ret != 0) { + WOLFPKCS11_MSG("RSA private decode failed ret=%d", ret); + } +#endif XMEMSET(der, 0, len); } - if (der != NULL) + if (der != NULL) { XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + if (ret != 0 && object->slot != NULL && + object->slot->token.loadReport != NULL) { + word32 plainIdx = 0; + triedPlain = 1; + ret = wc_RsaPrivateKeyDecode(object->keyData, &plainIdx, key, + object->keyDataLen); +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("RSA plaintext decode attempt ret=%d", ret); +#endif + } + if (ret == 0 && triedPlain) { + /* Remove stale encoded data; caller will re-encode. */ + if (object->keyData != NULL) { + XFREE(object->keyData, NULL, DYNAMIC_TYPE_TMP_BUFFER); + object->keyData = NULL; + object->keyDataLen = 0; + } +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("RSA plaintext decode succeeded; keyData cleared for rewrap"); +#endif + } + else if (ret != 0 && triedPlain && decryptRet != 0) { + ret = decryptRet; + } } else { /* Decode RSA public key. */ ret = wc_RsaPublicKeyDecode(object->keyData, &idx, key, object->keyDataLen); +#ifdef DEBUG_WOLFPKCS11 + if (ret != 0) { + WOLFPKCS11_MSG("RSA public decode failed ret=%d", ret); + } +#endif } object->encoded = (ret != 0); @@ -3805,11 +4044,30 @@ static int wp11_Object_Load_RsaKey(WP11_Object* object, int tokenId, int objId) storeType = WOLFPKCS11_STORE_RSAKEY_PUB; /* Open access to RSA key. */ +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("Load RSA key objId=%d class=%lu type=%lu storeType=%d", + objId, + (unsigned long)object->objClass, + (unsigned long)object->type, + storeType); +#endif ret = wp11_storage_open_readonly(storeType, tokenId, objId, &storage); +#ifdef DEBUG_WOLFPKCS11 + if (ret != 0) { + WOLFPKCS11_MSG("Load RSA key failed to open store (type=%d id=%d) ret=%d", + storeType, objId, ret); + } +#endif if (ret == 0) { /* Read of DER encoded RSA key. */ ret = wp11_storage_read_alloc_array(storage, &object->keyData, &object->keyDataLen); +#ifdef DEBUG_WOLFPKCS11 + if (ret != 0) { + WOLFPKCS11_MSG("Load RSA key failed to read data (storeType=%d id=%d) ret=%d", + storeType, objId, ret); + } +#endif wp11_storage_close(storage); } @@ -3876,6 +4134,9 @@ static int wp11_Object_Decode_EccKey(WP11_Object* object) int ret = 0; word32 idx = 0; ecc_key* key = object->data.ecKey; +#ifdef WOLFPKCS11_TPM + int hadTpmFlag = 0; +#endif ret = wc_ecc_init_ex(key, NULL, object->devId); if (ret != 0) { @@ -3883,10 +4144,32 @@ static int wp11_Object_Decode_EccKey(WP11_Object* object) } #ifdef WOLFPKCS11_TPM - if (object->opFlag & WP11_FLAG_TPM) { + hadTpmFlag = ((object->opFlag & WP11_FLAG_TPM) != 0); + if ((object->opFlag & WP11_FLAG_TPM) != 0 || + (object->slot != NULL && + object->slot->token.loadReport != NULL && + object->objClass == CKO_PRIVATE_KEY && + object->keyData != NULL && object->keyDataLen > 0)) { ret = WP11_Object_DecodeTpmKey(object); + if (ret == 0) { + if (!hadTpmFlag) { + object->opFlag |= WP11_FLAG_TPM; + if (object->slot != NULL && + object->slot->token.loadReport != NULL) { + object->slot->token.loadReport->flagFix = 1; + } + } + object->encoded = 0; + return 0; + } + if (hadTpmFlag && ret != BUFFER_E && ret != BAD_FUNC_ARG) { + return ret; + } + if (hadTpmFlag && (ret == BUFFER_E || ret == BAD_FUNC_ARG)) { + object->opFlag &= (word32)~WP11_FLAG_TPM; + ret = 0; + } } - else #endif if (object->objClass == CKO_PRIVATE_KEY) { unsigned char* der; @@ -4508,111 +4791,536 @@ static int wp11_Object_Load_SymmKey(WP11_Object* object, int tokenId, int objId) return ret; } -/** - * Store a symmetric key to storage. - * - * @param [in] object Symmetric key object. - * @param [in] tokenId Id of token this key belongs to. - * @param [in] objId Id of object for token. - * @return 0 on success. - * @return BUFFER_E when storing fails. - * @return NOT_AVAILABLE_E when unable to write data. - */ -static int wp11_Object_Store_SymmKey(WP11_Object* object, int tokenId, - int objId) +static int wp11_Object_Decode(WP11_Object* object); + +#ifndef WOLFPKCS11_NO_STORE +static int wp11_Object_Load_Object(WP11_Object* object, int tokenId, int objId); +#ifndef NO_RSA +static int wp11_Token_RecoverRsaPublicFromPrivate(WP11_Slot* slot, + int tokenId, int pubObjId, WP11_Object* pubObject) { - int ret = 0; - void* storage = NULL; + int ret = NOT_AVAILABLE_E; + int candidates[2]; + int i; - if (object->keyData == NULL) { - ret = wp11_Object_Encode_SymmKey(object); - } + candidates[0] = pubObjId - 1; + candidates[1] = pubObjId + 1; - /* Open access to symmetric key. */ - if (ret == 0) { - ret = wp11_storage_open(WOLFPKCS11_STORE_SYMMKEY, tokenId, objId, - object->keyDataLen, &storage); - } - if (ret == 0) { - /* Write symmetric key to storage. */ - ret = wp11_storage_write_array(storage, object->keyData, - object->keyDataLen); + for (i = 0; i < (int)(sizeof(candidates) / sizeof(candidates[0])); i++) { + WP11_Object* privObject = NULL; + int candidateId = candidates[i]; + int tmpRet = NOT_AVAILABLE_E; - wp11_storage_close(storage); - } + if (candidateId < 0 || candidateId >= WP11_TOKEN_OBJECT_CNT_MAX) + continue; - return ret; -} + tmpRet = wp11_Object_New(slot, pubObject->type, &privObject); + if (tmpRet != 0) + continue; -static int wp11_Object_Load_Object(WP11_Object* object, int tokenId, int objId) -{ - int ret; - void* storage = NULL; - word32 dummy = 0; + privObject->lock = pubObject->lock; - /* Open access to key object. */ - ret = wp11_storage_open_readonly(WOLFPKCS11_STORE_OBJECT, tokenId, objId, - &storage); - if (ret == 0) { - /* Read the IV. (12) */ - ret = wp11_storage_read_fixed_array(storage, object->iv, - sizeof(object->iv)); - if (ret == 0) { - /* Read handle value. (8) */ - ret = wp11_storage_read_ulong(storage, &object->handle); - } - if (ret == 0) { - /* Read object class. (8) */ - ret = wp11_storage_read_ulong(storage, &object->objClass); - } - if (ret == 0) { - /* Read key gen mechanism. (8) */ - ret = wp11_storage_read_ulong(storage, &object->keyGenMech); - } - if (ret == 0) { - /* Read whether the object is on a token. (1) */ - byte onToken = 0; - ret = wp11_storage_read_boolean(storage, &onToken); - if (ret == 0) { - object->onToken = (onToken != 0); - } - } - if (ret == 0) { - /* Read whether the object is local. (1) */ - byte local = 0; - ret = wp11_storage_read_boolean(storage, &local); - if (ret == 0) { - object->local = (local != 0); + tmpRet = wp11_Object_Load_Object(privObject, tokenId, candidateId); + if (tmpRet == 0) + tmpRet = wp11_Object_AllocateTypeData(privObject); + if (tmpRet == 0) + tmpRet = wp11_Object_Load_RsaKey(privObject, tokenId, candidateId); + if (tmpRet == 0) + tmpRet = wp11_Object_Decode(privObject); + if (tmpRet == 0) { +#if defined(WOLFSSL_KEY_GEN) || defined(OPENSSL_EXTRA) || \ + defined(WOLFSSL_CERT_GEN) + int derLen = wc_RsaKeyToPublicDer(privObject->data.rsaKey, NULL, 0); + if (derLen > 0) { + unsigned char* der = (unsigned char*)XMALLOC(derLen, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (der != NULL) { + int encLen = wc_RsaKeyToPublicDer(privObject->data.rsaKey, + der, derLen); + if (encLen > 0) { + word32 idx = 0; + int decRet; + + XFREE(pubObject->keyData, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + pubObject->keyData = der; + pubObject->keyDataLen = encLen; + + decRet = wc_InitRsaKey_ex(pubObject->data.rsaKey, NULL, + pubObject->devId); + if (decRet == 0) { + decRet = wc_RsaPublicKeyDecode(pubObject->keyData, + &idx, pubObject->data.rsaKey, + pubObject->keyDataLen); + } + + if (decRet == 0) { + pubObject->encoded = 0; + pubObject->opFlag |= WP11_FLAG_VERIFY | + WP11_FLAG_ENCRYPT; +#ifdef WOLFPKCS11_TPM + if ((privObject->opFlag & WP11_FLAG_TPM) != 0) + pubObject->opFlag |= WP11_FLAG_TPM; +#endif + if (pubObject->slot != NULL && + pubObject->slot->token.loadReport != NULL) { + WP11_TokenLoadReport* report = + pubObject->slot->token.loadReport; + if (report->rebuiltCount < + WP11_TOKEN_OBJECT_CNT_MAX) + report->rebuiltCount++; + report->flagFix = 1; + } + ret = 0; + } + else { + wc_FreeRsaKey(pubObject->data.rsaKey); + XFREE(pubObject->keyData, NULL, + DYNAMIC_TYPE_TMP_BUFFER); + pubObject->keyData = NULL; + pubObject->keyDataLen = 0; + tmpRet = decRet; + } + } + else { + XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER); + tmpRet = (encLen < 0) ? encLen : BUFFER_E; + } + } + else + tmpRet = MEMORY_E; } - } - if (ret == 0) { - /* Unused word32. (4) */ - ret = wp11_storage_read_word32(storage, &dummy); - } - if (ret == 0) { - /* Read the operational flags of the object. (4) */ - ret = wp11_storage_read_word32(storage, &object->opFlag); - } - if (ret == 0) { - /* Read the start date. (8) */ - ret = wp11_storage_read_fixed_array(storage, - (unsigned char*)object->startDate, sizeof(object->startDate)); - } - if (ret == 0) { - /* Read the end date. (8) */ - ret = wp11_storage_read_fixed_array(storage, - (unsigned char*)object->endDate, sizeof(object->endDate)); + else + tmpRet = (derLen < 0) ? derLen : NOT_AVAILABLE_E; +#else + tmpRet = NOT_COMPILED_IN; +#endif } - if (ret == 0) { - /* Read id for the object. (variable keyIdLen) */ - ret = wp11_storage_read_alloc_array(storage, &object->keyId, - &object->keyIdLen); + WP11_Object_Free(privObject); + + if (tmpRet == 0) { + ret = 0; + break; + } + } + + return ret; +} +#endif /* !NO_RSA */ +static int wp11_Token_RebuildObject(WP11_Slot* slot, WP11_Token* token, + int tokenId, int objId, CK_OBJECT_CLASS objClass, CK_KEY_TYPE keyType, + word32 opFlag) +{ + WP11_Object* object = NULL; + int ret = 0; + + if (token->objCnt >= WP11_TOKEN_OBJECT_CNT_MAX) + return MEMORY_E; + + ret = wp11_Object_New(slot, keyType, &object); + if (ret != 0) + return ret; + + object->lock = &token->lock; + + ret = wp11_Object_Load_Object(object, tokenId, objId); + if (ret != 0) + goto rebuild_fail; + + object->onToken = 1; + object->local = 1; + object->session = NULL; + object->encoded = 0; + + if (objClass != (CK_OBJECT_CLASS)-1) + object->objClass = objClass; + if (keyType != (CK_KEY_TYPE)-1) + object->type = keyType; + if (opFlag != 0) + object->opFlag |= opFlag; + + ret = wp11_Object_AllocateTypeData(object); + if (ret != 0) + goto rebuild_fail; + + switch (object->objClass) { + case CKO_PRIVATE_KEY: + object->opFlag |= WP11_FLAG_PRIVATE | WP11_FLAG_SENSITIVE; + object->opFlag |= WP11_FLAG_SIGN | WP11_FLAG_DECRYPT; + switch (object->type) { +#ifndef NO_RSA + case CKK_RSA: + ret = wp11_Object_Load_RsaKey(object, tokenId, objId); + if (ret == 0) { + int decodeRet = wp11_Object_Decode_RsaKey(object); + if (decodeRet != 0) { + int repaired = 0; + +#ifdef WOLFPKCS11_TPM + if ((object->opFlag & WP11_FLAG_TPM) != 0) { + int rebuildRet = WP11_Object_DecodeTpmKey(object); + if (rebuildRet == 0) + rebuildRet = wp11_Object_Encode_RsaKey(object); + if (rebuildRet == 0) + decodeRet = wp11_Object_Decode_RsaKey(object); + if (rebuildRet == 0 && decodeRet == 0) { + repaired = 1; + if (token->loadReport != NULL) { + WP11_TokenLoadReport* report = + token->loadReport; + if (report->rebuiltCount < + WP11_TOKEN_OBJECT_CNT_MAX) + report->rebuiltCount++; + report->flagFix = 1; + } + } + else if (rebuildRet != 0) + decodeRet = rebuildRet; + } +#endif + if (decodeRet != 0 && repaired == 0) { + wc_FreeRsaKey(object->data.rsaKey); + XMEMSET(object->data.rsaKey, 0, sizeof(RsaKey)); + decodeRet = wp11_Token_RecoverRsaPublicFromPrivate( + slot, tokenId, objId, object); + if (decodeRet == 0) + decodeRet = wp11_Object_Decode_RsaKey(object); + } + if (decodeRet != 0) + ret = decodeRet; + } +#ifdef WOLFPKCS11_TPM + else if (token->loadReport != NULL && + (object->opFlag & WP11_FLAG_TPM) == 0 && + object->keyData == NULL) { + int rewrapRet; +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("Repair rewrapping RSA private key idx=%d", objId); +#endif + object->opFlag |= WP11_FLAG_TPM; + rewrapRet = wp11_Object_Encode_RsaKey(object); + if (rewrapRet == 0) + rewrapRet = wp11_Object_Store_RsaKey(object, tokenId, objId); + if (rewrapRet == 0) + rewrapRet = wp11_Object_Store_Object(object, tokenId, objId); + if (rewrapRet != 0) { +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("Repair rewrap failed idx=%d ret=%d", objId, rewrapRet); +#endif + ret = rewrapRet; + } + else { + if (token->loadReport != NULL) + token->loadReport->flagFix = 1; +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("Repair rewrapped RSA private key idx=%d newLen=%d", + objId, object->keyDataLen); +#endif + } + } +#endif + } + break; +#endif +#ifdef HAVE_ECC + case CKK_EC: + ret = wp11_Object_Load_EccKey(object, tokenId, objId); + break; +#endif +#ifndef NO_DH + case CKK_DH: + ret = wp11_Object_Load_DhKey(object, tokenId, objId); + break; +#endif +#ifndef NO_AES + case CKK_AES: +#endif + case CKK_GENERIC_SECRET: + ret = wp11_Object_Load_SymmKey(object, tokenId, objId); + break; + default: + ret = NOT_AVAILABLE_E; + } + break; + case CKO_PUBLIC_KEY: + object->opFlag |= WP11_FLAG_VERIFY | WP11_FLAG_ENCRYPT; + switch (object->type) { +#ifndef NO_RSA + case CKK_RSA: + ret = wp11_Object_Load_RsaKey(object, tokenId, objId); + if (ret != 0) { + int recoverRet = wp11_Token_RecoverRsaPublicFromPrivate( + slot, tokenId, objId, object); + if (recoverRet == 0) { + ret = 0; + } + else + ret = recoverRet; + } + break; +#endif +#ifdef HAVE_ECC + case CKK_EC: + ret = wp11_Object_Load_EccKey(object, tokenId, objId); + break; +#endif +#ifndef NO_DH + case CKK_DH: + ret = wp11_Object_Load_DhKey(object, tokenId, objId); + break; +#endif + default: + ret = NOT_AVAILABLE_E; + } + break; + case CKO_CERTIFICATE: + ret = wp11_Object_Load_Cert(object, tokenId, objId); + break; + case CKO_DATA: + ret = wp11_Object_Load_Data(object, tokenId, objId); + break; + default: + ret = NOT_AVAILABLE_E; + break; + } + + if (ret == 0) { +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("Rebuilt object idx=%d class=%lu type=%lu keyLen=%d flags=0x%lx", + objId, + (unsigned long)object->objClass, + (unsigned long)object->type, + object->keyDataLen, + (unsigned long)object->opFlag); +#endif +#ifdef WOLFPKCS11_TPM + if ((object->opFlag & WP11_FLAG_TPM) == 0 && + (keyType == CKK_RSA || keyType == CKK_EC || keyType == CKK_DH)) { + object->opFlag |= WP11_FLAG_TPM; + } +#endif + object->next = token->object; + token->object = object; + token->objCnt++; + { + int objIdValue = OBJ_HANDLE_OBJ_ID(object->handle); + if (objIdValue >= token->nextObjId) + token->nextObjId = objIdValue + 1; + } + return 0; + } + +rebuild_fail: + if (object != NULL) + WP11_Object_Free(object); + return ret; +} +#endif /* !WOLFPKCS11_NO_STORE */ + +#ifndef WOLFPKCS11_NO_STORE +static void wp11_Token_ResetObjectList(WP11_Token* token) +{ + WP11_Object* object = token->object; + + while (object != NULL) { + WP11_Object* next = object->next; + WP11_Object_Free(object); + object = next; + } + token->object = NULL; + token->objCnt = 0; + token->nextObjId = 1; +} + +static int wp11_Token_RebuildObjectsFromStore(WP11_Slot* slot, + WP11_Token* token, int tokenId, WP11_TokenLoadReport* report) +{ + int ret = 0; + int objIdx; + int rebuildRet; + + wp11_Token_ResetObjectList(token); + + for (objIdx = 0; objIdx < WP11_TOKEN_OBJECT_CNT_MAX; objIdx++) { + rebuildRet = wp11_Token_RebuildObject(slot, token, tokenId, objIdx, + (CK_OBJECT_CLASS)-1, (CK_KEY_TYPE)-1, 0); + if (rebuildRet == 0) { + if (report != NULL && + report->rebuiltCount < WP11_TOKEN_OBJECT_CNT_MAX) { + report->rebuiltCount++; + } + } + else if (rebuildRet == NOT_AVAILABLE_E || + rebuildRet == BUFFER_E) { + continue; + } + else { + ret = rebuildRet; + break; + } + } + + if (ret == NOT_AVAILABLE_E) + ret = 0; + + if (token->nextObjId == 0) + token->nextObjId = 1; + + return ret; +} +#endif /* !WOLFPKCS11_NO_STORE */ + +/** + * Store a symmetric key to storage. + * + * @param [in] object Symmetric key object. + * @param [in] tokenId Id of token this key belongs to. + * @param [in] objId Id of object for token. + * @return 0 on success. + * @return BUFFER_E when storing fails. + * @return NOT_AVAILABLE_E when unable to write data. + */ +static int wp11_Object_Store_SymmKey(WP11_Object* object, int tokenId, + int objId) +{ + int ret = 0; + void* storage = NULL; + + if (object->keyData == NULL) { + ret = wp11_Object_Encode_SymmKey(object); + } + + /* Open access to symmetric key. */ + if (ret == 0) { + ret = wp11_storage_open(WOLFPKCS11_STORE_SYMMKEY, tokenId, objId, + object->keyDataLen, &storage); + } + if (ret == 0) { + /* Write symmetric key to storage. */ + ret = wp11_storage_write_array(storage, object->keyData, + object->keyDataLen); + + wp11_storage_close(storage); + } + + return ret; +} + +static int wp11_Object_Load_Object(WP11_Object* object, int tokenId, int objId) +{ + int ret; + void* storage = NULL; + word32 dummy = 0; + int labelMissing = 0; + + /* Open access to key object. */ +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("Load object metadata objId=%d", objId); +#endif + ret = wp11_storage_open_readonly(WOLFPKCS11_STORE_OBJECT, tokenId, objId, + &storage); +#ifdef DEBUG_WOLFPKCS11 + if (ret != 0) { + WOLFPKCS11_MSG("Load object metadata open failed objId=%d ret=%d", objId, ret); + } +#endif + if (ret == 0) { + /* Read the IV. (12) */ + ret = wp11_storage_read_fixed_array(storage, object->iv, + sizeof(object->iv)); +#ifdef DEBUG_WOLFPKCS11 + if (ret != 0) { + WOLFPKCS11_MSG("Load object metadata read failed at IV objId=%d ret=%d", objId, ret); + } +#endif + if (ret == 0) { + /* Read handle value. (8) */ + ret = wp11_storage_read_ulong(storage, &object->handle); +#ifdef DEBUG_WOLFPKCS11 + if (ret != 0) + WOLFPKCS11_MSG("Load object metadata read failed at handle objId=%d ret=%d", objId, ret); +#endif + } + if (ret == 0) { + /* Read object class. (8) */ + ret = wp11_storage_read_ulong(storage, &object->objClass); +#ifdef DEBUG_WOLFPKCS11 + if (ret != 0) + WOLFPKCS11_MSG("Load object metadata read failed at class objId=%d ret=%d", objId, ret); +#endif + } + if (ret == 0) { + /* Read key gen mechanism. (8) */ + ret = wp11_storage_read_ulong(storage, &object->keyGenMech); +#ifdef DEBUG_WOLFPKCS11 + if (ret != 0) + WOLFPKCS11_MSG("Load object metadata read failed at keyGen objId=%d ret=%d", objId, ret); +#endif + } + if (ret == 0) { + /* Read whether the object is on a token. (1) */ + byte onToken = 0; + ret = wp11_storage_read_boolean(storage, &onToken); + if (ret == 0) { + object->onToken = (onToken != 0); + } + } + if (ret == 0) { + /* Read whether the object is local. (1) */ + byte local = 0; + ret = wp11_storage_read_boolean(storage, &local); + if (ret == 0) { + object->local = (local != 0); + } } if (ret == 0) { + /* Unused word32. (4) */ + ret = wp11_storage_read_word32(storage, &dummy); + } + if (ret == 0) { + /* Read the operational flags of the object. (4) */ + ret = wp11_storage_read_word32(storage, &object->opFlag); + } + if (ret == 0) { + /* Read the start date. (8) */ + ret = wp11_storage_read_fixed_array(storage, + (unsigned char*)object->startDate, sizeof(object->startDate)); + } + if (ret == 0) { + /* Read the end date. (8) */ + ret = wp11_storage_read_fixed_array(storage, + (unsigned char*)object->endDate, sizeof(object->endDate)); + } + + if (ret == 0) { + /* Read id for the object. (variable keyIdLen) + * Treat truncated or missing data as recoverable when repairing. + */ + ret = wp11_storage_read_alloc_array(storage, &object->keyId, + &object->keyIdLen); + if (ret == BUFFER_E || ret == NOT_AVAILABLE_E) { + ret = 0; + object->keyId = NULL; + object->keyIdLen = 0; + } + } + if (ret == 0) { + int labelRet; /* Read label for the object. (variable labelLen) */ - ret = wp11_storage_read_alloc_array(storage, &object->label, + labelRet = wp11_storage_read_alloc_array(storage, &object->label, &object->labelLen); + if (labelRet == 0) + ret = 0; + else if (labelRet == BUFFER_E || labelRet == NOT_AVAILABLE_E || + labelRet != MEMORY_E) { + object->label = NULL; + object->labelLen = 0; + labelMissing = 1; + ret = 0; + } + else + ret = labelRet; } if (ret == 0) { /* Read issuer of the object. (variable issuerLen) */ @@ -4637,8 +5345,14 @@ static int wp11_Object_Load_Object(WP11_Object* object, int tokenId, int objId) /* Read email of the object. (variable emailLen) */ ret = wp11_storage_read_alloc_array(storage, &object->email, &object->emailLen); - if (ret == BUFFER_E) + if (ret == 0) + ret = 0; + else if (ret == BUFFER_E || ret == NOT_AVAILABLE_E || + ret != MEMORY_E) { + object->email = NULL; + object->emailLen = 0; ret = 0; + } } #endif } @@ -4648,10 +5362,31 @@ static int wp11_Object_Load_Object(WP11_Object* object, int tokenId, int objId) */ ret = 0; } + else if (ret != MEMORY_E) { + /* Treat other failures as missing optional data. */ + ret = 0; + object->issuer = NULL; + object->issuerLen = 0; + object->serial = NULL; + object->serialLen = 0; + object->subject = NULL; + object->subjectLen = 0; +#ifdef WOLFPKCS11_NSS + object->email = NULL; + object->emailLen = 0; +#endif + object->category = 0; + } } wp11_storage_close(storage); } + if (ret == 0 && labelMissing && object->slot != NULL && + object->slot->token.loadReport != NULL) { + WP11_TokenLoadReport* report = object->slot->token.loadReport; + if (report->labelFixCount < WP11_TOKEN_OBJECT_CNT_MAX) + report->labelFixCount++; + } return ret; } @@ -5146,6 +5881,37 @@ static void wp11_Token_Final(WP11_Token* token) } #ifndef WOLFPKCS11_NO_STORE +static void wp11_Token_UpdateFlagsFromPins(WP11_Token* token, + WP11_TokenLoadReport* report) +{ + int flagFix = 0; + + if (token->userPinLen > 0) { + if ((token->tokenFlags & WP11_TOKEN_FLAG_USER_PIN_SET) == 0) { + token->tokenFlags |= WP11_TOKEN_FLAG_USER_PIN_SET; + flagFix = 1; + } + } + else if ((token->tokenFlags & WP11_TOKEN_FLAG_USER_PIN_SET) != 0) { + token->tokenFlags &= ~WP11_TOKEN_FLAG_USER_PIN_SET; + flagFix = 1; + } + + if (token->soPinLen > 0) { + if ((token->tokenFlags & WP11_TOKEN_FLAG_SO_PIN_SET) == 0) { + token->tokenFlags |= WP11_TOKEN_FLAG_SO_PIN_SET; + flagFix = 1; + } + } + else if ((token->tokenFlags & WP11_TOKEN_FLAG_SO_PIN_SET) != 0) { + token->tokenFlags &= ~WP11_TOKEN_FLAG_SO_PIN_SET; + flagFix = 1; + } + + if (flagFix && report != NULL) + report->flagFix = 1; +} + /** * Load a token from storage. * @@ -5156,7 +5922,8 @@ static void wp11_Token_Final(WP11_Token* token) * @return BUFFER_E when loading fails. * @return NOT_AVAILABLE_E when unable to locate data. */ -static int wp11_Token_Load(WP11_Slot* slot, int tokenId, WP11_Token* token) +static int wp11_Token_LoadEx(WP11_Slot* slot, int tokenId, WP11_Token* token, + unsigned int flags, WP11_TokenLoadReport* report) { int ret; int i; @@ -5166,121 +5933,451 @@ static int wp11_Token_Load(WP11_Slot* slot, int tokenId, WP11_Token* token) int objCnt = 0; word32 len; + if (report != NULL) + XMEMSET(report, 0, sizeof(*report)); + /* Open access to token object. */ ret = wp11_storage_open_readonly(WOLFPKCS11_STORE_TOKEN, tokenId, 0, &storage); if (ret == 0) { + int allowRepair = ((flags & WP11_TOKEN_LOAD_FLAG_ALLOW_REPAIR) != 0); + int truncated = 0; + int truncatedMeta = 0; + int metadataObjectsLoaded = 0; + /* Read label for token. (32) */ ret = wp11_storage_read_string(storage, token->label, - sizeof(token->label)); + sizeof(token->label)); + if (ret == BUFFER_E && allowRepair) { + truncated = 1; + truncatedMeta = 1; + XMEMSET(token->label, 0, sizeof(token->label)); + ret = 0; + goto token_finish; + } if (ret == 0) { /* Read Security Officer's PIN. (32) */ ret = wp11_storage_read_array(storage, token->soPin, &len, - sizeof(token->soPin)); + sizeof(token->soPin)); + if (ret == BUFFER_E && allowRepair) { + truncated = 1; + if (!metadataObjectsLoaded) + truncatedMeta = 1; + len = 0; + XMEMSET(token->soPin, 0, sizeof(token->soPin)); + ret = 0; + goto token_finish; + } } if (ret == 0) { /* Read Security Officer's PIN seed. (16) */ token->soPinLen = len; ret = wp11_storage_read_fixed_array(storage, token->soPinSeed, - sizeof(token->soPinSeed)); + sizeof(token->soPinSeed)); + if (ret == BUFFER_E && allowRepair) { + truncated = 1; + if (!metadataObjectsLoaded) + truncatedMeta = 1; + XMEMSET(token->soPinSeed, 0, sizeof(token->soPinSeed)); + ret = 0; + goto token_finish; + } } if (ret == 0) { /* Read Security Officer's failed login count. (4) */ ret = wp11_storage_read_int(storage, &token->soFailedLogin); + if (ret == BUFFER_E && allowRepair) { + truncated = 1; + if (!metadataObjectsLoaded) + truncatedMeta = 1; + token->soFailedLogin = 0; + ret = 0; + goto token_finish; + } } if (ret == 0) { /* Read time of last failed login as Security Officer. (8) */ ret = wp11_storage_read_time(storage, &token->soLastFailedLogin); + if (ret == BUFFER_E && allowRepair) { + truncated = 1; + if (!metadataObjectsLoaded) + truncatedMeta = 1; + token->soLastFailedLogin = 0; + ret = 0; + goto token_finish; + } } if (ret == 0) { /* Read failed login timeout for Security Officer. (8) */ ret = wp11_storage_read_time(storage, &token->soFailLoginTimeout); + if (ret == BUFFER_E && allowRepair) { + truncated = 1; + if (!metadataObjectsLoaded) + truncatedMeta = 1; + token->soFailLoginTimeout = 0; + ret = 0; + goto token_finish; + } } if (ret == 0) { /* Read User's PIN. (32) */ token->userPinEmpty = 0; ret = wp11_storage_read_array(storage, token->userPin, &len, - sizeof(token->userPin)); + sizeof(token->userPin)); + if (ret == BUFFER_E && allowRepair) { + truncated = 1; + if (!metadataObjectsLoaded) + truncatedMeta = 1; + len = 0; + XMEMSET(token->userPin, 0, sizeof(token->userPin)); + ret = 0; + goto token_finish; + } } if (ret == 0) { /* Read User's PIN seed. (16) */ token->userPinLen = len; ret = wp11_storage_read_fixed_array(storage, token->userPinSeed, - sizeof(token->userPinSeed)); + sizeof(token->userPinSeed)); + if (ret == BUFFER_E && allowRepair) { + truncated = 1; + if (!metadataObjectsLoaded) + truncatedMeta = 1; + XMEMSET(token->userPinSeed, 0, sizeof(token->userPinSeed)); + ret = 0; + goto token_finish; + } } if (ret == 0) { /* Read User's failed login count. (4)) */ ret = wp11_storage_read_int(storage, &token->userFailedLogin); + if (ret == BUFFER_E && allowRepair) { + truncated = 1; + if (!metadataObjectsLoaded) + truncatedMeta = 1; + token->userFailedLogin = 0; + ret = 0; + goto token_finish; + } } if (ret == 0) { /* Read time of last failed login as User. (8) */ ret = wp11_storage_read_time(storage, &token->userLastFailedLogin); + if (ret == BUFFER_E && allowRepair) { + truncated = 1; + if (!metadataObjectsLoaded) + truncatedMeta = 1; + token->userLastFailedLogin = 0; + ret = 0; + goto token_finish; + } } if (ret == 0) { /* Read failed login timeout for User. (8) */ ret = wp11_storage_read_time(storage, &token->userFailLoginTimeout); + if (ret == BUFFER_E && allowRepair) { + truncated = 1; + if (!metadataObjectsLoaded) + truncatedMeta = 1; + token->userFailLoginTimeout = 0; + ret = 0; + goto token_finish; + } } if (ret == 0) { /* Read seed used to calculate key. (16) */ ret = wp11_storage_read_fixed_array(storage, token->seed, - sizeof(token->seed)); + sizeof(token->seed)); + if (ret == BUFFER_E && allowRepair) { + truncated = 1; + if (!metadataObjectsLoaded) + truncatedMeta = 1; + XMEMSET(token->seed, 0, sizeof(token->seed)); + ret = 0; + goto token_finish; + } } if (ret == 0) { /* Read count of object on token. (4) */ ret = wp11_storage_read_int(storage, &objCnt); + if (ret == BUFFER_E && allowRepair) { + truncated = 1; + if (!metadataObjectsLoaded) + truncatedMeta = 1; + objCnt = 0; + ret = 0; + goto token_finish; + } } - /* Create an objects. */ + /* Create objects from metadata. */ current = &token->object; for (i = 0; (ret == 0) && (i < objCnt); i++) { CK_KEY_TYPE type; - /* Read type of key object for creation of key object. - * (variable objCnt * 8) */ ret = wp11_storage_read_ulong(storage, &type); + if (ret == BUFFER_E && allowRepair) { + truncated = 1; + if (!metadataObjectsLoaded) + truncatedMeta = 1; + ret = 0; + goto token_finish; + } if (ret == 0) { object = NULL; ret = wp11_Object_New(slot, type, &object); } if (ret == 0) { object->lock = &token->lock; - /* Add to end of list. */ *current = object; current = &object->next; token->objCnt++; } } + if (ret != 0) + goto token_finish; + + metadataObjectsLoaded = 1; + + /* Read token flags. This may be missing on older versions. */ + ret = wp11_storage_read_int(storage, &token->tokenFlags); + if (ret == BUFFER_E) { + if (token->userPinLen > 0) + token->tokenFlags |= WP11_TOKEN_FLAG_USER_PIN_SET; + if (token->soPinLen > 0) + token->tokenFlags |= WP11_TOKEN_FLAG_SO_PIN_SET; + token->nextObjId = 1; + ret = 0; + } if (ret == 0) { - /* Read token flags. This is a new variable, so might not exist on - * an upgrade. If not set, it was from a version that doesn't - * support empty pins. So, we can calculate it from the pin lengths. - */ - ret = wp11_storage_read_int(storage, &token->tokenFlags); + ret = wp11_storage_read_int(storage, &token->nextObjId); if (ret == BUFFER_E) { - if (token->userPinLen > 0) { - token->tokenFlags |= WP11_TOKEN_FLAG_USER_PIN_SET; - } - if (token->soPinLen > 0) { - token->tokenFlags |= WP11_TOKEN_FLAG_SO_PIN_SET; - } token->nextObjId = 1; ret = 0; } - else { - ret = wp11_storage_read_int(storage, &token->nextObjId); - if (ret == BUFFER_E || token->nextObjId == 0) { - token->nextObjId = 1; - ret = 0; + else if (ret == 0 && token->nextObjId == 0) { + token->nextObjId = 1; + } + } +token_finish: + if (storage != NULL) { + wp11_storage_close(storage); + storage = NULL; + } + if (token->nextObjId == 0) + token->nextObjId = 1; + wp11_Token_UpdateFlagsFromPins(token, report); + if (allowRepair && truncated) { + if (report != NULL) + report->truncated = 1; + if (truncatedMeta) { + int rebuildRet = wp11_Token_RebuildObjectsFromStore(slot, token, + tokenId, report); + if (rebuildRet != 0 && ret == 0) + ret = rebuildRet; + } + if (ret == 0) + ret = WP11_TOKEN_STORE_NEEDS_REPAIR_E; + } + + object = token->object; + token->loadReport = report; + { + WP11_Object* prev = NULL; + int objIdx = token->objCnt - 1; + int savedRet = ret; + + if (ret == WP11_TOKEN_STORE_NEEDS_REPAIR_E) + ret = 0; + + while ((ret == 0) && (object != NULL) && (objIdx >= 0)) { + int loadRet = wp11_Object_Load(object, tokenId, objIdx); + + if (loadRet == 0) { +#ifdef WOLFPKCS11_TPM + int skipObject = 0; + if ((flags & WP11_TOKEN_LOAD_FLAG_ALLOW_REPAIR) != 0 && + (report != NULL) && + (object->opFlag & WP11_FLAG_TPM) != 0) { + int tpmRet = 0; + WOLFTPM2_KEYBLOB tmpBlob; + WP11_Object tmpObj; + + XMEMSET(&tmpBlob, 0, sizeof(tmpBlob)); + tmpObj = *object; + if (object->tpmKey != NULL) { + XMEMCPY(&tmpBlob, object->tpmKey, sizeof(tmpBlob)); + } + tmpObj.tpmKey = &tmpBlob; + + if (object->type == CKK_RSA) { + #ifndef NO_RSA + RsaKey tmpRsa; + + XMEMSET(&tmpRsa, 0, sizeof(tmpRsa)); + tpmRet = wc_InitRsaKey_ex(&tmpRsa, NULL, object->devId); + if (tpmRet == 0) { + tmpObj.data.rsaKey = &tmpRsa; + tpmRet = WP11_Object_DecodeTpmKey(&tmpObj); + } + wc_FreeRsaKey(&tmpRsa); + #else + tpmRet = NOT_COMPILED_IN; + #endif + } + else if (object->type == CKK_EC) { + #ifdef HAVE_ECC + ecc_key tmpEcc; + + XMEMSET(&tmpEcc, 0, sizeof(tmpEcc)); + tpmRet = wc_ecc_init_ex(&tmpEcc, NULL, object->devId); + if (tpmRet == 0) { + tmpObj.data.ecKey = &tmpEcc; + tpmRet = WP11_Object_DecodeTpmKey(&tmpObj); + } + wc_ecc_free(&tmpEcc); + #else + tpmRet = NOT_COMPILED_IN; + #endif + } + + if (tpmRet != 0) { + skipObject = 1; + if (report->removedCount < WP11_TOKEN_OBJECT_CNT_MAX) { + report->removedIdx[report->removedCount] = objIdx; + report->removedType[report->removedCount] = object->type; + report->removedClass[report->removedCount] = object->objClass; + report->removedFlags[report->removedCount] = object->opFlag; + report->removedCount++; +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("Repair removing corrupt TPM object idx=%d type=%lu class=%lu flags=0x%lx", + objIdx, + (unsigned long)object->type, + (unsigned long)object->objClass, + (unsigned long)object->opFlag); +#endif + } + { + WP11_Object* toFree = object; + object = object->next; + if (prev == NULL) + token->object = object; + else + prev->next = object; + WP11_Object_Free(toFree); + token->objCnt--; + } + } + } + if (!skipObject) { + prev = object; + object = object->next; + } +#else + prev = object; + object = object->next; +#endif + } + else if ((flags & WP11_TOKEN_LOAD_FLAG_ALLOW_REPAIR) != 0 && + (loadRet == NOT_AVAILABLE_E || + loadRet == BUFFER_E)) { + if (report != NULL && + report->removedCount < WP11_TOKEN_OBJECT_CNT_MAX) { + report->removedIdx[report->removedCount] = objIdx; + report->removedType[report->removedCount] = object->type; + report->removedClass[report->removedCount] = object->objClass; + report->removedFlags[report->removedCount] = object->opFlag; + report->removedCount++; +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("Repair removing missing object idx=%d type=%lu class=%lu flags=0x%lx", + objIdx, + (unsigned long)object->type, + (unsigned long)object->objClass, + (unsigned long)object->opFlag); +#endif + } + { + WP11_Object* toFree = object; + object = object->next; + if (prev == NULL) + token->object = object; + else + prev->next = object; + WP11_Object_Free(toFree); + token->objCnt--; + } } + else { + ret = loadRet; + } + objIdx--; } + + if (ret == 0 && savedRet == WP11_TOKEN_STORE_NEEDS_REPAIR_E) + ret = savedRet; } - wp11_storage_close(storage); + if (ret == 0 && report != NULL && + (flags & WP11_TOKEN_LOAD_FLAG_ALLOW_REPAIR) != 0) { + int rebuildIdx; - object = token->object; - for (i = token->objCnt - 1; (ret == 0) && (i >= 0); i--) { - /* Load the objects. */ - ret = wp11_Object_Load(object, tokenId, i); - object = object->next; +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("Repair pass: %d object(s) flagged for rebuild", + report->removedCount); +#endif + for (rebuildIdx = 0; rebuildIdx < report->removedCount; rebuildIdx++) { + CK_OBJECT_CLASS rebuildClass = report->removedClass[rebuildIdx]; + CK_KEY_TYPE rebuildType = report->removedType[rebuildIdx]; + + if (rebuildClass == 0 && rebuildType == 0) + continue; + + if (rebuildClass == 0) { + switch (rebuildType) { + case CKK_RSA: + case CKK_EC: + case CKK_DH: + case CKK_GENERIC_SECRET: + case CKK_AES: + rebuildClass = CKO_PRIVATE_KEY; + break; + default: + rebuildClass = CKO_DATA; + break; + } + } + + { + int rebuildRet = wp11_Token_RebuildObject(slot, token, + tokenId, report->removedIdx[rebuildIdx], rebuildClass, + rebuildType, report->removedFlags[rebuildIdx]); + if (rebuildRet == 0) { +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("Repair rebuilt token object idx=%d class=%lu type=%lu flags=0x%lx", + report->removedIdx[rebuildIdx], + (unsigned long)rebuildClass, + (unsigned long)rebuildType, + (unsigned long)report->removedFlags[rebuildIdx]); +#endif + wp11_TokenLoadReport_RemoveAt(report, rebuildIdx); + report->rebuiltCount++; + rebuildIdx--; + } +#ifdef DEBUG_WOLFPKCS11 + else { + WOLFPKCS11_MSG("Repair failed to rebuild token object idx=%d class=%lu type=%lu; leaving in removed list (ret=%d)", + report->removedIdx[rebuildIdx], + (unsigned long)rebuildClass, + (unsigned long)rebuildType, + rebuildRet); + } +#endif + } + } + } + token->loadReport = NULL; + if (ret == NOT_AVAILABLE_E || ret == BUFFER_E) { + if ((flags & WP11_TOKEN_LOAD_FLAG_ALLOW_REPAIR) == 0) + ret = WP11_TOKEN_STORE_NEEDS_REPAIR_E; } if (ret == 0) { @@ -5299,7 +6396,7 @@ static int wp11_Token_Load(WP11_Slot* slot, int tokenId, WP11_Token* token) #endif } - if (ret != 0) { + if (ret != 0 && ret != WP11_TOKEN_STORE_NEEDS_REPAIR_E) { ret = CKR_DEVICE_ERROR; } } @@ -5311,6 +6408,11 @@ static int wp11_Token_Load(WP11_Slot* slot, int tokenId, WP11_Token* token) return ret; } +static int wp11_Token_Load(WP11_Slot* slot, int tokenId, WP11_Token* token) +{ + return wp11_Token_LoadEx(slot, tokenId, token, 0, NULL); +} + /** * Store a token to storage. * @@ -5558,9 +6660,13 @@ static void wp11_TpmFinal(WP11_Slot* slot) */ static void wp11_Slot_Final(WP11_Slot* slot) { + int slotIdx = -1; + if (slot == NULL) { return; } + if (slot->id > 0 && slot->id <= slotCnt) + slotIdx = (int)(slot->id - 1); while (slot->session != NULL) { wp11_Slot_FreeSession(slot, slot->session); } @@ -5569,6 +6675,8 @@ static void wp11_Slot_Final(WP11_Slot* slot) wp11_TpmFinal(slot); #endif WP11_Lock_Free(&slot->lock); + if (slotIdx >= 0) + slotInitDone[slotIdx] = 0; } /** @@ -5614,6 +6722,10 @@ static int wp11_Slot_Init(WP11_Slot* slot, int id) } } + if (ret == 0 && id > 0 && id <= slotCnt) { + slotInitDone[id - 1] = 1; + } + return ret; } @@ -5649,6 +6761,56 @@ static int wp11_Slot_Store(WP11_Slot* slot, int id) } #endif +static int wp11_Library_EnsureGlobals(void) +{ + int ret = 0; + int lockInitialized = 0; + int cryptInitialized = 0; + int rngInitialized = 0; + + if (libraryGlobalsReady) + return 0; + + ret = WP11_Lock_Init(&globalLock); + if (ret == 0) { + lockInitialized = 1; + + ret = wolfCrypt_Init(); + } + if (ret == 0) { + cryptInitialized = 1; +#ifdef WC_RNG_SEED_CB + ret = wc_SetSeed_Cb(wc_GenerateSeed); +#endif + } +#ifdef HAVE_FIPS + if (ret == 0) { + ret = wc_RunAllCast_fips(); + } +#endif + if (ret == 0) { +#ifdef WOLFSSL_MAXQ10XX_CRYPTO + ret = wc_InitRng_ex(&globalRandom, NULL, MAXQ_DEVICE_ID); +#else + ret = wc_InitRng(&globalRandom); +#endif + } + if (ret == 0) { + rngInitialized = 1; + libraryGlobalsReady = 1; + } + else { + if (rngInitialized) + wc_FreeRng(&globalRandom); + if (cryptInitialized) + wolfCrypt_Cleanup(); + if (lockInitialized) + WP11_Lock_Free(&globalLock); + } + + return ret; +} + /** * Initialize the globals for the library. * Multiple initializations allowed. @@ -5663,35 +6825,18 @@ int WP11_Library_Init(void) int i; if (libraryInitCount == 0) { - ret = WP11_Lock_Init(&globalLock); - if (ret == 0) { - - ret = wolfCrypt_Init(); -#ifdef WC_RNG_SEED_CB - if (ret == 0) { - ret = wc_SetSeed_Cb(wc_GenerateSeed); - } -#endif -#ifdef HAVE_FIPS - if (ret == 0) { - ret = wc_RunAllCast_fips(); - } -#endif - if (ret == 0) { -#ifdef WOLFSSL_MAXQ10XX_CRYPTO - ret = wc_InitRng_ex(&globalRandom, NULL, MAXQ_DEVICE_ID); -#else - ret = wc_InitRng(&globalRandom); -#endif - } - - } + ret = wp11_Library_EnsureGlobals(); for (i = 0; (ret == 0) && (i < slotCnt); i++) { + if (slotInitDone[i]) { + wp11_Slot_Final(&slotList[i]); + } ret = wp11_Slot_Init(&slotList[i], i + 1); } #ifndef WOLFPKCS11_NO_STORE for (i = 0; (ret == 0) && (i < slotCnt); i++) { ret = wp11_Slot_Load(&slotList[i], i + 1); + if (ret == WP11_TOKEN_STORE_NEEDS_REPAIR_E) + break; } #endif } @@ -5740,6 +6885,7 @@ void WP11_Library_Final(void) wc_FreeRng(&globalRandom); WP11_Lock_Free(&globalLock); wolfCrypt_Cleanup(); + libraryGlobalsReady = 0; } } @@ -5764,6 +6910,33 @@ int WP11_Library_IsInitialized(void) return ret; } +#ifdef WOLFPKCS11_NO_STORE +#define wp11_ErrorToCkr(err) CKR_FUNCTION_FAILED +#else +static CK_RV wp11_ErrorToCkr(int err) +{ + if (err == 0) + return CKR_OK; + if (err == WP11_TOKEN_STORE_NEEDS_REPAIR_E) + return CKR_WOLFPKCS11_TOKEN_REPAIR_NEEDED; + switch (err) { + case MEMORY_E: + return CKR_HOST_MEMORY; + case BAD_FUNC_ARG: + return CKR_ARGUMENTS_BAD; + case NOT_AVAILABLE_E: + case BUFFER_E: + return CKR_DEVICE_ERROR; + default: + break; + } + if (err > 0) + return (CK_RV)err; + + return CKR_FUNCTION_FAILED; +} +#endif + /** * Check if slot id is valid. * @@ -5831,6 +7004,135 @@ int WP11_Slot_Get(CK_SLOT_ID slotId, WP11_Slot** slot) return ret; } +#ifndef WOLFPKCS11_NO_STORE +WP11_API CK_RV wolfPKCS11_TokenRepair(CK_SLOT_ID slotId, CK_FLAGS flags) +{ + CK_RV rv = CKR_OK; + int ret; + int slotIdx; + WP11_Slot* slot; + WP11_TokenLoadReport report; + static const int cleanupTypes[] = { + WOLFPKCS11_STORE_OBJECT, + WOLFPKCS11_STORE_SYMMKEY, + WOLFPKCS11_STORE_RSAKEY_PRIV, + WOLFPKCS11_STORE_RSAKEY_PUB, + WOLFPKCS11_STORE_ECCKEY_PRIV, + WOLFPKCS11_STORE_ECCKEY_PUB, + WOLFPKCS11_STORE_DHKEY_PRIV, + WOLFPKCS11_STORE_DHKEY_PUB, + WOLFPKCS11_STORE_CERT, + WOLFPKCS11_STORE_TRUST, + WOLFPKCS11_STORE_DATA + }; + char emptyLabel[LABEL_SZ] = { 0 }; + + if (flags != 0) + return CKR_ARGUMENTS_BAD; + if (!WP11_SlotIdValid(slotId)) + return CKR_SLOT_ID_INVALID; + + ret = wp11_Library_EnsureGlobals(); + if (ret != 0) + return wp11_ErrorToCkr(ret); + + slotIdx = (int)(slotId - 1); + slot = &slotList[slotIdx]; + + if (slotInitDone[slotIdx]) { + wp11_Slot_Final(slot); + } + + ret = wp11_Slot_Init(slot, (int)slotId); + if (ret != 0) { + rv = wp11_ErrorToCkr(ret); + goto cleanup_slot; + } + + XMEMSET(&report, 0, sizeof(report)); + + ret = wp11_Token_LoadEx(slot, (int)slotId, &slot->token, + WP11_TOKEN_LOAD_FLAG_ALLOW_REPAIR, &report); +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("TokenRepair start slot=%lu ret=%d removed=%d rebuilt=%d flagFix=%d truncated=%d", + (unsigned long)slotId, ret, report.removedCount, report.rebuiltCount, + report.flagFix, report.truncated); +#endif + if (ret != 0) { + rv = wp11_ErrorToCkr(ret); + if (rv == CKR_DEVICE_ERROR || rv == CKR_WOLFPKCS11_TOKEN_REPAIR_NEEDED) { + if (rv == CKR_DEVICE_ERROR) + report.flagFix = 1; + rv = CKR_OK; + ret = 0; + } + else { + goto cleanup_token; + } + } + + if (report.removedCount == 0 && report.labelFixCount == 0 && + report.flagFix == 0 && report.rebuiltCount == 0 && + report.truncated == 0) { + rv = CKR_OK; + goto cleanup_token; + } + + ret = wp11_Token_Store(&slot->token, (int)slotId); + if (ret != 0) { + rv = wp11_ErrorToCkr(ret); + goto cleanup_token; + } + +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("TokenRepair summary slot=%lu removed=%d rebuilt=%d labelFix=%d flagFix=%d truncated=%d", + (unsigned long)slotId, report.removedCount, report.rebuiltCount, + report.labelFixCount, report.flagFix, report.truncated); +#endif + + if (report.removedCount > 0) { + int i, t; + + for (i = 0; i < report.removedCount; i++) { + int idx = report.removedIdx[i]; +#ifdef DEBUG_WOLFPKCS11 + WOLFPKCS11_MSG("TokenRepair removing leftover object idx=%d type=%lu class=%lu flags=0x%lx", + idx, + (unsigned long)report.removedType[i], + (unsigned long)report.removedClass[i], + (unsigned long)report.removedFlags[i]); +#endif + + if (idx < slot->token.objCnt) + continue; + + for (t = 0; t < (int)(sizeof(cleanupTypes) / sizeof(cleanupTypes[0])); t++) { + (void)wp11_storage_remove(cleanupTypes[t], + (CK_ULONG)slotId, (CK_ULONG)idx); + } + } + } + +cleanup_token: + if (slotInitDone[slotIdx]) { + wp11_Token_Final(&slot->token); + ret = wp11_Token_Init(&slot->token, emptyLabel); + if (ret != 0 && rv == CKR_OK) + rv = wp11_ErrorToCkr(ret); + } + +cleanup_slot: + return rv; +} +#else +WP11_API CK_RV wolfPKCS11_TokenRepair(CK_SLOT_ID slotId, CK_FLAGS flags) +{ + (void)slotId; + (void)flags; + return CKR_FUNCTION_NOT_SUPPORTED; +} +#endif + /** * Open a new session on the token in the slot. * Notification callback and application data ignored. @@ -6296,10 +7598,12 @@ int WP11_Slot_UserLogin(WP11_Slot* slot, char* pin, int pinLen) if (ret == 0) { ret = WP11_Slot_CheckUserPin(slot, pin, pinLen); + WOLFPKCS11_MSG("User PIN check result: %d", ret); #ifndef WOLFPKCS11_NO_STORE if (ret == 0) { ret = HashPIN(pin, pinLen, token->seed, sizeof(token->seed), token->key, sizeof(token->key), slot); + WOLFPKCS11_MSG("User PIN hash result: %d", ret); } #endif WP11_Lock_LockRW(&slot->lock); @@ -6326,7 +7630,19 @@ int WP11_Slot_UserLogin(WP11_Slot* slot, char* pin, int pinLen) #ifndef WOLFPKCS11_NO_STORE object = token->object; while (ret == 0 && object != NULL) { + WOLFPKCS11_MSG("Decoding handle=%lu class=%lu type=%lu keyLen=%d flags=0x%lx", + (unsigned long)object->handle, + (unsigned long)object->objClass, + (unsigned long)object->type, + object->keyDataLen, + (unsigned long)object->opFlag); ret = wp11_Object_Decode(object); + if (ret != 0) { + WOLFPKCS11_MSG("Decode failed handle=%lu class=%lu type=%lu ret=%d", + (unsigned long)object->handle, + (unsigned long)object->objClass, + (unsigned long)object->type, ret); + } object = object->next; } #endif diff --git a/src/wolfpkcs11.c b/src/wolfpkcs11.c index d0134c9..ff3edda 100644 --- a/src/wolfpkcs11.c +++ b/src/wolfpkcs11.c @@ -236,8 +236,16 @@ CK_RV C_Initialize(CK_VOID_PTR pInitArgs) #endif } - if (ret == CKR_OK) - ret = WP11_Library_Init() == 0 ? CKR_OK : CKR_FUNCTION_FAILED; + if (ret == CKR_OK) { + int initRet = WP11_Library_Init(); + + if (initRet == 0) + ret = CKR_OK; + else if (initRet == WP11_TOKEN_STORE_NEEDS_REPAIR_E) + ret = CKR_WOLFPKCS11_TOKEN_REPAIR_NEEDED; + else + ret = CKR_FUNCTION_FAILED; + } (void)pInitArgs; diff --git a/tests/include.am b/tests/include.am index ad3ad4b..647bdcc 100644 --- a/tests/include.am +++ b/tests/include.am @@ -36,6 +36,11 @@ noinst_PROGRAMS += tests/object_id_uniqueness_test tests_object_id_uniqueness_test_SOURCES = tests/object_id_uniqueness_test.c tests_object_id_uniqueness_test_LDADD = +check_PROGRAMS += tests/tpm_object_upgrade_test +noinst_PROGRAMS += tests/tpm_object_upgrade_test +tests_tpm_object_upgrade_test_SOURCES = tests/tpm_object_upgrade_test.c +tests_tpm_object_upgrade_test_LDADD = + if BUILD_STATIC tests_pkcs11test_LDADD += src/libwolfpkcs11.la tests_pkcs11mtt_LDADD += src/libwolfpkcs11.la @@ -44,6 +49,7 @@ tests_token_path_test_LDADD += src/libwolfpkcs11.la tests_rsa_session_persistence_test_LDADD += src/libwolfpkcs11.la tests_debug_test_LDADD += src/libwolfpkcs11.la tests_object_id_uniqueness_test_LDADD += src/libwolfpkcs11.la +tests_tpm_object_upgrade_test_LDADD += src/libwolfpkcs11.la else tests_object_id_uniqueness_test_LDADD += src/libwolfpkcs11.la endif diff --git a/tests/tpm_object_upgrade_test.c b/tests/tpm_object_upgrade_test.c new file mode 100644 index 0000000..875b77b --- /dev/null +++ b/tests/tpm_object_upgrade_test.c @@ -0,0 +1,901 @@ +/* tpm_object_upgrade_test.c + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfPKCS11. + * + * wolfPKCS11 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPKCS11 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef _GNU_SOURCE + #define _GNU_SOURCE +#endif + +#ifdef HAVE_CONFIG_H + #include +#endif + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include + +#ifndef WOLFPKCS11_USER_SETTINGS + #include +#endif +#include + +#include +#ifndef PATH_MAX + #define PATH_MAX 4096 +#endif + +#ifndef HAVE_PKCS11_STATIC +#include +#include +#endif + +#include +#include +#include +#include + +#if !defined(WOLFPKCS11_NO_STORE) && !defined(NO_RSA) + +#include "testdata.h" + +#define MAX_TRACKED_OBJECTS 10 +#define DEFAULT_METADATA_FILE "tpm-upgrade-metadata.txt" + +/* Helper macros mirroring other TPM regression tests */ +#define CHECK_CKR(rv, msg) \ + do { \ + if ((rv) != CKR_OK) { \ + fprintf(stderr, "%s:%d: %s failed with 0x%lx\n", \ + __FILE__, __LINE__, (msg), (unsigned long)(rv)); \ + } \ + } \ + while (0) + +#define CHECK_COND(cond, msg) \ + do { \ + if (!(cond)) { \ + fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, (msg)); \ + return -1; \ + } \ + } \ + while (0) + +typedef struct tpm_upgrade_options { + int prepare; + int verify; + const char* module_path; + const char* metadata_path; + int verbose; +} tpm_upgrade_options; + +typedef struct tpm_upgrade_counts { + int key_count; + int cert_count; +} tpm_upgrade_counts; + +typedef CK_RV (*wolfPKCS11_TokenRepair_func)(CK_SLOT_ID, CK_FLAGS); +typedef void (*wolfPKCS11_Debugging_On_func)(void); +typedef void (*wolfPKCS11_Debugging_Off_func)(void); + +#ifndef HAVE_PKCS11_STATIC +static void* dlib; +#endif +static CK_FUNCTION_LIST* func_list; +static wolfPKCS11_TokenRepair_func token_repair = NULL; +static wolfPKCS11_Debugging_On_func debug_on = NULL; +static wolfPKCS11_Debugging_Off_func debug_off = NULL; +static CK_SLOT_ID slot_id = 0; +static byte so_pin[] = "password123456"; +static byte user_pin[] = "wolfpkcs11-test"; +static const CK_ULONG so_pin_len = (CK_ULONG)(sizeof(so_pin) - 1); +static const CK_ULONG user_pin_len = (CK_ULONG)(sizeof(user_pin) - 1); +static const CK_UTF8CHAR token_label[] = "wolfPKCS11 TPM upgrade"; + +static CK_OBJECT_CLASS priv_key_class = CKO_PRIVATE_KEY; +static CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; +static CK_CERTIFICATE_TYPE x509_cert_type = CKC_X_509; +static CK_KEY_TYPE rsa_key_type = CKK_RSA; +static CK_BBOOL ck_true = CK_TRUE; + +static int verbose_log = 0; +static int debug_enabled = 0; +static char loaded_module_path[PATH_MAX]; +static const char* loaded_module_path_ptr = NULL; + +/* Simple X.509 certificate blob reused for all certificate objects. */ +static const unsigned char rsa_cert_der[] = { + 0x30, 0x82, 0x01, 0x0A, 0x30, 0x81, 0xB7, 0xA0, 0x03, 0x02, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, + 0x04, 0x03, 0x02, 0x30, 0x12, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0C, 0x07, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x41, 0x30, + 0x1E, 0x17, 0x0D, 0x32, 0x33, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5A, 0x17, 0x0D, 0x32, 0x34, 0x30, 0x31, 0x30, 0x31, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x30, 0x15, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x0A, 0x54, 0x65, 0x73, 0x74, + 0x20, 0x43, 0x65, 0x72, 0x74, 0x20, 0x31, 0x30, 0x59, 0x30, 0x13, 0x06, + 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, + 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x01, 0x02, + 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, + 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, + 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, + 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, + 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, + 0x3F, 0x40, 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, + 0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x20, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x02, 0x21, 0x00, 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, + 0x3D, 0x3E, 0x3F +}; + +static void enable_debug_logging(void) +{ + if (debug_on != NULL && !debug_enabled) { + debug_on(); + debug_enabled = 1; + } +} + +static void usage(const char* prog) +{ + fprintf(stderr, + "Usage: %s (--prepare|--verify) [--module ] " + "[--metadata-file ] [--verbose]\n", prog); +} + +static int parse_args(int argc, char** argv, tpm_upgrade_options* opts) +{ + int i; + + XMEMSET(opts, 0, sizeof(*opts)); + opts->metadata_path = DEFAULT_METADATA_FILE; + + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "--prepare") == 0) { + opts->prepare = 1; + } + else if (strcmp(argv[i], "--verify") == 0) { + opts->verify = 1; + } + else if (strcmp(argv[i], "--module") == 0) { + if ((i + 1) >= argc) { + usage(argv[0]); + return -1; + } + opts->module_path = argv[++i]; + } + else if (strcmp(argv[i], "--metadata-file") == 0) { + if ((i + 1) >= argc) { + usage(argv[0]); + return -1; + } + opts->metadata_path = argv[++i]; + } + else if (strcmp(argv[i], "--verbose") == 0) { + opts->verbose = 1; + } + else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + usage(argv[0]); + return -1; + } + else { + fprintf(stderr, "Unknown option: %s\n", argv[i]); + usage(argv[0]); + return -1; + } + } + + if (opts->prepare == opts->verify) { + fprintf(stderr, "Exactly one of --prepare or --verify must be set\n"); + usage(argv[0]); + return -1; + } + + if (opts->module_path == NULL) { + opts->module_path = WOLFPKCS11_DLL_FILENAME; + } + + verbose_log = opts->verbose; + + return 0; +} + +#define verbose_printf(...) \ + do { \ + if (verbose_log) \ + printf(__VA_ARGS__); \ + } while (0) + +static void* pkcs11_open_module_handle(const char* module_path) +{ + void* handle = NULL; + +#if defined(LM_ID_NEWLM) + /* Prefer a fresh namespace so we never reuse a previously-loaded copy. */ + dlerror(); + handle = dlmopen(LM_ID_NEWLM, module_path, RTLD_NOW | RTLD_LOCAL); + if (handle == NULL) { + const char* err = dlerror(); + if (verbose_log && err != NULL) { + fprintf(stderr, + "dlmopen failed for %s: %s (falling back to dlopen)\n", + module_path, err); + } + } +#endif + + if (handle == NULL) { + dlerror(); + handle = dlopen(module_path, RTLD_NOW | RTLD_LOCAL); + } + + return handle; +} + +static void log_module_version(const char* module_path) +{ + CK_INFO info; + CK_RV rv; + const char* path = module_path != NULL ? module_path : "(unknown)"; + + if (func_list == NULL || func_list->C_GetInfo == NULL) + return; + + XMEMSET(&info, 0, sizeof(info)); + rv = func_list->C_GetInfo(&info); + if (rv == CKR_OK) { + printf("Loaded %s (libraryVersion %u.%u)\n", path, + (unsigned int)info.libraryVersion.major, + (unsigned int)info.libraryVersion.minor); + } + else { + fprintf(stderr, "C_GetInfo failed for %s: 0x%lx\n", path, + (unsigned long)rv); + } +} + +static void remember_module_path(const char* module_path) +{ + if (module_path != NULL) { + size_t len = strlen(module_path); + if (len >= sizeof(loaded_module_path)) + len = sizeof(loaded_module_path) - 1; + memcpy(loaded_module_path, module_path, len); + loaded_module_path[len] = '\0'; + loaded_module_path_ptr = loaded_module_path; + } + else { + loaded_module_path_ptr = NULL; + } +} + +static CK_RV pkcs11_load_module(const char* module_path) +{ + CK_RV ret = CKR_OK; +#ifndef HAVE_PKCS11_STATIC + CK_C_GetFunctionList func = NULL; + const char* resolved_path = module_path; + char resolved_path_buf[PATH_MAX]; + resolved_path_buf[0] = '\0'; + + dlib = pkcs11_open_module_handle(module_path); + if (dlib == NULL) { + glob_t matches; + char pattern[PATH_MAX]; + + if (strlen(module_path) < sizeof(pattern) - 2) { + snprintf(pattern, sizeof(pattern), "%s*", module_path); + if (glob(pattern, 0, NULL, &matches) == 0) { + size_t i; + for (i = 0; i < matches.gl_pathc; i++) { + const char* candidate = matches.gl_pathv[i]; + if (strcmp(candidate, module_path) == 0) + continue; + dlib = pkcs11_open_module_handle(candidate); + if (dlib != NULL) { + snprintf(resolved_path_buf, sizeof(resolved_path_buf), + "%s", candidate); + resolved_path = resolved_path_buf; + break; + } + } + } + globfree(&matches); + } + } + + if (dlib == NULL) { + fprintf(stderr, "dlopen failed for %s: %s\n", + module_path, dlerror()); + return CKR_GENERAL_ERROR; + } + + func = (CK_C_GetFunctionList)dlsym(dlib, "C_GetFunctionList"); + if (func == NULL) { + fprintf(stderr, "dlsym(C_GetFunctionList) failed\n"); + dlclose(dlib); + dlib = NULL; + return CKR_GENERAL_ERROR; + } + + ret = func(&func_list); + if (ret == CKR_OK && func_list != NULL) { + Dl_info info; + if (dladdr((void*)func, &info) != 0 && info.dli_fname != NULL) { + remember_module_path(info.dli_fname); + } + else { + if (resolved_path == module_path && module_path != NULL && + resolved_path_buf[0] == '\0') { + snprintf(resolved_path_buf, sizeof(resolved_path_buf), "%s", + module_path); + resolved_path = resolved_path_buf; + } + remember_module_path(resolved_path); + } + token_repair = (wolfPKCS11_TokenRepair_func)dlsym(dlib, + "wolfPKCS11_TokenRepair"); + debug_on = (wolfPKCS11_Debugging_On_func)dlsym(dlib, + "wolfPKCS11_Debugging_On"); + debug_off = (wolfPKCS11_Debugging_Off_func)dlsym(dlib, + "wolfPKCS11_Debugging_Off"); + } +#else + (void)module_path; + ret = C_GetFunctionList(&func_list); + if (ret == CKR_OK && func_list != NULL) { + remember_module_path("libwolfpkcs11 (static link)"); + token_repair = wolfPKCS11_TokenRepair; + #ifdef DEBUG_WOLFPKCS11 + debug_on = wolfPKCS11_Debugging_On; + debug_off = wolfPKCS11_Debugging_Off; + #else + debug_on = NULL; + debug_off = NULL; + #endif + } +#endif + CHECK_CKR(ret, "C_GetFunctionList"); + return ret; +} + +static void pkcs11_unload_module(int finalize) +{ + if (finalize && func_list != NULL) { + func_list->C_Finalize(NULL); + } + if (debug_off != NULL && debug_enabled) + debug_off(); + debug_enabled = 0; +#ifndef HAVE_PKCS11_STATIC + if (dlib != NULL) { + dlclose(dlib); + dlib = NULL; + } +#endif + func_list = NULL; + token_repair = NULL; + debug_on = NULL; + debug_off = NULL; +} + +static CK_RV pkcs11_initialize(void) +{ + CK_C_INITIALIZE_ARGS args; + CK_RV ret; + CK_SLOT_ID slots[8]; + CK_ULONG slot_count = sizeof(slots) / sizeof(slots[0]); + + XMEMSET(&args, 0, sizeof(args)); + args.flags = CKF_OS_LOCKING_OK; + + ret = func_list->C_Initialize(&args); + if (ret == CKR_OK) + enable_debug_logging(); + if (ret == CKR_WOLFPKCS11_TOKEN_REPAIR_NEEDED) { + verbose_printf("C_Initialize reported CKR_WOLFPKCS11_TOKEN_REPAIR_NEEDED\n"); + if (token_repair == NULL) { + fprintf(stderr, "wolfPKCS11_TokenRepair not available in module\n"); + return ret; + } + enable_debug_logging(); + ret = token_repair(1, 0); + CHECK_CKR(ret, "wolfPKCS11_TokenRepair"); + if (ret != CKR_OK) + return ret; + ret = func_list->C_Initialize(&args); + if (ret == CKR_OK) + enable_debug_logging(); + } + CHECK_CKR(ret, "C_Initialize"); + + if (ret == CKR_OK) + log_module_version(loaded_module_path_ptr); + + if (ret != CKR_OK) + return ret; + + ret = func_list->C_GetSlotList(CK_TRUE, slots, &slot_count); + CHECK_CKR(ret, "C_GetSlotList"); + if (ret != CKR_OK) + return ret; + + if (slot_count == 0) { + fprintf(stderr, "No slots available\n"); + return CKR_GENERAL_ERROR; + } + + slot_id = slots[0]; + verbose_printf("Using slot %lu\n", (unsigned long)slot_id); + + return CKR_OK; +} + +static CK_RV init_token_if_needed(void) +{ + CK_RV ret; + unsigned char label[32]; + + XMEMSET(label, ' ', sizeof(label)); + XMEMCPY(label, token_label, + XSTRLEN((const char*)token_label) < (int)sizeof(label) ? + XSTRLEN((const char*)token_label) : (int)sizeof(label)); + + ret = func_list->C_InitToken(slot_id, so_pin, so_pin_len, label); + if (ret == CKR_PIN_INCORRECT || ret == CKR_SESSION_EXISTS) { + /* Already initialised, treat as success */ + verbose_printf("Token already initialised (0x%lx)\n", (unsigned long)ret); + ret = CKR_OK; + } + CHECK_CKR(ret, "C_InitToken"); + return ret; +} + +static CK_RV set_user_pin(void) +{ + CK_SESSION_HANDLE session; + CK_RV ret; + + ret = func_list->C_OpenSession(slot_id, + CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL, &session); + CHECK_CKR(ret, "C_OpenSession"); + + if (ret != CKR_OK) + return ret; + + ret = func_list->C_Login(session, CKU_SO, so_pin, so_pin_len); + if (ret == CKR_OK) { + ret = func_list->C_InitPIN(session, user_pin, user_pin_len); + CHECK_CKR(ret, "C_InitPIN"); + func_list->C_Logout(session); + } + else if (ret == CKR_USER_ALREADY_LOGGED_IN) { + verbose_printf("SO already logged in\n"); + ret = CKR_OK; + } + else if (ret == CKR_PIN_INCORRECT) { + verbose_printf("SO PIN already set\n"); + ret = CKR_OK; + } + else { + CHECK_CKR(ret, "SO login during InitPIN"); + } + + func_list->C_CloseSession(session); + return ret; +} + +static CK_RV open_user_session(CK_SESSION_HANDLE* session, int rw) +{ + CK_RV ret; + CK_FLAGS flags = CKF_SERIAL_SESSION; + + if (rw) + flags |= CKF_RW_SESSION; + + ret = func_list->C_OpenSession(slot_id, flags, NULL, NULL, session); + CHECK_CKR(ret, "C_OpenSession"); + if (ret != CKR_OK) + return ret; + + if (token_repair == NULL) + verbose_printf("Token repair callback not available\n"); + + ret = func_list->C_Login(*session, CKU_USER, user_pin, user_pin_len); + if (ret == CKR_USER_ALREADY_LOGGED_IN) { + verbose_printf("User already logged in\n"); + ret = CKR_OK; + } + else if ((ret == CKR_PIN_INCORRECT || + ret == CKR_USER_PIN_NOT_INITIALIZED) && + token_repair != NULL) { + verbose_printf("User login failed, attempting token repair...\n"); + CHECK_CKR(token_repair(slot_id, 0), "wolfPKCS11_TokenRepair"); + ret = func_list->C_Login(*session, CKU_USER, user_pin, user_pin_len); + CHECK_CKR(ret, "C_Login"); + } + else { + CHECK_CKR(ret, "C_Login"); + } + + if (ret != CKR_OK) { + func_list->C_CloseSession(*session); + *session = CK_INVALID_HANDLE; + } + + return ret; +} + +static void close_user_session(CK_SESSION_HANDLE session) +{ + if (session != CK_INVALID_HANDLE) { + func_list->C_Logout(session); + func_list->C_CloseSession(session); + } +} + +static CK_RV create_rsa_private_key(CK_SESSION_HANDLE session, int index) +{ + CK_RV ret; + (void)index; /* Unique attributes not supported on legacy versions */ + CK_OBJECT_HANDLE handle = CK_INVALID_HANDLE; + CK_ATTRIBUTE template[] = { + { CKA_CLASS, &priv_key_class, sizeof(priv_key_class) }, + { CKA_KEY_TYPE, &rsa_key_type, sizeof(rsa_key_type) }, + { CKA_TOKEN, &ck_true, sizeof(ck_true) }, + { CKA_PRIVATE, &ck_true, sizeof(ck_true) }, + { CKA_DECRYPT, &ck_true, sizeof(ck_true) }, + { CKA_SIGN, &ck_true, sizeof(ck_true) }, + { CKA_MODULUS, rsa_2048_modulus, sizeof(rsa_2048_modulus) }, + { CKA_PRIVATE_EXPONENT, rsa_2048_priv_exp, sizeof(rsa_2048_priv_exp) }, + { CKA_PRIME_1, rsa_2048_p, sizeof(rsa_2048_p) }, + { CKA_PRIME_2, rsa_2048_q, sizeof(rsa_2048_q) }, + { CKA_EXPONENT_1, rsa_2048_dP, sizeof(rsa_2048_dP) }, + { CKA_EXPONENT_2, rsa_2048_dQ, sizeof(rsa_2048_dQ) }, + { CKA_COEFFICIENT, rsa_2048_u, sizeof(rsa_2048_u) }, + { CKA_PUBLIC_EXPONENT, rsa_2048_pub_exp, sizeof(rsa_2048_pub_exp) } + }; + + ret = func_list->C_CreateObject(session, template, + (CK_ULONG)(sizeof(template)/sizeof(CK_ATTRIBUTE)), &handle); + + if (ret == CKR_DEVICE_MEMORY || ret == CKR_HOST_MEMORY || + ret == CKR_FUNCTION_FAILED) { + verbose_printf("TPM memory exhausted while creating key %d\n", index); + } + else if (ret != CKR_OK) { + CHECK_CKR(ret, "Create RSA private key"); + } + return ret; +} + +static CK_RV create_certificate(CK_SESSION_HANDLE session, int index) +{ + CK_RV ret; + CK_OBJECT_HANDLE handle = CK_INVALID_HANDLE; + CK_ATTRIBUTE template[] = { + { CKA_CLASS, &cert_class, sizeof(cert_class) }, + { CKA_CERTIFICATE_TYPE, &x509_cert_type, sizeof(x509_cert_type) }, + { CKA_TOKEN, &ck_true, sizeof(ck_true) }, + { CKA_VALUE, (void*)rsa_cert_der, sizeof(rsa_cert_der) } + }; + + ret = func_list->C_CreateObject(session, template, + (CK_ULONG)(sizeof(template)/sizeof(CK_ATTRIBUTE)), &handle); + if (ret == CKR_DEVICE_MEMORY || ret == CKR_HOST_MEMORY || + ret == CKR_FUNCTION_FAILED) { + verbose_printf("TPM memory exhausted while creating cert %d\n", index); + } + else if (ret != CKR_OK) { + CHECK_CKR(ret, "Create certificate"); + } + return ret; +} + +static int write_metadata(const char* path, const tpm_upgrade_counts* counts) +{ + FILE* file; + + file = fopen(path, "w"); + if (file == NULL) { + fprintf(stderr, "Failed to open metadata file %s for writing: %s\n", + path, strerror(errno)); + return -1; + } + + if (fprintf(file, "%d %d\n", counts->key_count, counts->cert_count) < 0) { + fprintf(stderr, "Failed to write metadata\n"); + fclose(file); + return -1; + } + + fclose(file); + return 0; +} + +static int read_metadata(const char* path, tpm_upgrade_counts* counts) +{ + FILE* file; + + file = fopen(path, "r"); + if (file == NULL) { + fprintf(stderr, "Failed to open metadata file %s: %s\n", + path, strerror(errno)); + return -1; + } + + if (fscanf(file, "%d %d", &counts->key_count, + &counts->cert_count) != 2) { + fprintf(stderr, "Invalid metadata file format\n"); + fclose(file); + return -1; + } + + fclose(file); + return 0; +} + +static CK_RV prepare_objects(const tpm_upgrade_options* opts) +{ + CK_RV ret; + CK_SESSION_HANDLE session = CK_INVALID_HANDLE; + tpm_upgrade_counts counts; + int i; + + counts.key_count = 0; + counts.cert_count = 0; + + ret = init_token_if_needed(); + if (ret != CKR_OK) + return ret; + + ret = set_user_pin(); + if (ret != CKR_OK) + return ret; + + ret = open_user_session(&session, 1); + if (ret != CKR_OK) + return ret; + + for (i = 0; i < MAX_TRACKED_OBJECTS; i++) { + ret = create_rsa_private_key(session, i); + if (ret == CKR_OK) { + counts.key_count++; + } + else if (ret == CKR_DEVICE_MEMORY || ret == CKR_HOST_MEMORY || + ret == CKR_FUNCTION_FAILED) { + continue; + } + else { + close_user_session(session); + return ret; + } + + ret = create_certificate(session, i); + if (ret == CKR_OK) { + counts.cert_count++; + } + else if (ret == CKR_DEVICE_MEMORY || ret == CKR_HOST_MEMORY || + ret == CKR_FUNCTION_FAILED) { + continue; + } + else { + close_user_session(session); + return ret; + } + } + + verbose_printf("Prepared %d private keys and %d public keys\n", + counts.key_count, counts.cert_count); + + if (write_metadata(opts->metadata_path, &counts) != 0) + return CKR_GENERAL_ERROR; + + return CKR_OK; +} + +static CK_RV count_objects_by_class(CK_SESSION_HANDLE session, + CK_OBJECT_CLASS cls, CK_ULONG* total_out) +{ + CK_RV ret; + CK_OBJECT_HANDLE sample = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE handles[16]; + CK_ULONG total = 0; + CK_ATTRIBUTE template[] = { + { CKA_CLASS, (void*)&cls, sizeof(cls) }, + { CKA_TOKEN, (void*)&ck_true, sizeof(ck_true) } + }; + + ret = func_list->C_FindObjectsInit(session, template, + (CK_ULONG)(sizeof(template)/sizeof(CK_ATTRIBUTE))); + CHECK_CKR(ret, "C_FindObjectsInit"); + if (ret != CKR_OK) + return ret; + + for (;;) { + CK_ULONG count = 0; + + ret = func_list->C_FindObjects(session, handles, + (CK_ULONG)(sizeof(handles)/sizeof(handles[0])), &count); + CHECK_CKR(ret, "C_FindObjects"); + if (ret != CKR_OK) + break; + + if (count > 0 && sample == CK_INVALID_HANDLE) + sample = handles[0]; + + total += count; + + if (count == 0) + break; + } + + { + CK_RV final_ret = func_list->C_FindObjectsFinal(session); + CHECK_CKR(final_ret, "C_FindObjectsFinal"); + if (ret == CKR_OK && final_ret != CKR_OK) + ret = final_ret; + } + + if (ret == CKR_OK) { + *total_out = total; + + if (sample != CK_INVALID_HANDLE) { + CK_OBJECT_CLASS class_value; + CK_ATTRIBUTE attr = { + CKA_CLASS, (void*)&class_value, sizeof(class_value) + }; + + ret = func_list->C_GetAttributeValue(session, sample, &attr, 1); + CHECK_CKR(ret, "C_GetAttributeValue"); + } + } + + return ret; +} + +static CK_RV verify_objects(const tpm_upgrade_options* opts) +{ + CK_RV ret; + CK_SESSION_HANDLE session = CK_INVALID_HANDLE; + tpm_upgrade_counts counts; + CK_ULONG key_total = 0; + CK_ULONG cert_total = 0; + + if (read_metadata(opts->metadata_path, &counts) != 0) + return CKR_GENERAL_ERROR; + + ret = open_user_session(&session, 0); + if (ret != CKR_OK) + return ret; + + ret = count_objects_by_class(session, CKO_PRIVATE_KEY, &key_total); + if (ret == CKR_OK && key_total != (CK_ULONG)counts.key_count) { + fprintf(stderr, "Expected %d private keys, found %lu\n", + counts.key_count, (unsigned long)key_total); + ret = CKR_GENERAL_ERROR; + } + + if (ret == CKR_OK) { + ret = count_objects_by_class(session, cert_class, &cert_total); + if (ret == CKR_OK && cert_total != (CK_ULONG)counts.cert_count) { + fprintf(stderr, "Expected %d public keys, found %lu\n", + counts.cert_count, (unsigned long)cert_total); + ret = CKR_GENERAL_ERROR; + } + } + + close_user_session(session); + + return ret; +} + +static const char* resolve_metadata_path(const tpm_upgrade_options* opts, + char* buffer, size_t buffer_len) +{ + const char* token_path = getenv("WOLFPKCS11_TOKEN_PATH"); + + if (opts->metadata_path != NULL && + opts->metadata_path[0] == '/') { + return opts->metadata_path; + } + + if (token_path != NULL && buffer != NULL) { + size_t len = strlen(token_path); + int need_sep = (len > 0 && token_path[len - 1] != '/') ? 1 : 0; + + if (snprintf(buffer, buffer_len, "%s%s%s", + token_path, need_sep ? "/" : "", + opts->metadata_path) >= (int)buffer_len) { + fprintf(stderr, "Metadata path buffer too small\n"); + return NULL; + } + return buffer; + } + + return opts->metadata_path; +} + +int main(int argc, char** argv) +{ + tpm_upgrade_options opts; + CK_RV ret; + int exit_code = EXIT_FAILURE; + char metadata_path[512]; + const char* resolved_metadata; + + if (argc == 1) { + fprintf(stderr, + "tpm_object_upgrade_test: requires --prepare or --verify (skipping)\n"); + return 77; + } + + if (parse_args(argc, argv, &opts) != 0) + return EXIT_FAILURE; + + resolved_metadata = resolve_metadata_path(&opts, metadata_path, + sizeof(metadata_path)); + if (resolved_metadata == NULL) + return EXIT_FAILURE; + opts.metadata_path = resolved_metadata; + + if (pkcs11_load_module(opts.module_path) != CKR_OK) + return EXIT_FAILURE; + + ret = pkcs11_initialize(); + if (ret != CKR_OK) { + pkcs11_unload_module(0); + return EXIT_FAILURE; + } + + if (opts.prepare) { + ret = prepare_objects(&opts); + } + else { + ret = verify_objects(&opts); + } + + if (ret == CKR_OK) { + exit_code = EXIT_SUCCESS; + } + else { + fprintf(stderr, "TPM object upgrade test failed with 0x%lx\n", + (unsigned long)ret); + } + + pkcs11_unload_module(opts.prepare ? 0 : 1); + + return exit_code; +} + +#else + +int main(void) +{ + fprintf(stderr, "TPM object upgrade test requires RSA and storage support\n"); + return 77; /* skipped */ +} + +#endif /* !WOLFPKCS11_NO_STORE && !NO_RSA */ +#include diff --git a/wolfpkcs11/internal.h b/wolfpkcs11/internal.h index ebc224e..624529b 100644 --- a/wolfpkcs11/internal.h +++ b/wolfpkcs11/internal.h @@ -145,6 +145,8 @@ C_EXTRA_FLAGS="-DWOLFSSL_PUBLIC_MP -DWC_RSA_DIRECT" #endif #endif +#define WP11_TOKEN_STORE_NEEDS_REPAIR_E (-3200) + /* Session was opened read-only or read/write. */ #define WP11_TOKEN_STATE_UNKNOWN 0 #define WP11_TOKEN_STATE_INITIALIZED 1 diff --git a/wolfpkcs11/pkcs11.h b/wolfpkcs11/pkcs11.h index 575bb96..7bed41a 100644 --- a/wolfpkcs11/pkcs11.h +++ b/wolfpkcs11/pkcs11.h @@ -1175,6 +1175,10 @@ struct CK_FUNCTION_LIST { }; +#define CKR_WOLFPKCS11_TOKEN_REPAIR_NEEDED (CKR_VENDOR_DEFINED + 0x100) + +WP11_API CK_RV wolfPKCS11_TokenRepair(CK_SLOT_ID slotID, CK_FLAGS flags); + /* Debug control functions */ #ifdef DEBUG_WOLFPKCS11 WP11_API void wolfPKCS11_Debugging_On(void);