mirror of https://github.com/wolfSSL/wolfTPM.git
9332 lines
341 KiB
C
9332 lines
341 KiB
C
/* unit_tests.c
|
|
*
|
|
* Copyright (C) 2006-2026 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfTPM.
|
|
*
|
|
* wolfTPM 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.
|
|
*
|
|
* wolfTPM 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
|
|
*/
|
|
|
|
/* wolfTPM 2.0 unit tests */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <wolftpm/tpm2.h>
|
|
#include <wolftpm/tpm2_wrap.h>
|
|
#include <wolftpm/tpm2_param_enc.h>
|
|
#include <wolftpm/tpm2_asn.h>
|
|
#include <wolftpm/tpm2_swtpm.h>
|
|
#include <wolftpm/tpm2_tis.h>
|
|
#include <wolftpm/tpm2_spdm.h>
|
|
#ifdef WOLFTPM_MLDSA_SIGN
|
|
#include <wolfssl/wolfcrypt/wc_mldsa.h>
|
|
#endif
|
|
|
|
#include <hal/tpm_io.h>
|
|
#include <examples/tpm_test.h>
|
|
#include <examples/tpm_test_keys.h>
|
|
#include <examples/wrap/wrap_test.h>
|
|
#include <examples/firmware/st33_blob0.h>
|
|
|
|
#include <stdio.h>
|
|
#if defined(__linux__) || defined(__APPLE__) || defined(__unix__)
|
|
#include <fcntl.h>
|
|
#endif
|
|
#ifdef WOLFTPM_SPDM
|
|
#include <stdlib.h>
|
|
#endif
|
|
|
|
/* Test Fail Helpers */
|
|
#ifndef NO_ABORT
|
|
#ifndef XABORT
|
|
#include <stdlib.h>
|
|
#define XABORT() abort()
|
|
#endif
|
|
#else
|
|
#undef XABORT
|
|
#define XABORT()
|
|
#endif
|
|
|
|
#define Fail(description, result) do { \
|
|
printf("\nERROR - %s line %d failed with:", __FILE__, __LINE__); \
|
|
printf("\n expected: "); printf description; \
|
|
printf("\n result: "); printf result; printf("\n\n"); \
|
|
fflush(stdout); \
|
|
XABORT(); \
|
|
} while(0)
|
|
#define Assert(test, description, result) if (!(test)) Fail(description, result)
|
|
#define AssertTrue(x) Assert( (x), ("%s is true", #x), (#x " => FALSE"))
|
|
#define AssertFalse(x) Assert(!(x), ("%s is false", #x), (#x " => TRUE"))
|
|
#define AssertNotNull(x) Assert( (x), ("%s is not null", #x), (#x " => NULL"))
|
|
#define AssertNull(x) do { \
|
|
void* _x = (void *) (x); \
|
|
Assert(!_x, ("%s is null", #x), (#x " => %p", _x)); \
|
|
} while(0)
|
|
#define AssertInt(x, y, op, er) do { \
|
|
int _x = (int)x; \
|
|
int _y = (int)y; \
|
|
Assert(_x op _y, ("%s " #op " %s", #x, #y), ("%d(0x%x) " #er " %d(0x%x)", \
|
|
_x, _x, _y, _y)); \
|
|
} while(0)
|
|
#define AssertIntEQ(x, y) AssertInt(x, y, ==, !=)
|
|
#define AssertIntNE(x, y) AssertInt(x, y, !=, ==)
|
|
#define AssertIntGT(x, y) AssertInt(x, y, >, <=)
|
|
#define AssertIntLT(x, y) AssertInt(x, y, <, >=)
|
|
#define AssertIntGE(x, y) AssertInt(x, y, >=, <)
|
|
#define AssertIntLE(x, y) AssertInt(x, y, <=, >)
|
|
#define AssertStr(x, y, op, er) do { \
|
|
const char* _x = x; \
|
|
const char* _y = y; \
|
|
int _z = (_x && _y) ? strcmp(_x, _y) : -1; \
|
|
Assert(_z op 0, ("%s " #op " %s", #x, #y), \
|
|
("\"%s\" " #er " \"%s\"", _x, _y));\
|
|
} while(0)
|
|
#define AssertStrEQ(x, y) AssertStr(x, y, ==, !=)
|
|
#define AssertStrNE(x, y) AssertStr(x, y, !=, ==)
|
|
#define AssertStrGT(x, y) AssertStr(x, y, >, <=)
|
|
#define AssertStrLT(x, y) AssertStr(x, y, <, >=)
|
|
#define AssertStrGE(x, y) AssertStr(x, y, >=, <)
|
|
#define AssertStrLE(x, y) AssertStr(x, y, <=, >)
|
|
|
|
#ifndef WOLFTPM2_NO_WRAPPER
|
|
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) && \
|
|
!defined(WOLFTPM2_NO_ASN)
|
|
/* Query TPM_CAP_ALGS to see if a given algorithm is supported.
|
|
* Returns 1 if supported, 0 otherwise. Used to skip test iterations on TPMs
|
|
* that don't implement a given hash (e.g. Nuvoton NPCT75x lacks SHA512).
|
|
* Guarded by the same ifdef as its only caller (test_wolfTPM2_EccSignVerifyDig)
|
|
* so non-ECC builds don't trip -Werror=unused-function. */
|
|
static int test_tpm_alg_supported(TPM_ALG_ID alg)
|
|
{
|
|
GetCapability_In in;
|
|
GetCapability_Out out;
|
|
word32 i;
|
|
|
|
XMEMSET(&in, 0, sizeof(in));
|
|
XMEMSET(&out, 0, sizeof(out));
|
|
in.capability = TPM_CAP_ALGS;
|
|
in.property = alg;
|
|
in.propertyCount = 1;
|
|
if (TPM2_GetCapability(&in, &out) != TPM_RC_SUCCESS) {
|
|
return 1; /* On error, assume supported and let the real call fail */
|
|
}
|
|
for (i = 0; i < out.capabilityData.data.algorithms.count; i++) {
|
|
if (out.capabilityData.data.algorithms.algProperties[i].alg == alg) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#endif /* !WOLFTPM2_NO_WOLFCRYPT && HAVE_ECC && !WOLFTPM2_NO_ASN */
|
|
|
|
#if defined(WOLFTPM_SPDM) && defined(WOLFTPM_SPDM_TCG) && \
|
|
!defined(NO_GETENV)
|
|
static int TestWolfTPM2_HasResponderPin(void)
|
|
{
|
|
const char* keyHex = getenv("SPDM_RESPONDER_PUBKEY");
|
|
|
|
return keyHex != NULL && keyHex[0] != '\0';
|
|
}
|
|
|
|
static int TestWolfTPM2_InitConfigured(WOLFTPM2_DEV* dev,
|
|
TPM2HalIoCb ioCb, void* userCtx)
|
|
{
|
|
const char* keyHex = getenv("SPDM_RESPONDER_PUBKEY");
|
|
const char* vendor = getenv("SPDM_IDENTITY_VENDOR");
|
|
byte key[WOLFSPDM_ECC_POINT_SIZE];
|
|
WOLFSPDM_MODE mode = WOLFSPDM_MODE_AUTO;
|
|
int keySz;
|
|
|
|
if (keyHex == NULL || keyHex[0] == '\0') {
|
|
return wolfTPM2_Init(dev, ioCb, userCtx);
|
|
}
|
|
if (XSTRLEN(keyHex) != sizeof(key) * 2U) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
keySz = hexToByte(keyHex, key, (unsigned long)XSTRLEN(keyHex));
|
|
if (keySz != (int)sizeof(key)) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
if (vendor != NULL && XSTRCMP(vendor, "nuvoton") == 0) {
|
|
mode = WOLFSPDM_MODE_NUVOTON;
|
|
}
|
|
else if (vendor != NULL && XSTRCMP(vendor, "nations") == 0) {
|
|
mode = WOLFSPDM_MODE_NATIONS;
|
|
}
|
|
else if (vendor != NULL && vendor[0] != '\0') {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
return wolfTPM2_InitWithSpdmKey_ex(dev, ioCb, userCtx, key,
|
|
(word32)sizeof(key), mode);
|
|
}
|
|
#else
|
|
static int TestWolfTPM2_HasResponderPin(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int TestWolfTPM2_InitConfigured(WOLFTPM2_DEV* dev,
|
|
TPM2HalIoCb ioCb, void* userCtx)
|
|
{
|
|
return wolfTPM2_Init(dev, ioCb, userCtx);
|
|
}
|
|
#endif
|
|
|
|
#if defined(WOLFTPM_SPDM) && defined(WOLFTPM_SPDM_TCG) && \
|
|
defined(WOLFSPDM_NUVOTON) && defined(WOLFSPDM_NATIONS)
|
|
static void test_wolfTPM2_SpdmModeFromDidVid(void)
|
|
{
|
|
WOLFSPDM_MODE mode;
|
|
int rc;
|
|
|
|
mode = WOLFSPDM_MODE_AUTO;
|
|
rc = wolfTPM2_SpdmModeFromDidVid(
|
|
0x12340000U | TPM_VENDOR_NUVOTON, &mode);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(mode, WOLFSPDM_MODE_NUVOTON);
|
|
|
|
mode = WOLFSPDM_MODE_AUTO;
|
|
rc = wolfTPM2_SpdmModeFromDidVid(
|
|
0x56780000U | TPM_VENDOR_NATIONTECH, &mode);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(mode, WOLFSPDM_MODE_NATIONS);
|
|
|
|
mode = WOLFSPDM_MODE_NUVOTON;
|
|
rc = wolfTPM2_SpdmModeFromDidVid(0x12345678U, &mode);
|
|
AssertIntEQ(rc, WOLFSPDM_E_BAD_STATE);
|
|
AssertIntEQ(mode, WOLFSPDM_MODE_AUTO);
|
|
AssertIntEQ(wolfTPM2_SpdmModeFromDidVid(0, NULL), BAD_FUNC_ARG);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "SPDM DID/VID mode:");
|
|
}
|
|
#endif
|
|
|
|
#if defined(WOLFTPM_SPDM) && defined(WOLFTPM_SPDM_PSK) && \
|
|
!defined(NO_GETENV)
|
|
static void test_wolfTPM2_InitWithSpdmPsk_success(void)
|
|
{
|
|
const char* pskHex = getenv("WOLFTPM_TEST_SPDM_PSK");
|
|
byte psk[128];
|
|
size_t hexSz;
|
|
int pskSz;
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
|
|
if (pskHex == NULL || pskHex[0] == '\0') {
|
|
return;
|
|
}
|
|
|
|
hexSz = XSTRLEN(pskHex);
|
|
AssertTrue((hexSz & 1U) == 0U);
|
|
AssertTrue(hexSz <= sizeof(psk) * 2U);
|
|
if ((hexSz & 1U) != 0U || hexSz > sizeof(psk) * 2U) {
|
|
return;
|
|
}
|
|
pskSz = hexToByte(pskHex, psk, (unsigned long)hexSz);
|
|
AssertIntGT(pskSz, 0);
|
|
if (pskSz <= 0) {
|
|
wc_ForceZero(psk, sizeof(psk));
|
|
return;
|
|
}
|
|
|
|
rc = wolfTPM2_InitWithSpdmPsk(&dev, TPM2_IoCb, NULL, psk,
|
|
(word32)pskSz, NULL, 0);
|
|
wc_ForceZero(psk, sizeof(psk));
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(wolfTPM2_SpdmIsConnected(&dev), 1);
|
|
AssertIntNE(wolfTPM2_SpdmGetSessionId(&dev), 0);
|
|
AssertIntEQ(wolfTPM2_Cleanup(&dev), TPM_RC_SUCCESS);
|
|
AssertNull(dev.spdmCtx);
|
|
}
|
|
#endif
|
|
|
|
static void test_wolfTPM2_Init(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
#ifdef WOLFTPM_LINUX_DEV
|
|
TPM2_CTX initCtx;
|
|
#endif
|
|
#if defined(WOLFTPM_SPDM) && defined(WOLFTPM_SPDM_TCG)
|
|
byte rspPubKey[WOLFSPDM_ECC_POINT_SIZE];
|
|
#if !defined(WOLFSPDM_NUVOTON) && !defined(WOLFSPDM_NATIONS)
|
|
WOLFTPM2_DEV untouchedDev;
|
|
#endif
|
|
#endif
|
|
#if defined(WOLFTPM_SPDM) && defined(WOLFTPM_SPDM_PSK)
|
|
byte psk[32];
|
|
#endif
|
|
|
|
#ifdef WOLFTPM_LINUX_DEV
|
|
rc = TPM2_Init_ex(&initCtx, NULL, &initCtx, 0);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
AssertIntEQ(initCtx.fd, -1);
|
|
AssertIntEQ(TPM2_Cleanup(&initCtx), TPM_RC_SUCCESS);
|
|
#endif
|
|
#if defined(WOLFTPM_SPDM) && defined(WOLFTPM_SPDM_TCG)
|
|
XMEMSET(rspPubKey, 0xA5, sizeof(rspPubKey));
|
|
rc = wolfTPM2_InitWithSpdmKey(NULL, TPM2_IoCb, NULL,
|
|
rspPubKey, sizeof(rspPubKey));
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_InitWithSpdmKey(&dev, TPM2_IoCb, NULL, NULL, 0);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_InitWithSpdmKey(&dev, TPM2_IoCb, NULL,
|
|
rspPubKey, sizeof(rspPubKey) - 1);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_InitWithSpdmKey_ex(&dev, TPM2_IoCb, NULL,
|
|
rspPubKey, sizeof(rspPubKey), (WOLFSPDM_MODE)99);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
#if !defined(WOLFSPDM_NUVOTON) && !defined(WOLFSPDM_NATIONS)
|
|
XMEMSET(&dev, 0xA5, sizeof(dev));
|
|
XMEMCPY(&untouchedDev, &dev, sizeof(untouchedDev));
|
|
rc = wolfTPM2_InitWithSpdmKey(&dev, TPM2_IoCb, NULL,
|
|
rspPubKey, sizeof(rspPubKey));
|
|
AssertIntEQ(rc, WOLFSPDM_E_NOT_AVAILABLE);
|
|
AssertIntEQ(XMEMCMP(&dev, &untouchedDev, sizeof(dev)), 0);
|
|
#endif
|
|
#endif
|
|
#if defined(WOLFTPM_SPDM) && defined(WOLFTPM_SPDM_PSK)
|
|
XMEMSET(psk, 0x5A, sizeof(psk));
|
|
rc = wolfTPM2_InitWithSpdmPsk(NULL, TPM2_IoCb, NULL, psk,
|
|
sizeof(psk), NULL, 0);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_InitWithSpdmPsk(&dev, TPM2_IoCb, NULL, NULL, 0,
|
|
NULL, 0);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_InitWithSpdmPsk(&dev, TPM2_IoCb, NULL, psk,
|
|
sizeof(psk), NULL, 1);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
#ifndef NO_GETENV
|
|
test_wolfTPM2_InitWithSpdmPsk_success();
|
|
#endif
|
|
#endif
|
|
|
|
/* Test first argument, wolfTPM2 context */
|
|
rc = wolfTPM2_Init(NULL, TPM2_IoCb, NULL);
|
|
AssertIntNE(rc, 0);
|
|
#if defined(WOLFTPM_SPDM) && !defined(NO_GETENV)
|
|
if (getenv("WOLFTPM_TEST_SPDM_ONLY") != NULL) {
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, WOLFSPDM_E_BAD_STATE);
|
|
AssertNull(TPM2_GetActiveCtx());
|
|
AssertIntEQ(dev.ctx.locality, -1);
|
|
AssertNull(dev.spdmCtx);
|
|
}
|
|
#endif
|
|
/* Test second argument, TPM2 IO Callbacks */
|
|
rc = TestWolfTPM2_InitConfigured(&dev, NULL, NULL);
|
|
#if defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_SWTPM) || \
|
|
defined(WOLFTPM_WINAPI)
|
|
/* Custom IO Callbacks are not needed for Linux TIS driver */
|
|
AssertIntEQ(rc, 0);
|
|
wolfTPM2_Cleanup(&dev);
|
|
#else
|
|
/* IO Callbacks are required for SPIdev/I2C and must be valid */
|
|
AssertIntNE(rc, 0);
|
|
#endif
|
|
|
|
/* Test success */
|
|
rc = TestWolfTPM2_InitConfigured(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper: %-40s %s\n", "Init:",
|
|
rc == 0 ? "Passed" : "Failed");
|
|
}
|
|
|
|
#if defined(WOLFTPM_SWTPM) && !defined(NO_GETENV)
|
|
/* The test server returns TPM_RC_UPGRADE from Startup. The wrapper must keep
|
|
* the active context so the caller can issue vendor recovery commands. */
|
|
static void test_wolfTPM2_InitUpgrade(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
|
|
rc = wolfTPM2_Init(&dev, NULL, NULL);
|
|
AssertIntEQ(rc, TPM_RC_UPGRADE);
|
|
AssertTrue(TPM2_GetActiveCtx() == &dev.ctx);
|
|
AssertIntEQ(wolfTPM2_Cleanup(&dev), TPM_RC_SUCCESS);
|
|
AssertNull(TPM2_GetActiveCtx());
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "Init upgrade context:");
|
|
}
|
|
#endif
|
|
|
|
/* When the SPDM integration harness supplies a trusted pin, route the
|
|
* remaining wrapper tests through authenticated initialization. */
|
|
#define wolfTPM2_Init TestWolfTPM2_InitConfigured
|
|
|
|
|
|
/* test for WOLFTPM2_DEV restore */
|
|
static void test_wolfTPM2_OpenExisting(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_CAPS caps;
|
|
|
|
/* Init the TPM2 device */
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Test access to TPM by getting capabilities */
|
|
rc = wolfTPM2_GetCapabilities(&dev, &caps);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Perform cleanup, but don't shutdown TPM module */
|
|
rc = wolfTPM2_Cleanup_ex(&dev, 0);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* OpenExisting deliberately does not recreate transport sessions. The
|
|
* pinned SPDM integration run covers authenticated reinitialization in
|
|
* every other wrapper test. */
|
|
if (TestWolfTPM2_HasResponderPin()) {
|
|
printf("Test TPM Wrapper: %-40s Skipped (SPDM pinned mode)\n",
|
|
"Open Existing:");
|
|
return;
|
|
}
|
|
|
|
|
|
/* Restore TPM access */
|
|
rc = wolfTPM2_OpenExisting(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Test access to TPM by getting capabilities */
|
|
rc = wolfTPM2_GetCapabilities(&dev, &caps);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper: %-40s %s\n", "Open Existing:",
|
|
rc == 0 ? "Passed" : "Failed");
|
|
}
|
|
|
|
/* test for wolfTPM2_GetCapabilities */
|
|
static void test_wolfTPM2_GetCapabilities(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_CAPS caps;
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Test Arguments */
|
|
rc = wolfTPM2_GetCapabilities(NULL, &caps);
|
|
AssertIntNE(rc, 0);
|
|
rc = wolfTPM2_GetCapabilities(&dev, NULL);
|
|
AssertIntNE(rc, 0);
|
|
|
|
/* Test success */
|
|
rc = wolfTPM2_GetCapabilities(&dev, &caps);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
#ifdef DEBUG_WOLFTPM
|
|
printf("Mfg %s (%d), Vendor %s, Fw %u.%u (%u), FIPS %s, CC-EAL4 %d\n",
|
|
caps.mfgStr, caps.mfg, caps.vendorStr, caps.fwVerMajor,
|
|
caps.fwVerMinor, caps.fwVerVendor,
|
|
TPM2_GetCapsFipsStr(caps.fips140_3, caps.fips140_2),
|
|
caps.cc_eal4);
|
|
#endif
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper: %-40s %s\n", "Get Capabilities:",
|
|
rc == 0 ? "Passed" : "Failed");
|
|
}
|
|
|
|
static void test_wolfTPM2_DictionaryAttack(void)
|
|
{
|
|
/* Argument validation (non-destructive; the functional lockout/recovery
|
|
* path is exercised by examples/management/da_check and the fwTPM unit
|
|
* tests). */
|
|
AssertIntEQ(wolfTPM2_DictionaryAttackLockReset(NULL), BAD_FUNC_ARG);
|
|
AssertIntEQ(wolfTPM2_DictionaryAttackParameters(NULL, 32, 0, 0),
|
|
BAD_FUNC_ARG);
|
|
/* newMaxTries of 0 is rejected client-side */
|
|
AssertIntEQ(wolfTPM2_DictionaryAttackParameters((WOLFTPM2_DEV*)1, 0, 0, 0),
|
|
BAD_FUNC_ARG);
|
|
|
|
printf("Test TPM Wrapper: %-40s %s\n", "Dictionary Attack args:",
|
|
"Passed");
|
|
}
|
|
|
|
/* test for wolfTPM2_ReadPublicKey */
|
|
static void test_wolfTPM2_ReadPublicKey(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY storageKey;
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Test arguments */
|
|
rc = wolfTPM2_ReadPublicKey(NULL, &storageKey, TPM2_DEMO_STORAGE_KEY_HANDLE);
|
|
AssertIntNE(rc, 0);
|
|
rc = wolfTPM2_ReadPublicKey(&dev, NULL, TPM2_DEMO_STORAGE_KEY_HANDLE);
|
|
AssertIntNE(rc, 0);
|
|
|
|
/* Test success: read storage primary key */
|
|
rc = wolfTPM2_ReadPublicKey(&dev, &storageKey,
|
|
TPM2_DEMO_STORAGE_KEY_HANDLE);
|
|
if ((rc & RC_MAX_FMT1) == TPM_RC_HANDLE) {
|
|
rc = 0; /* okay if not found */
|
|
}
|
|
AssertIntEQ(rc, 0);
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper: %-40s %s\n", "Read Public Key:",
|
|
rc == 0 ? "Passed" : "Failed");
|
|
}
|
|
|
|
#ifdef WOLFTPM_FIRMWARE_UPGRADE
|
|
#if defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT)
|
|
/* Test ST33 firmware upgrade APIs (function availability and
|
|
* parameter validation). LMS vs non-LMS format is auto-detected
|
|
* from manifest size (177 bytes = non-LMS, 2697 bytes = LMS). */
|
|
static void test_wolfTPM2_ST33_FirmwareUpgrade(void)
|
|
{
|
|
int rc;
|
|
int rcEx;
|
|
int isImpl;
|
|
int fromTpm = 0;
|
|
TPM_CC ccStart, ccData, ccStartRule, ccDataRule;
|
|
TPM2_CTX* savedCtx;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_CAPS caps;
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFSSL_SHA384)
|
|
/* Invalid manifest size (not 321, 177 or 2697) for auto-detection */
|
|
uint8_t dummy_manifest[10] = {0};
|
|
#endif
|
|
|
|
/* A missing active context must fail before raw packet marshalling. */
|
|
savedCtx = TPM2_GetActiveCtx();
|
|
TPM2_SetActiveCtx(NULL);
|
|
rc = TPM2_ST33_FieldUpgradeCommand(TPM_CC_FieldUpgradeData, NULL, 0);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
TPM2_SetActiveCtx(savedCtx);
|
|
|
|
/* Initialize TPM */
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
if (rc != 0) {
|
|
printf("Test ST33 FW: %-40s Skipped (TPM not available)\n", "Init:");
|
|
return;
|
|
}
|
|
|
|
rc = wolfTPM2_GetCapabilities(&dev, &caps);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
#ifdef DEBUG_WOLFTPM
|
|
/* Display firmware version info */
|
|
if (caps.mfg == TPM_MFG_STM) {
|
|
printf("ST33 TPM - Firmware: %u.%u (0x%x), Format: %s\n",
|
|
caps.fwVerMajor, caps.fwVerMinor, caps.fwVerVendor,
|
|
(caps.fwVerMajor >= 9 && caps.fwVerMinor >= 512) ?
|
|
"LMS" : "non-LMS");
|
|
}
|
|
#endif
|
|
|
|
/* ===== Test NULL dev parameter handling ===== */
|
|
|
|
/* wolfTPM2_FirmwareUpgradeCancel - NULL dev */
|
|
rc = wolfTPM2_FirmwareUpgradeCancel(NULL);
|
|
AssertIntNE(rc, 0);
|
|
|
|
/* wolfTPM2_FirmwareUpgradeHash - NULL dev */
|
|
rc = wolfTPM2_FirmwareUpgradeHash(NULL, TPM_ALG_SHA384, NULL, 0, NULL,
|
|
0, NULL, NULL);
|
|
AssertIntNE(rc, 0);
|
|
|
|
/* wolfTPM2_FirmwareUpgradeRecover - NULL dev */
|
|
rc = wolfTPM2_FirmwareUpgradeRecover(NULL, NULL, 0, NULL, NULL);
|
|
AssertIntNE(rc, 0);
|
|
|
|
/* _ex variants with caller session - NULL dev */
|
|
rc = wolfTPM2_FirmwareUpgradeHash_ex(NULL, TPM_ALG_SHA384, NULL, 0, NULL,
|
|
0, NULL, NULL, NULL);
|
|
AssertIntNE(rc, 0);
|
|
rc = wolfTPM2_FirmwareUpgradeRecover_ex(NULL, NULL, 0, NULL, NULL, NULL);
|
|
AssertIntNE(rc, 0);
|
|
|
|
/* startSession == NULL delegates to the legacy call (same rc). Use a NULL
|
|
* dev so this never reaches the TPM (a live dev under autodetect could
|
|
* otherwise push an Infineon part into firmware-upgrade mode). */
|
|
rc = wolfTPM2_FirmwareUpgradeHash(NULL, TPM_ALG_SHA384,
|
|
NULL, 0, NULL, 0, NULL, NULL);
|
|
rcEx = wolfTPM2_FirmwareUpgradeHash_ex(NULL, TPM_ALG_SHA384,
|
|
NULL, 0, NULL, 0, NULL, NULL, NULL);
|
|
AssertIntEQ(rc, rcEx);
|
|
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFSSL_SHA384)
|
|
/* wolfTPM2_FirmwareUpgrade - NULL dev */
|
|
rc = wolfTPM2_FirmwareUpgrade(NULL, NULL, 0, NULL, NULL);
|
|
AssertIntNE(rc, 0);
|
|
|
|
/* wolfTPM2_FirmwareUpgrade_ex - NULL dev */
|
|
rc = wolfTPM2_FirmwareUpgrade_ex(NULL, NULL, 0, NULL, NULL, NULL);
|
|
AssertIntNE(rc, 0);
|
|
#endif /* !WOLFTPM2_NO_WOLFCRYPT && WOLFSSL_SHA384 */
|
|
|
|
/* ===== Test NULL/invalid parameter combinations ===== */
|
|
|
|
/* wolfTPM2_FirmwareUpgradeHash - valid dev, NULL manifest */
|
|
rc = wolfTPM2_FirmwareUpgradeHash(&dev, TPM_ALG_SHA384, NULL, 0, NULL,
|
|
0, NULL, NULL);
|
|
AssertIntNE(rc, 0);
|
|
|
|
/* wolfTPM2_FirmwareUpgradeRecover - valid dev, NULL manifest */
|
|
rc = wolfTPM2_FirmwareUpgradeRecover(&dev, NULL, 0, NULL, NULL);
|
|
AssertIntNE(rc, 0);
|
|
|
|
/* wolfTPM2_FirmwareUpgradeCancel - valid dev (may succeed or fail
|
|
* depending on TPM state) */
|
|
rc = wolfTPM2_FirmwareUpgradeCancel(&dev);
|
|
/* Note: This may return success or error depending on TPM state -
|
|
* just verify it doesn't crash */
|
|
(void)rc;
|
|
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFSSL_SHA384)
|
|
/* wolfTPM2_FirmwareUpgrade - valid dev, NULL manifest */
|
|
rc = wolfTPM2_FirmwareUpgrade(&dev, NULL, 0, NULL, NULL);
|
|
AssertIntNE(rc, 0);
|
|
|
|
/* wolfTPM2_FirmwareUpgrade - valid dev, NULL callback */
|
|
rc = wolfTPM2_FirmwareUpgrade(&dev, dummy_manifest, sizeof(dummy_manifest),
|
|
NULL, NULL);
|
|
AssertIntNE(rc, 0);
|
|
|
|
/* Test ST33-specific manifest size validation if we have an ST33 TPM.
|
|
* The manifest must be exactly 321 (generation 1, RSA signed), 177
|
|
* (generation 9 below 512, ECDSA signed) or 2697 (LMS) bytes, and must
|
|
* also match the generation the TPM is running. Any other size is
|
|
* rejected before the size is compared against the running firmware. */
|
|
if (caps.mfg == TPM_MFG_STM) {
|
|
rc = wolfTPM2_FirmwareUpgradeHash(&dev, TPM_ALG_SHA384, NULL, 0,
|
|
dummy_manifest, sizeof(dummy_manifest), NULL, NULL);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
}
|
|
#endif /* !WOLFTPM2_NO_WOLFCRYPT && WOLFSSL_SHA384 */
|
|
|
|
/* The probe rebuilds a command code from TPMA_CC. Every TPM implements
|
|
* GetCapability and none a top of range vendor code, so no ST33 needed */
|
|
isImpl = -1;
|
|
AssertIntEQ(wolfTPM2_ST33_CmdImplemented(TPM_CC_GetCapability, &isImpl),
|
|
TPM_RC_SUCCESS);
|
|
AssertIntEQ(isImpl, 1);
|
|
isImpl = -1;
|
|
AssertIntEQ(wolfTPM2_ST33_CmdImplemented((TPM_CC)(CC_VEND + 0x7FFE),
|
|
&isImpl), TPM_RC_SUCCESS);
|
|
AssertIntEQ(isImpl, 0);
|
|
AssertIntEQ(wolfTPM2_ST33_CmdImplemented(TPM_CC_GetCapability, NULL),
|
|
BAD_FUNC_ARG);
|
|
|
|
/* Selection stays self consistent on any TPM: a settled command list
|
|
* gives a real pair, an unsettled one gives exactly the version rule */
|
|
AssertIntEQ(wolfTPM2_ST33_GetFwUpgradeCommands(&caps, 9, &ccStart, &ccData,
|
|
&fromTpm), TPM_RC_SUCCESS);
|
|
if (fromTpm) {
|
|
AssertIntEQ(ccStart == TPM_CC_FieldUpgradeStartVendor_ST33 ||
|
|
ccStart == TPM_CC_FieldUpgradeStart, 1);
|
|
AssertIntEQ(ccData == TPM_CC_FieldUpgradeDataVendor_ST33 ||
|
|
ccData == TPM_CC_FieldUpgradeData, 1);
|
|
}
|
|
else {
|
|
AssertIntEQ(wolfTPM2_ST33_FwUpgradeCommands(caps.fwVerMinor, 1, 9,
|
|
&ccStartRule, &ccDataRule), TPM_RC_SUCCESS);
|
|
AssertIntEQ((int)ccStart, (int)ccStartRule);
|
|
AssertIntEQ((int)ccData, (int)ccDataRule);
|
|
}
|
|
|
|
/* Once the TPM is in firmware upgrade mode it accepts only
|
|
* FieldUpgradeData, so selection must issue no capability query at all.
|
|
* caps NULL is that mode, and it has to resolve from the image alone. */
|
|
AssertIntEQ(wolfTPM2_ST33_GetFwUpgradeCommands(NULL, 2, &ccStart, &ccData,
|
|
&fromTpm), TPM_RC_SUCCESS);
|
|
AssertIntEQ(fromTpm, 0);
|
|
AssertIntEQ((int)ccStart, (int)TPM_CC_FieldUpgradeStart);
|
|
AssertIntEQ((int)ccData, (int)TPM_CC_FieldUpgradeData);
|
|
AssertIntEQ(wolfTPM2_ST33_GetFwUpgradeCommands(NULL, 9, &ccStart, &ccData,
|
|
&fromTpm), TPM_RC_SUCCESS);
|
|
AssertIntEQ(fromTpm, 0);
|
|
AssertIntEQ((int)ccStart, (int)TPM_CC_FieldUpgradeStartVendor_ST33);
|
|
AssertIntEQ(wolfTPM2_ST33_GetFwUpgradeCommands(&caps, 9, NULL, &ccData,
|
|
NULL), BAD_FUNC_ARG);
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test ST33 FW: %-40s Passed\n", "API Availability:");
|
|
}
|
|
#endif /* WOLFTPM_ST33 || WOLFTPM_AUTODETECT */
|
|
#endif /* WOLFTPM_FIRMWARE_UPGRADE */
|
|
|
|
#ifdef WOLFTPM_FIRMWARE_UPGRADE
|
|
/* The vendor FieldUpgradeStart commands serialize an authorization area that
|
|
* carries only the session handle - empty nonceCaller, zero attributes, empty
|
|
* HMAC. A caller session that would need a computed session HMAC or parameter
|
|
* encryption must therefore be rejected by wolfTPM2_FirmwareUpgradeHash_ex
|
|
* before any command is sent, rather than failing on the wire.
|
|
*
|
|
* These cases run against the simulator only (and only when it reports an
|
|
* unknown manufacturer) so a real TPM is never pushed toward firmware-upgrade
|
|
* mode by the accepted-session case. */
|
|
static void test_wolfTPM2_FirmwareUpgrade_ex_session(void)
|
|
{
|
|
#if defined(WOLFTPM_SWTPM)
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_CAPS caps;
|
|
WOLFTPM2_SESSION sess;
|
|
TPM2B_AUTH bindAuth;
|
|
uint8_t hash[TPM_SHA384_DIGEST_SIZE];
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&caps, 0, sizeof(caps));
|
|
XMEMSET(&bindAuth, 0, sizeof(bindAuth));
|
|
XMEMSET(hash, 0, sizeof(hash));
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
rc = wolfTPM2_GetCapabilities(&dev, &caps);
|
|
AssertIntEQ(rc, 0);
|
|
if (caps.mfg != TPM_MFG_UNKNOWN) {
|
|
/* not the simulator - do not exercise firmware upgrade paths.
|
|
* NOTE this gate means "wolfTPM recognizes the manufacturer", which
|
|
* is only a proxy for "real hardware". TPM_MFG_MSFT covers both the
|
|
* ms-tpm-20-ref simulator and firmware TPMs built from it (e.g. the
|
|
* Jetson OP-TEE fTPM), so against that simulator these tests skip
|
|
* rather than run. Skipping is the safe direction; distinguishing
|
|
* the two needs a real simulator check. */
|
|
wolfTPM2_Cleanup(&dev);
|
|
printf("Test FW Upgr _ex: %-40s Skipped (real TPM)\n",
|
|
"Session Validation:");
|
|
return;
|
|
}
|
|
|
|
/* A clean, unsalted, unbound policy session passes validation and reaches
|
|
* the manufacturer dispatch, which rejects the simulator with
|
|
* TPM_RC_COMMAND_CODE. This proves the checks below are real rejections
|
|
* and not just the generic argument handling. */
|
|
XMEMSET(&sess, 0, sizeof(sess));
|
|
sess.handle.hndl = POLICY_SESSION_FIRST;
|
|
rc = wolfTPM2_FirmwareUpgradeHash_ex(&dev, TPM_ALG_SHA384,
|
|
hash, (uint32_t)sizeof(hash), NULL, 0, NULL, NULL, &sess);
|
|
AssertIntEQ(rc, TPM_RC_COMMAND_CODE);
|
|
|
|
/* An HMAC (non-policy) session handle always needs a session HMAC */
|
|
XMEMSET(&sess, 0, sizeof(sess));
|
|
sess.handle.hndl = HMAC_SESSION_FIRST;
|
|
AssertIntEQ(wolfTPM2_FirmwareUpgradeHash_ex(&dev, TPM_ALG_SHA384,
|
|
hash, (uint32_t)sizeof(hash), NULL, 0, NULL, NULL, &sess),
|
|
BAD_FUNC_ARG);
|
|
|
|
/* wolfTPM2_PolicyAuthValue marks the session; the auth value it needs is
|
|
* not serialized by the vendor command */
|
|
XMEMSET(&sess, 0, sizeof(sess));
|
|
sess.handle.hndl = POLICY_SESSION_FIRST;
|
|
sess.handle.policyAuth = 1;
|
|
AssertIntEQ(wolfTPM2_FirmwareUpgradeHash_ex(&dev, TPM_ALG_SHA384,
|
|
hash, (uint32_t)sizeof(hash), NULL, 0, NULL, NULL, &sess),
|
|
BAD_FUNC_ARG);
|
|
|
|
/* wolfTPM2_PolicyPassword likewise */
|
|
XMEMSET(&sess, 0, sizeof(sess));
|
|
sess.handle.hndl = POLICY_SESSION_FIRST;
|
|
sess.handle.policyPass = 1;
|
|
AssertIntEQ(wolfTPM2_FirmwareUpgradeHash_ex(&dev, TPM_ALG_SHA384,
|
|
hash, (uint32_t)sizeof(hash), NULL, 0, NULL, NULL, &sess),
|
|
BAD_FUNC_ARG);
|
|
|
|
/* an attached auth value implies a non-empty session HMAC */
|
|
XMEMSET(&sess, 0, sizeof(sess));
|
|
sess.handle.hndl = POLICY_SESSION_FIRST;
|
|
sess.handle.auth.size = 4;
|
|
AssertIntEQ(wolfTPM2_FirmwareUpgradeHash_ex(&dev, TPM_ALG_SHA384,
|
|
hash, (uint32_t)sizeof(hash), NULL, 0, NULL, NULL, &sess),
|
|
BAD_FUNC_ARG);
|
|
|
|
/* a bound session implies a non-empty session HMAC */
|
|
XMEMSET(&sess, 0, sizeof(sess));
|
|
sess.handle.hndl = POLICY_SESSION_FIRST;
|
|
sess.bind = &bindAuth;
|
|
AssertIntEQ(wolfTPM2_FirmwareUpgradeHash_ex(&dev, TPM_ALG_SHA384,
|
|
hash, (uint32_t)sizeof(hash), NULL, 0, NULL, NULL, &sess),
|
|
BAD_FUNC_ARG);
|
|
|
|
/* a salted session implies a non-empty session HMAC */
|
|
XMEMSET(&sess, 0, sizeof(sess));
|
|
sess.handle.hndl = POLICY_SESSION_FIRST;
|
|
sess.salt.size = 16;
|
|
AssertIntEQ(wolfTPM2_FirmwareUpgradeHash_ex(&dev, TPM_ALG_SHA384,
|
|
hash, (uint32_t)sizeof(hash), NULL, 0, NULL, NULL, &sess),
|
|
BAD_FUNC_ARG);
|
|
|
|
/* parameter encryption is not applied on this raw path */
|
|
XMEMSET(&sess, 0, sizeof(sess));
|
|
sess.handle.hndl = POLICY_SESSION_FIRST;
|
|
sess.sessionAttributes = TPMA_SESSION_encrypt;
|
|
AssertIntEQ(wolfTPM2_FirmwareUpgradeHash_ex(&dev, TPM_ALG_SHA384,
|
|
hash, (uint32_t)sizeof(hash), NULL, 0, NULL, NULL, &sess),
|
|
BAD_FUNC_ARG);
|
|
|
|
/* the same validation guards the recover entry point */
|
|
XMEMSET(&sess, 0, sizeof(sess));
|
|
sess.handle.hndl = POLICY_SESSION_FIRST;
|
|
sess.handle.policyAuth = 1;
|
|
AssertIntEQ(wolfTPM2_FirmwareUpgradeRecover_ex(&dev, NULL, 0, NULL, NULL,
|
|
&sess), BAD_FUNC_ARG);
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
printf("Test FW Upgr _ex: %-40s Passed\n", "Session Validation:");
|
|
#else
|
|
printf("Test FW Upgr _ex: %-40s Skipped (requires SWTPM)\n",
|
|
"Session Validation:");
|
|
#endif /* WOLFTPM_SWTPM */
|
|
}
|
|
#endif /* WOLFTPM_FIRMWARE_UPGRADE */
|
|
|
|
/* Regression test for hierarchy authorization after SetPrimaryPolicy.
|
|
*
|
|
* Per TPM 2.0 Part 1 Sec.19.7 a hierarchy is authorized by EITHER its
|
|
* authValue OR its authPolicy. Installing an authPolicy therefore does not
|
|
* lock out the password path, which is what lets the firmware examples roll
|
|
* back a policy they provisioned (see examples/firmware/firmware_policy.c).
|
|
* This pins that behavior: a password session can still clear the policy, and
|
|
* a policy session with a non-matching digest is still rejected (so the
|
|
* password success above is not simply an unchecked auth path).
|
|
*
|
|
* Simulator only - never provision a platform policy on a real TPM. */
|
|
static void test_wolfTPM2_SetPrimaryPolicy_rollback(void)
|
|
{
|
|
#if defined(WOLFTPM_SWTPM) && !defined(WOLFTPM2_NO_WOLFCRYPT)
|
|
int rc;
|
|
int startRc = 0, authRc = 0, clearRc = 0;
|
|
int mismatchRc = TPM_RC_SUCCESS; /* must end up != SUCCESS */
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_CAPS caps;
|
|
WOLFTPM2_SESSION sess;
|
|
byte policy[TPM_MAX_DIGEST_SIZE];
|
|
word32 policySz = (word32)sizeof(policy);
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&caps, 0, sizeof(caps));
|
|
XMEMSET(&sess, 0, sizeof(sess));
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
rc = wolfTPM2_GetCapabilities(&dev, &caps);
|
|
AssertIntEQ(rc, 0);
|
|
if (caps.mfg != TPM_MFG_UNKNOWN) {
|
|
/* see the TPM_MFG_MSFT note on the first such gate above */
|
|
wolfTPM2_Cleanup(&dev);
|
|
printf("Test SetPrimPol: %-40s Skipped (real TPM)\n", "Rollback:");
|
|
return;
|
|
}
|
|
/* defensive: clear any policy a previously aborted run left behind */
|
|
(void)wolfTPM2_SetPrimaryPolicy(&dev, TPM_RH_PLATFORM, TPM_ALG_NULL,
|
|
NULL, 0);
|
|
|
|
/* Provision a platform authPolicy, mirroring what the firmware examples do
|
|
* before an upgrade. Use the same digest shape: PolicyCommandCode. */
|
|
rc = wolfTPM2_PolicyCommandCodeMake(TPM_ALG_SHA256, policy, &policySz,
|
|
TPM_CC_SetPrimaryPolicy);
|
|
AssertIntEQ(rc, 0);
|
|
rc = wolfTPM2_SetPrimaryPolicy(&dev, TPM_RH_PLATFORM, TPM_ALG_SHA256,
|
|
policy, policySz);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* From here the platform hierarchy is gated by that policy. Assert*() is
|
|
* abort(), so record results into locals and do not assert until the
|
|
* policy has been cleared again - otherwise a failure here would leave a
|
|
* persistent swtpm state dir with a policy-gated platform hierarchy,
|
|
* wedging every later run. */
|
|
|
|
/* A policy session whose running digest does not match the installed
|
|
* authPolicy must be rejected - proves the policy is actually enforced. */
|
|
startRc = wolfTPM2_StartSession(&dev, &sess, NULL, NULL, TPM_SE_POLICY,
|
|
TPM_ALG_NULL);
|
|
if (startRc == 0) {
|
|
authRc = wolfTPM2_SetAuthSession(&dev, 0, &sess, 0);
|
|
if (authRc == 0) {
|
|
/* expected to FAIL: digest does not match */
|
|
mismatchRc = wolfTPM2_SetPrimaryPolicy(&dev, TPM_RH_PLATFORM,
|
|
TPM_ALG_NULL, NULL, 0);
|
|
}
|
|
/* restore default password authorization and release the session */
|
|
wolfTPM2_SetAuthPassword(&dev, 0, NULL);
|
|
wolfTPM2_UnloadHandle(&dev, &sess.handle);
|
|
}
|
|
|
|
/* The password path still authorizes the hierarchy, so the example's
|
|
* rollback works without a session that satisfies the installed policy.
|
|
* This also restores the simulator to a clean state. */
|
|
clearRc = wolfTPM2_SetPrimaryPolicy(&dev, TPM_RH_PLATFORM, TPM_ALG_NULL,
|
|
NULL, 0);
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
/* safe to abort now - the platform hierarchy carries no policy */
|
|
AssertIntEQ(startRc, 0);
|
|
AssertIntEQ(authRc, 0);
|
|
AssertIntNE(mismatchRc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(clearRc, TPM_RC_SUCCESS);
|
|
printf("Test SetPrimPol: %-40s Passed\n", "Rollback:");
|
|
#else
|
|
printf("Test SetPrimPol: %-40s Skipped (requires SWTPM)\n", "Rollback:");
|
|
#endif /* WOLFTPM_SWTPM && !WOLFTPM2_NO_WOLFCRYPT */
|
|
}
|
|
|
|
/* Cover the mechanism behind firmware_policy_clear_by_policy(): when
|
|
* platformAuth is NOT the default empty password, the password rollback path
|
|
* fails and the example falls back to authorizing TPM2_SetPrimaryPolicy under
|
|
* the provisioned PolicyOR's PolicyCommandCode(TPM_CC_SetPrimaryPolicy)
|
|
* branch. The example helper itself lives in examples/ and is not linked into
|
|
* the unit suite, so this exercises the same library call sequence directly.
|
|
*
|
|
* Simulator only - this sets and clears platformAuth. */
|
|
static void test_wolfTPM2_PolicyClear_underPolicy(void)
|
|
{
|
|
#if defined(WOLFTPM_SWTPM) && !defined(WOLFTPM2_NO_WOLFCRYPT)
|
|
int rc;
|
|
int pwClearRc = 0, polClearRc = 0, finalRc = 0;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_CAPS caps;
|
|
WOLFTPM2_SESSION sess;
|
|
TPML_DIGEST orList;
|
|
TPM2B_AUTH platAuth;
|
|
HierarchyChangeAuth_In changeIn;
|
|
byte branchFu[TPM_MAX_DIGEST_SIZE];
|
|
byte branchSpp[TPM_MAX_DIGEST_SIZE];
|
|
byte concat[2 * TPM_MAX_DIGEST_SIZE];
|
|
byte policy[TPM_MAX_DIGEST_SIZE];
|
|
word32 aSz, bSz, polSz;
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&caps, 0, sizeof(caps));
|
|
XMEMSET(&sess, 0, sizeof(sess));
|
|
XMEMSET(&orList, 0, sizeof(orList));
|
|
XMEMSET(&platAuth, 0, sizeof(platAuth));
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
rc = wolfTPM2_GetCapabilities(&dev, &caps);
|
|
AssertIntEQ(rc, 0);
|
|
if (caps.mfg != TPM_MFG_UNKNOWN) {
|
|
/* see the TPM_MFG_MSFT note on the first such gate above */
|
|
wolfTPM2_Cleanup(&dev);
|
|
printf("Test PolicyClear: %-40s Skipped (real TPM)\n", "Under Policy:");
|
|
return;
|
|
}
|
|
(void)wolfTPM2_SetPrimaryPolicy(&dev, TPM_RH_PLATFORM, TPM_ALG_NULL,
|
|
NULL, 0);
|
|
|
|
/* Build the two branches the example provisions: the firmware-start
|
|
* branch (stood in for by NV_Read here, since the vendor CC is not
|
|
* meaningful to the simulator) and the SetPrimaryPolicy rollback branch. */
|
|
aSz = (word32)sizeof(branchFu);
|
|
AssertIntEQ(wolfTPM2_PolicyCommandCodeMake(TPM_ALG_SHA256, branchFu, &aSz,
|
|
TPM_CC_NV_Read), 0);
|
|
bSz = (word32)sizeof(branchSpp);
|
|
AssertIntEQ(wolfTPM2_PolicyCommandCodeMake(TPM_ALG_SHA256, branchSpp, &bSz,
|
|
TPM_CC_SetPrimaryPolicy), 0);
|
|
XMEMCPY(concat, branchFu, aSz);
|
|
XMEMCPY(&concat[aSz], branchSpp, bSz);
|
|
XMEMSET(policy, 0, sizeof(policy));
|
|
polSz = TPM_SHA256_DIGEST_SIZE;
|
|
AssertIntEQ(wolfTPM2_PolicyHash(TPM_ALG_SHA256, policy, &polSz,
|
|
TPM_CC_PolicyOR, concat, aSz + bSz), 0);
|
|
|
|
/* provision the platform authPolicy while platformAuth is still empty */
|
|
AssertIntEQ(wolfTPM2_SetPrimaryPolicy(&dev, TPM_RH_PLATFORM,
|
|
TPM_ALG_SHA256, policy, polSz), TPM_RC_SUCCESS);
|
|
|
|
/* Now make the password path unusable, the situation the fallback exists
|
|
* for. Use TPM2_HierarchyChangeAuth directly with a KNOWN value rather
|
|
* than wolfTPM2_ChangeHierarchyAuth, which sets a random auth that could
|
|
* never be restored - that would permanently wedge the simulator's
|
|
* platform hierarchy for every later test. Everything from here records
|
|
* into locals so an abort cannot leave the hierarchy modified. */
|
|
XMEMSET(&changeIn, 0, sizeof(changeIn));
|
|
changeIn.authHandle = TPM_RH_PLATFORM;
|
|
changeIn.newAuth.size = 4;
|
|
XMEMCPY(changeIn.newAuth.buffer, "hier", 4);
|
|
rc = TPM2_HierarchyChangeAuth(&changeIn);
|
|
/* Deliberately leave dev->session[0] holding the EMPTY password: that is
|
|
* what makes the example's first rollback attempt fail below, which is the
|
|
* condition the policy fallback exists to handle. platAuth is kept so the
|
|
* restore at the end can authorize itself. */
|
|
platAuth.size = 4;
|
|
XMEMCPY(platAuth.buffer, "hier", 4);
|
|
|
|
if (rc == 0) {
|
|
/* the example's first attempt - password auth - must now fail */
|
|
pwClearRc = wolfTPM2_SetPrimaryPolicy(&dev, TPM_RH_PLATFORM,
|
|
TPM_ALG_NULL, NULL, 0);
|
|
|
|
/* fallback: satisfy the SetPrimaryPolicy branch, then clear */
|
|
if (wolfTPM2_StartSession(&dev, &sess, NULL, NULL, TPM_SE_POLICY,
|
|
TPM_ALG_NULL) == 0) {
|
|
if (wolfTPM2_PolicyCommandCode(&dev, &sess,
|
|
TPM_CC_SetPrimaryPolicy) == 0) {
|
|
orList.count = 2;
|
|
orList.digests[0].size = (UINT16)aSz;
|
|
XMEMCPY(orList.digests[0].buffer, branchFu, aSz);
|
|
orList.digests[1].size = (UINT16)bSz;
|
|
XMEMCPY(orList.digests[1].buffer, branchSpp, bSz);
|
|
if (wolfTPM2_PolicyOR(&dev, &sess, &orList) == 0 &&
|
|
wolfTPM2_SetAuthSession(&dev, 0, &sess, 0) == 0) {
|
|
polClearRc = wolfTPM2_SetPrimaryPolicy(&dev,
|
|
TPM_RH_PLATFORM, TPM_ALG_NULL, NULL, 0);
|
|
}
|
|
}
|
|
wolfTPM2_SetAuthPassword(&dev, 0, NULL);
|
|
wolfTPM2_UnloadHandle(&dev, &sess.handle);
|
|
}
|
|
}
|
|
|
|
/* restore the default empty platformAuth so later tests are unaffected.
|
|
* The current (known) auth authorizes this change. */
|
|
wolfTPM2_SetAuthPassword(&dev, 0, &platAuth);
|
|
XMEMSET(&changeIn, 0, sizeof(changeIn));
|
|
changeIn.authHandle = TPM_RH_PLATFORM;
|
|
changeIn.newAuth.size = 0;
|
|
finalRc = TPM2_HierarchyChangeAuth(&changeIn);
|
|
wolfTPM2_SetAuthPassword(&dev, 0, NULL);
|
|
(void)wolfTPM2_SetPrimaryPolicy(&dev, TPM_RH_PLATFORM, TPM_ALG_NULL,
|
|
NULL, 0);
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
/* safe to abort now - hierarchy state has been restored */
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntNE(pwClearRc, TPM_RC_SUCCESS); /* password path must fail */
|
|
AssertIntEQ(polClearRc, TPM_RC_SUCCESS); /* policy fallback must work */
|
|
AssertIntEQ(finalRc, 0);
|
|
printf("Test PolicyClear: %-40s Passed\n", "Under Policy:");
|
|
#else
|
|
printf("Test PolicyClear: %-40s Skipped (requires SWTPM)\n",
|
|
"Under Policy:");
|
|
#endif /* WOLFTPM_SWTPM && !WOLFTPM2_NO_WOLFCRYPT */
|
|
}
|
|
|
|
/* Argument-validation coverage for wolfTPM2_PolicyOR (host-side, no TPM). */
|
|
static void test_wolfTPM2_PolicyOR(void)
|
|
{
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_SESSION sess;
|
|
TPML_DIGEST list;
|
|
word32 cap = (word32)(sizeof(list.digests) / sizeof(list.digests[0]));
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&sess, 0, sizeof(sess));
|
|
XMEMSET(&list, 0, sizeof(list));
|
|
list.count = 2;
|
|
list.digests[0].size = TPM_SHA256_DIGEST_SIZE;
|
|
list.digests[1].size = TPM_SHA256_DIGEST_SIZE;
|
|
|
|
/* NULL pointer arguments */
|
|
AssertIntEQ(wolfTPM2_PolicyOR(NULL, &sess, &list), BAD_FUNC_ARG);
|
|
AssertIntEQ(wolfTPM2_PolicyOR(&dev, NULL, &list), BAD_FUNC_ARG);
|
|
AssertIntEQ(wolfTPM2_PolicyOR(&dev, &sess, NULL), BAD_FUNC_ARG);
|
|
|
|
/* count of 0 is invalid */
|
|
list.count = 0;
|
|
AssertIntEQ(wolfTPM2_PolicyOR(&dev, &sess, &list), BAD_FUNC_ARG);
|
|
|
|
/* TPM2_PolicyOR requires at least two digests (TPM 2.0 Part 3 Sec.23.6),
|
|
* so a one-branch list must be rejected here rather than sent to the TPM,
|
|
* which would answer TPM_RC_VALUE. */
|
|
list.count = 1;
|
|
AssertIntEQ(wolfTPM2_PolicyOR(&dev, &sess, &list), BAD_FUNC_ARG);
|
|
|
|
/* count beyond the TPML_DIGEST capacity is invalid */
|
|
list.count = cap + 1;
|
|
AssertIntEQ(wolfTPM2_PolicyOR(&dev, &sess, &list), BAD_FUNC_ARG);
|
|
|
|
/* a branch digest size larger than the buffer is invalid (CWE-125). Use a
|
|
* valid count so this fails for the size reason, not the count reason. */
|
|
list.count = 2;
|
|
list.digests[0].size = (UINT16)(sizeof(list.digests[0].buffer) + 1);
|
|
AssertIntEQ(wolfTPM2_PolicyOR(&dev, &sess, &list), BAD_FUNC_ARG);
|
|
|
|
printf("Test PolicyOR: %-40s Passed\n", "Arg Validation:");
|
|
}
|
|
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
/* Known-answer + arg-validation for wolfTPM2_PolicyCommandCodeMake (no TPM).
|
|
* Requires wolfCrypt for the policy hash. Vectors are the offline digest
|
|
* H(zeros(hashSz) || TPM_CC_PolicyCommandCode || TPM_CC_NV_Read). */
|
|
static void test_wolfTPM2_PolicyCommandCodeMake(void)
|
|
{
|
|
int rc;
|
|
byte digest[TPM_MAX_DIGEST_SIZE];
|
|
byte guard[TPM_MAX_DIGEST_SIZE]; /* canary to detect any write */
|
|
word32 digestSz = 0;
|
|
/* SHA2-256 (also in examples/nvram/extend.c) */
|
|
static const byte expected256[] = {
|
|
0x47,0xce,0x30,0x32,0xd8,0xba,0xd1,0xf3,
|
|
0x08,0x9c,0xb0,0xc0,0x90,0x88,0xde,0x43,
|
|
0x50,0x14,0x91,0xd4,0x60,0x40,0x2b,0x90,
|
|
0xcd,0x1b,0x7f,0xc0,0xb6,0x8c,0xa9,0x2f
|
|
};
|
|
#ifdef WOLFSSL_SHA384
|
|
static const byte expected384[] = {
|
|
0xfb,0xdd,0x14,0x92,0x1c,0x8b,0xd9,0x5c,
|
|
0x9f,0x35,0x96,0x79,0xd2,0xbf,0x75,0x78,
|
|
0xb1,0x47,0xe8,0x29,0x83,0x21,0xf8,0xe9,
|
|
0xea,0xc4,0x4c,0x11,0x77,0x2f,0xfa,0x6e,
|
|
0xe5,0x91,0x78,0x43,0x47,0x83,0x9b,0xef,
|
|
0xf1,0x22,0xf2,0x14,0x4d,0xd0,0xb0,0xf0
|
|
};
|
|
#endif
|
|
#ifdef WOLFSSL_SHA512
|
|
static const byte expected512[] = {
|
|
0x31,0x38,0x6a,0xba,0x16,0xd8,0xf0,0x64,
|
|
0xbd,0x51,0x4d,0x1d,0xd9,0x48,0x1c,0x65,
|
|
0x6d,0x0e,0x32,0xe2,0xad,0x84,0x8e,0x1b,
|
|
0xe9,0xb9,0xab,0x1d,0xd6,0x6f,0xfa,0xd2,
|
|
0xc5,0xc0,0x2d,0x22,0x1c,0x61,0xd2,0x01,
|
|
0x99,0x4e,0xd8,0x30,0x6b,0x77,0x0e,0x56,
|
|
0xbb,0x13,0x05,0x32,0xdf,0x62,0xea,0x8d,
|
|
0x06,0xc6,0xdf,0x53,0x5f,0x19,0xb8,0x21
|
|
};
|
|
#endif
|
|
|
|
/* NULL argument rejection */
|
|
digestSz = (word32)sizeof(digest);
|
|
AssertIntEQ(wolfTPM2_PolicyCommandCodeMake(TPM_ALG_SHA256, NULL, &digestSz,
|
|
TPM_CC_NV_Read), BAD_FUNC_ARG);
|
|
AssertIntEQ(wolfTPM2_PolicyCommandCodeMake(TPM_ALG_SHA256, digest, NULL,
|
|
TPM_CC_NV_Read), BAD_FUNC_ARG);
|
|
/* Unsupported hash algorithm rejection */
|
|
AssertIntEQ(wolfTPM2_PolicyCommandCodeMake(TPM_ALG_NULL, digest, &digestSz,
|
|
TPM_CC_NV_Read), BAD_FUNC_ARG);
|
|
|
|
/* digestSz is in/out: on input it is the buffer capacity. A capacity
|
|
* smaller than the hash size must return BUFFER_E and must not write to
|
|
* digest (which the function would otherwise zero and hash into) nor
|
|
* clobber the caller's capacity value. */
|
|
XMEMSET(guard, 0xA5, sizeof(guard));
|
|
XMEMCPY(digest, guard, sizeof(guard));
|
|
digestSz = TPM_SHA256_DIGEST_SIZE - 1;
|
|
AssertIntEQ(wolfTPM2_PolicyCommandCodeMake(TPM_ALG_SHA256, digest,
|
|
&digestSz, TPM_CC_NV_Read), BUFFER_E);
|
|
AssertIntEQ(XMEMCMP(digest, guard, sizeof(guard)), 0);
|
|
AssertIntEQ((int)digestSz, TPM_SHA256_DIGEST_SIZE - 1);
|
|
|
|
/* one-byte capacity must not be overrun either */
|
|
digestSz = 1;
|
|
AssertIntEQ(wolfTPM2_PolicyCommandCodeMake(TPM_ALG_SHA256, digest,
|
|
&digestSz, TPM_CC_NV_Read), BUFFER_E);
|
|
AssertIntEQ(XMEMCMP(digest, guard, sizeof(guard)), 0);
|
|
#ifdef WOLFSSL_SHA512
|
|
/* a SHA2-512 digest does not fit a SHA2-256 sized buffer */
|
|
digestSz = TPM_SHA256_DIGEST_SIZE;
|
|
AssertIntEQ(wolfTPM2_PolicyCommandCodeMake(TPM_ALG_SHA512, digest,
|
|
&digestSz, TPM_CC_NV_Read), BUFFER_E);
|
|
AssertIntEQ(XMEMCMP(digest, guard, sizeof(guard)), 0);
|
|
#endif
|
|
/* exactly the hash size is sufficient */
|
|
digestSz = TPM_SHA256_DIGEST_SIZE;
|
|
AssertIntEQ(wolfTPM2_PolicyCommandCodeMake(TPM_ALG_SHA256, digest,
|
|
&digestSz, TPM_CC_NV_Read), 0);
|
|
AssertIntEQ((int)digestSz, TPM_SHA256_DIGEST_SIZE);
|
|
|
|
/* SHA2-256 known-answer */
|
|
digestSz = (word32)sizeof(digest);
|
|
rc = wolfTPM2_PolicyCommandCodeMake(TPM_ALG_SHA256, digest, &digestSz,
|
|
TPM_CC_NV_Read);
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntEQ((int)digestSz, (int)sizeof(expected256));
|
|
AssertIntEQ(XMEMCMP(digest, expected256, sizeof(expected256)), 0);
|
|
#ifdef WOLFSSL_SHA384
|
|
digestSz = (word32)sizeof(digest);
|
|
rc = wolfTPM2_PolicyCommandCodeMake(TPM_ALG_SHA384, digest, &digestSz,
|
|
TPM_CC_NV_Read);
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntEQ((int)digestSz, (int)sizeof(expected384));
|
|
AssertIntEQ(XMEMCMP(digest, expected384, sizeof(expected384)), 0);
|
|
#endif
|
|
#ifdef WOLFSSL_SHA512
|
|
digestSz = (word32)sizeof(digest);
|
|
rc = wolfTPM2_PolicyCommandCodeMake(TPM_ALG_SHA512, digest, &digestSz,
|
|
TPM_CC_NV_Read);
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntEQ((int)digestSz, (int)sizeof(expected512));
|
|
AssertIntEQ(XMEMCMP(digest, expected512, sizeof(expected512)), 0);
|
|
#endif
|
|
|
|
printf("Test PolicyCCMake:%-40s Passed\n", "Known Vectors:");
|
|
}
|
|
#endif /* !WOLFTPM2_NO_WOLFCRYPT */
|
|
|
|
/* Arg-validation for wolfTPM2_SetPrimaryPolicy (no TPM). */
|
|
static void test_wolfTPM2_SetPrimaryPolicy(void)
|
|
{
|
|
WOLFTPM2_DEV dev;
|
|
byte pol[TPM_MAX_DIGEST_SIZE + 4];
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(pol, 0, sizeof(pol));
|
|
|
|
/* NULL dev */
|
|
AssertIntEQ(wolfTPM2_SetPrimaryPolicy(NULL, TPM_RH_PLATFORM,
|
|
TPM_ALG_SHA256, pol, TPM_SHA256_DIGEST_SIZE), BAD_FUNC_ARG);
|
|
/* policy digest larger than the buffer */
|
|
AssertIntEQ(wolfTPM2_SetPrimaryPolicy(&dev, TPM_RH_PLATFORM,
|
|
TPM_ALG_SHA256, pol, (word32)sizeof(pol)), BAD_FUNC_ARG);
|
|
/* NULL policy with a non-zero size must not silently clear the policy */
|
|
AssertIntEQ(wolfTPM2_SetPrimaryPolicy(&dev, TPM_RH_PLATFORM,
|
|
TPM_ALG_SHA256, NULL, TPM_SHA256_DIGEST_SIZE), BAD_FUNC_ARG);
|
|
|
|
printf("Test SetPrimPol: %-40s Passed\n", "Arg Validation:");
|
|
}
|
|
|
|
/* Argument handling and, against the simulator, the success paths of
|
|
* wolfTPM2_IsAlgSupported. */
|
|
static void test_wolfTPM2_IsAlgSupported(void)
|
|
{
|
|
int isSupported = 1; /* seeded true to prove the error paths clear it */
|
|
#if defined(WOLFTPM_SWTPM)
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
#endif
|
|
|
|
/* NULL dev must fail and must not leave the out-param saying "supported" */
|
|
AssertIntEQ(wolfTPM2_IsAlgSupported(NULL, TPM_ALG_SHA256, &isSupported),
|
|
BAD_FUNC_ARG);
|
|
AssertIntEQ(isSupported, 0);
|
|
/* NULL out-param */
|
|
AssertIntEQ(wolfTPM2_IsAlgSupported(NULL, TPM_ALG_SHA256, NULL),
|
|
BAD_FUNC_ARG);
|
|
|
|
#if defined(WOLFTPM_SWTPM)
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* SHA2-256 is mandatory for a TPM 2.0 part, so it must report supported
|
|
* with a success rc */
|
|
isSupported = 0;
|
|
AssertIntEQ(wolfTPM2_IsAlgSupported(&dev, TPM_ALG_SHA256, &isSupported),
|
|
TPM_RC_SUCCESS);
|
|
AssertIntEQ(isSupported, 1);
|
|
|
|
/* an algorithm identifier no TPM implements must report unsupported, still
|
|
* with a success rc (the query itself worked) */
|
|
isSupported = 1;
|
|
AssertIntEQ(wolfTPM2_IsAlgSupported(&dev, (TPM_ALG_ID)0x7FFF,
|
|
&isSupported), TPM_RC_SUCCESS);
|
|
AssertIntEQ(isSupported, 0);
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
printf("Test IsAlgSupp: %-40s Passed\n", "Args + Query:");
|
|
#else
|
|
printf("Test IsAlgSupp: %-40s Passed\n", "Arg Validation:");
|
|
#endif /* WOLFTPM_SWTPM */
|
|
}
|
|
|
|
/* Success path for wolfTPM2_PolicyOR: satisfy one branch of a real two-branch
|
|
* OR on a live policy session and confirm the TPM's running policy digest
|
|
* matches the offline computation. Simulator only. */
|
|
static void test_wolfTPM2_PolicyOR_success(void)
|
|
{
|
|
#if defined(WOLFTPM_SWTPM) && !defined(WOLFTPM2_NO_WOLFCRYPT)
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_SESSION sess;
|
|
TPML_DIGEST list;
|
|
byte branchA[TPM_MAX_DIGEST_SIZE];
|
|
byte branchB[TPM_MAX_DIGEST_SIZE];
|
|
byte concat[2 * TPM_MAX_DIGEST_SIZE];
|
|
byte expected[TPM_MAX_DIGEST_SIZE];
|
|
byte got[TPM_MAX_DIGEST_SIZE];
|
|
word32 aSz, bSz, expSz, gotSz;
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&sess, 0, sizeof(sess));
|
|
XMEMSET(&list, 0, sizeof(list));
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* two distinct PolicyCommandCode branches */
|
|
aSz = (word32)sizeof(branchA);
|
|
AssertIntEQ(wolfTPM2_PolicyCommandCodeMake(TPM_ALG_SHA256, branchA, &aSz,
|
|
TPM_CC_NV_Read), 0);
|
|
bSz = (word32)sizeof(branchB);
|
|
AssertIntEQ(wolfTPM2_PolicyCommandCodeMake(TPM_ALG_SHA256, branchB, &bSz,
|
|
TPM_CC_Unseal), 0);
|
|
|
|
/* offline expected digest = H(zeros || TPM_CC_PolicyOR || A || B) */
|
|
XMEMCPY(concat, branchA, aSz);
|
|
XMEMCPY(&concat[aSz], branchB, bSz);
|
|
XMEMSET(expected, 0, sizeof(expected));
|
|
expSz = TPM_SHA256_DIGEST_SIZE;
|
|
AssertIntEQ(wolfTPM2_PolicyHash(TPM_ALG_SHA256, expected, &expSz,
|
|
TPM_CC_PolicyOR, concat, aSz + bSz), 0);
|
|
|
|
/* satisfy branch A on a live session, then OR against {A,B} */
|
|
rc = wolfTPM2_StartSession(&dev, &sess, NULL, NULL, TPM_SE_POLICY,
|
|
TPM_ALG_NULL);
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntEQ(wolfTPM2_PolicyCommandCode(&dev, &sess, TPM_CC_NV_Read), 0);
|
|
|
|
list.count = 2;
|
|
list.digests[0].size = (UINT16)aSz;
|
|
XMEMCPY(list.digests[0].buffer, branchA, aSz);
|
|
list.digests[1].size = (UINT16)bSz;
|
|
XMEMCPY(list.digests[1].buffer, branchB, bSz);
|
|
AssertIntEQ(wolfTPM2_PolicyOR(&dev, &sess, &list), 0);
|
|
|
|
/* the TPM's running digest must match the offline value */
|
|
gotSz = (word32)sizeof(got);
|
|
AssertIntEQ(wolfTPM2_GetPolicyDigest(&dev, sess.handle.hndl, got, &gotSz),
|
|
0);
|
|
AssertIntEQ((int)gotSz, (int)expSz);
|
|
AssertIntEQ(XMEMCMP(got, expected, expSz), 0);
|
|
|
|
wolfTPM2_UnloadHandle(&dev, &sess.handle);
|
|
wolfTPM2_Cleanup(&dev);
|
|
printf("Test PolicyOR: %-40s Passed\n", "Two-Branch Success:");
|
|
#else
|
|
printf("Test PolicyOR: %-40s Skipped (requires SWTPM)\n",
|
|
"Two-Branch Success:");
|
|
#endif /* WOLFTPM_SWTPM && !WOLFTPM2_NO_WOLFCRYPT */
|
|
}
|
|
|
|
static void test_wolfTPM2_GetRandom(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_BUFFER rngData;
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Test arguments */
|
|
rc = wolfTPM2_GetRandom(NULL, rngData.buffer, sizeof(rngData.buffer));
|
|
AssertIntNE(rc, 0);
|
|
rc = wolfTPM2_GetRandom(&dev, NULL, sizeof(rngData.buffer));
|
|
AssertIntNE(rc, 0);
|
|
rc = wolfTPM2_GetRandom(&dev, rngData.buffer, 0);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Test success */
|
|
rc = wolfTPM2_GetRandom(&dev, rngData.buffer, sizeof(rngData.buffer));
|
|
|
|
AssertIntEQ(rc, 0);
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper: %-40s %s\n", "Get Random:",
|
|
rc == 0 ? "Passed" : "Failed");
|
|
}
|
|
|
|
static void test_wolfTPM2_HashFinish_BufferTooSmall(void)
|
|
{
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_HASH hash;
|
|
byte digest[TPM_SHA256_DIGEST_SIZE];
|
|
word32 digestSz;
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
XMEMSET(&hash, 0, sizeof(hash));
|
|
rc = wolfTPM2_HashStart(&dev, &hash, TPM_ALG_SHA256, NULL, 0);
|
|
AssertIntEQ(rc, 0);
|
|
rc = wolfTPM2_HashUpdate(&dev, &hash, (const byte*)"abc", 3);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Undersized buffer must be rejected, not silently truncated, and the
|
|
* required size reported back to the caller. */
|
|
digestSz = 1;
|
|
rc = wolfTPM2_HashFinish(&dev, &hash, digest, &digestSz);
|
|
AssertIntEQ(BUFFER_E, rc);
|
|
AssertIntEQ(TPM_SHA256_DIGEST_SIZE, (int)digestSz);
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "HashFinish BufferTooSmall:");
|
|
#else
|
|
printf("Test TPM Wrapper: %-40s Skipped\n", "HashFinish BufferTooSmall:");
|
|
#endif
|
|
}
|
|
|
|
static void test_TPM2_PCRSel(void)
|
|
{
|
|
int rc = 0;
|
|
TPML_PCR_SELECTION pcr;
|
|
/* This array holds PCR indexes, not a select bitmap, so it is sized by the
|
|
* number of indexes the test uses. PCR_SELECT_MAX is a byte count and is
|
|
* only 1 in a reduced-PCR build (IMPLEMENTATION_PCR <= 8). */
|
|
byte pcrArray[3];
|
|
word32 pcrArraySz;
|
|
|
|
XMEMSET(&pcr, 0, sizeof(pcr));
|
|
XMEMSET(pcrArray, 0, sizeof(pcrArray));
|
|
|
|
pcrArraySz = 0;
|
|
pcrArray[pcrArraySz++] = 1;
|
|
pcrArray[pcrArraySz++] = 2;
|
|
pcrArray[pcrArraySz++] = 3;
|
|
TPM2_SetupPCRSelArray(&pcr, TPM_ALG_SHA, pcrArray, pcrArraySz);
|
|
|
|
pcrArraySz = 0;
|
|
pcrArray[pcrArraySz++] = 4;
|
|
pcrArray[pcrArraySz++] = 5;
|
|
pcrArray[pcrArraySz++] = 6;
|
|
TPM2_SetupPCRSelArray(&pcr, TPM_ALG_SHA256, pcrArray, pcrArraySz);
|
|
|
|
if (pcr.count != 2 ||
|
|
pcr.pcrSelections[0].hash != TPM_ALG_SHA ||
|
|
pcr.pcrSelections[0].pcrSelect[0] != 0x0E ||
|
|
pcr.pcrSelections[1].hash != TPM_ALG_SHA256 ||
|
|
pcr.pcrSelections[1].pcrSelect[0] != 0x70
|
|
) {
|
|
rc = BAD_FUNC_ARG;
|
|
}
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Test bad case - invalid PCR */
|
|
XMEMSET(&pcr, 0, sizeof(pcr));
|
|
pcrArray[0] = PCR_LAST+1;
|
|
TPM2_SetupPCRSelArray(&pcr, TPM_ALG_SHA256, pcrArray, 1);
|
|
if (pcr.count != 0) {
|
|
rc = BAD_FUNC_ARG;
|
|
}
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Test bad case - too many hash algorithms */
|
|
XMEMSET(&pcr, 0, sizeof(pcr));
|
|
pcrArray[0] = 1;
|
|
TPM2_SetupPCRSelArray(&pcr, TPM_ALG_SHA, pcrArray, 1);
|
|
pcrArray[0] = 2;
|
|
TPM2_SetupPCRSelArray(&pcr, TPM_ALG_SHA256, pcrArray, 1);
|
|
pcrArray[0] = 3;
|
|
TPM2_SetupPCRSelArray(&pcr, TPM_ALG_SHA384, pcrArray, 1);
|
|
pcrArray[0] = 4;
|
|
TPM2_SetupPCRSelArray(&pcr, TPM_ALG_SHA512, pcrArray, 1);
|
|
pcrArray[0] = 5;
|
|
TPM2_SetupPCRSelArray(&pcr, TPM_ALG_SHA3_256, pcrArray, 1);
|
|
pcrArray[0] = 6;
|
|
TPM2_SetupPCRSelArray(&pcr, TPM_ALG_SHA3_384, pcrArray, 1);
|
|
pcrArray[0] = 7;
|
|
TPM2_SetupPCRSelArray(&pcr, TPM_ALG_SHA3_512, pcrArray, 1);
|
|
if (pcr.count != HASH_COUNT) {
|
|
rc = BAD_FUNC_ARG;
|
|
}
|
|
AssertIntEQ(rc, 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s %s\n", "PCR Select Array:",
|
|
rc == 0 ? "Passed" : "Failed");
|
|
}
|
|
|
|
/* Test NULL input handling for policy commands (security fix) */
|
|
static void test_TPM2_Policy_NULL_Args(void)
|
|
{
|
|
int rc;
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
const byte expectedDigest[TPM_SHA256_DIGEST_SIZE] = {
|
|
0x4e, 0x35, 0x3a, 0xbb, 0x5b, 0x73, 0xa0, 0x8b,
|
|
0x9c, 0x1c, 0x53, 0x1e, 0x02, 0x27, 0x9a, 0xa9,
|
|
0x39, 0xb6, 0xb5, 0x61, 0x2d, 0xe3, 0x59, 0x6d,
|
|
0x74, 0xfe, 0xd8, 0x99, 0x9b, 0xef, 0x13, 0xdf
|
|
};
|
|
byte pcrArray[1] = {0};
|
|
byte pcrDigest[sizeof(TPML_PCR_SELECTION) + WC_MAX_DIGEST_SIZE + 1] = {0};
|
|
byte digest[TPM_SHA256_DIGEST_SIZE];
|
|
word32 digestSz = (word32)sizeof(digest);
|
|
#endif
|
|
|
|
/* Test NULL input handling for policy commands */
|
|
rc = TPM2_PolicyPhysicalPresence(NULL);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
rc = TPM2_PolicyAuthValue(NULL);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
rc = TPM2_PolicyPassword(NULL);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
rc = wolfTPM2_PolicyPCRMake(TPM_ALG_SHA256, pcrArray,
|
|
(word32)sizeof(pcrArray), NULL, 0, NULL, &digestSz);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
rc = wolfTPM2_PolicyPCRMake(TPM_ALG_SHA256, pcrArray,
|
|
(word32)sizeof(pcrArray), NULL, 0, digest, NULL);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
rc = wolfTPM2_PolicyPCRMake(TPM_ALG_SHA256, NULL,
|
|
(word32)sizeof(pcrArray), NULL, 0, digest, &digestSz);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
rc = wolfTPM2_PolicyPCRMake(TPM_ALG_SHA256, pcrArray, 0,
|
|
NULL, 0, digest, &digestSz);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
rc = wolfTPM2_PolicyPCRMake(TPM_ALG_NULL, pcrArray,
|
|
(word32)sizeof(pcrArray), NULL, 0, digest, &digestSz);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* A nonzero PCR digest size requires backing digest data. */
|
|
rc = wolfTPM2_PolicyPCRMake(TPM_ALG_SHA256, pcrArray,
|
|
(word32)sizeof(pcrArray), NULL, 1, digest, &digestSz);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* An empty PCR digest is valid. */
|
|
XMEMSET(digest, 0, sizeof(digest));
|
|
digestSz = (word32)sizeof(digest);
|
|
rc = wolfTPM2_PolicyPCRMake(TPM_ALG_SHA256, pcrArray,
|
|
(word32)sizeof(pcrArray), NULL, 0, digest, &digestSz);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(digestSz, (word32)sizeof(expectedDigest));
|
|
AssertIntEQ(XMEMCMP(digest, expectedDigest, sizeof(expectedDigest)), 0);
|
|
|
|
/* Reject a caller-declared output capacity below the hash size. */
|
|
digestSz = (word32)sizeof(digest) - 1;
|
|
rc = wolfTPM2_PolicyPCRMake(TPM_ALG_SHA256, pcrArray,
|
|
(word32)sizeof(pcrArray), NULL, 0, digest, &digestSz);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
|
|
/* Reject a PCR digest that cannot fit in the assembly buffer. */
|
|
digestSz = (word32)sizeof(digest);
|
|
rc = wolfTPM2_PolicyPCRMake(TPM_ALG_SHA256, pcrArray,
|
|
(word32)sizeof(pcrArray), pcrDigest, (word32)sizeof(pcrDigest), digest,
|
|
&digestSz);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
#endif
|
|
|
|
printf("Test TPM2: %-40s Passed\n", "Policy NULL Args:");
|
|
}
|
|
|
|
static void test_wolfTPM2_SetLocality(void)
|
|
{
|
|
int rc = 0;
|
|
WOLFTPM2_DEV dev;
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
|
|
/* Argument validation. The Linux-kernel driver and Windows TBS backends
|
|
* return NOT_COMPILED_IN before validating args, so only assert
|
|
* BAD_FUNC_ARG on the SWTPM and built-in TIS backends. */
|
|
#if !defined(WOLFTPM_LINUX_DEV) && !defined(WOLFTPM_WINAPI)
|
|
rc = wolfTPM2_SetLocality(NULL, 0);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_SetLocality(&dev, -1);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_SetLocality(&dev, 5);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
#endif
|
|
|
|
#if defined(WOLFTPM_SWTPM)
|
|
/* SWTPM/mssim record-only path: records the locality for subsequent
|
|
* commands with no TIS handshake, so it needs no live connection. */
|
|
rc = wolfTPM2_SetLocality(&dev, 2);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(dev.ctx.locality, 2);
|
|
#endif
|
|
(void)rc;
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "SetLocality args/SWTPM:");
|
|
}
|
|
|
|
static void test_wolfTPM2_PolicyAuthValue_AuthOffset(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT)
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_SESSION session;
|
|
const byte testAuth[] = {0x11, 0x22, 0x33, 0x44};
|
|
int authDigestSz;
|
|
int i;
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&session, 0, sizeof(session));
|
|
|
|
(void)wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
|
|
/* Configure session with SHA-256 auth hash */
|
|
session.authHash = TPM_ALG_SHA256;
|
|
authDigestSz = TPM2_GetHashDigestSize(TPM_ALG_SHA256);
|
|
AssertIntEQ(authDigestSz, TPM_SHA256_DIGEST_SIZE);
|
|
|
|
/* Pre-fill the HMAC key region with a sentinel */
|
|
XMEMSET(session.handle.auth.buffer, 0xFF, authDigestSz);
|
|
|
|
/* Call PolicyAuthValue - ignore return (TPM command may fail without
|
|
* a real session handle, but auth buffer placement happens first) */
|
|
(void)wolfTPM2_PolicyAuthValue(&dev, &session, testAuth,
|
|
(int)sizeof(testAuth));
|
|
|
|
/* Verify auth.size = authDigestSz + authSz */
|
|
AssertIntEQ(session.handle.auth.size,
|
|
authDigestSz + (int)sizeof(testAuth));
|
|
|
|
/* Verify HMAC key slot [0..authDigestSz-1] is preserved (still 0xFF) */
|
|
for (i = 0; i < authDigestSz; i++) {
|
|
AssertIntEQ(session.handle.auth.buffer[i], 0xFF);
|
|
}
|
|
|
|
/* Verify auth placed at offset [authDigestSz..authDigestSz+authSz-1] */
|
|
AssertIntEQ(XMEMCMP(&session.handle.auth.buffer[authDigestSz],
|
|
testAuth, sizeof(testAuth)), 0);
|
|
|
|
/* Verify policyAuth flag is set */
|
|
AssertIntEQ(session.handle.policyAuth, 1);
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "PolicyAuthValue Offset:");
|
|
#endif
|
|
}
|
|
|
|
static void test_wolfTPM2_SetAuthHandle_PolicyAuthOffset(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT)
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_HANDLE handle;
|
|
TPM2_AUTH_SESSION sessionBefore;
|
|
int authDigestSz;
|
|
int maxAuthSz;
|
|
int i;
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&handle, 0, sizeof(handle));
|
|
XMEMSET(&sessionBefore, 0, sizeof(sessionBefore));
|
|
|
|
(void)wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
|
|
/* Configure session 0 with SHA-256 auth hash and a non-PW session handle */
|
|
dev.session[0].authHash = TPM_ALG_SHA256;
|
|
dev.session[0].sessionHandle = 0x02000000; /* HMAC session handle */
|
|
authDigestSz = TPM2_GetHashDigestSize(TPM_ALG_SHA256);
|
|
AssertIntEQ(authDigestSz, TPM_SHA256_DIGEST_SIZE);
|
|
|
|
/* Pre-fill the HMAC key region with sentinel */
|
|
XMEMSET(dev.session[0].auth.buffer, 0xFF, authDigestSz);
|
|
|
|
/* Set up handle with policyAuth and auth data */
|
|
handle.policyAuth = 1;
|
|
handle.auth.size = 4;
|
|
handle.auth.buffer[0] = 0x11;
|
|
handle.auth.buffer[1] = 0x22;
|
|
handle.auth.buffer[2] = 0x33;
|
|
handle.auth.buffer[3] = 0x44;
|
|
handle.name.size = 2;
|
|
handle.name.name[0] = 0xAA;
|
|
handle.name.name[1] = 0xBB;
|
|
|
|
/* Test wolfTPM2_SetAuthHandle policyAuth branch */
|
|
rc = wolfTPM2_SetAuthHandle(&dev, 0, &handle);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* Verify auth.size = authDigestSz + authSz */
|
|
AssertIntEQ(dev.session[0].auth.size,
|
|
authDigestSz + (int)handle.auth.size);
|
|
|
|
/* Verify HMAC key slot [0..authDigestSz-1] preserved */
|
|
for (i = 0; i < authDigestSz; i++) {
|
|
AssertIntEQ(dev.session[0].auth.buffer[i], 0xFF);
|
|
}
|
|
|
|
/* Verify auth at offset [authDigestSz..] */
|
|
AssertIntEQ(XMEMCMP(&dev.session[0].auth.buffer[authDigestSz],
|
|
handle.auth.buffer, handle.auth.size), 0);
|
|
|
|
/* Now test wolfTPM2_SetAuthHandleName policyAuth branch */
|
|
XMEMSET(dev.session[0].auth.buffer, 0xEE, authDigestSz);
|
|
dev.session[0].auth.size = 0;
|
|
|
|
rc = wolfTPM2_SetAuthHandleName(&dev, 0, &handle);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* Verify auth.size = authDigestSz + authSz */
|
|
AssertIntEQ(dev.session[0].auth.size,
|
|
authDigestSz + (int)handle.auth.size);
|
|
|
|
/* Verify HMAC key slot [0..authDigestSz-1] preserved */
|
|
for (i = 0; i < authDigestSz; i++) {
|
|
AssertIntEQ(dev.session[0].auth.buffer[i], 0xEE);
|
|
}
|
|
|
|
/* Verify auth at offset [authDigestSz..] */
|
|
AssertIntEQ(XMEMCMP(&dev.session[0].auth.buffer[authDigestSz],
|
|
handle.auth.buffer, handle.auth.size), 0);
|
|
|
|
/* Verify the largest combined digest and authorization fits exactly. */
|
|
maxAuthSz = (int)sizeof(dev.session[0].auth.buffer) - authDigestSz;
|
|
AssertIntGT(maxAuthSz, 0);
|
|
handle.auth.size = (word16)maxAuthSz;
|
|
XMEMSET(handle.auth.buffer, 0x5A, handle.auth.size);
|
|
handle.name.size = (word16)sizeof(handle.name.name);
|
|
XMEMSET(handle.name.name, 0xA5, handle.name.size);
|
|
|
|
rc = wolfTPM2_SetAuthHandle(&dev, 0, &handle);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(dev.session[0].auth.size,
|
|
(int)sizeof(dev.session[0].auth.buffer));
|
|
AssertIntEQ(XMEMCMP(&dev.session[0].auth.buffer[authDigestSz],
|
|
handle.auth.buffer, handle.auth.size), 0);
|
|
AssertIntEQ(dev.session[0].name.size,
|
|
(int)sizeof(dev.session[0].name.name));
|
|
AssertIntEQ(XMEMCMP(dev.session[0].name.name, handle.name.name,
|
|
handle.name.size), 0);
|
|
|
|
/* Reject one byte beyond the remaining auth capacity without mutation. */
|
|
handle.auth.size = (word16)(maxAuthSz + 1);
|
|
XMEMCPY(&sessionBefore, &dev.session[0], sizeof(sessionBefore));
|
|
rc = wolfTPM2_SetAuthHandle(&dev, 0, &handle);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
AssertIntEQ(XMEMCMP(&dev.session[0], &sessionBefore,
|
|
sizeof(sessionBefore)), 0);
|
|
|
|
/* Reject an oversized name without mutating the session. */
|
|
handle.auth.size = 4;
|
|
handle.name.size = (word16)(sizeof(handle.name.name) + 1U);
|
|
XMEMCPY(&sessionBefore, &dev.session[0], sizeof(sessionBefore));
|
|
rc = wolfTPM2_SetAuthHandle(&dev, 0, &handle);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
AssertIntEQ(XMEMCMP(&dev.session[0], &sessionBefore,
|
|
sizeof(sessionBefore)), 0);
|
|
|
|
/* Reject a non-hash session algorithm without mutating the session. */
|
|
handle.name.size = 2;
|
|
dev.session[0].authHash = TPM_ALG_NULL;
|
|
XMEMCPY(&sessionBefore, &dev.session[0], sizeof(sessionBefore));
|
|
rc = wolfTPM2_SetAuthHandle(&dev, 0, &handle);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
AssertIntEQ(XMEMCMP(&dev.session[0], &sessionBefore,
|
|
sizeof(sessionBefore)), 0);
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "SetAuthHandle PolicyAuth:");
|
|
#endif
|
|
}
|
|
|
|
/* Verify wolfTPM2_StartSession enables encrypt/decrypt attributes for
|
|
* salted (tpmKey-only, bind == NULL) sessions when caller selects a
|
|
* symmetric algorithm. Per TPM 2.0 spec, salted sessions have valid
|
|
* shared-secret state for parameter encryption. */
|
|
static void test_wolfTPM2_StartSession_SaltedEncryptAttrs(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT)
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY tpmKey;
|
|
WOLFTPM2_SESSION session;
|
|
TPMA_SESSION expected = TPMA_SESSION_decrypt | TPMA_SESSION_encrypt;
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&tpmKey, 0, sizeof(tpmKey));
|
|
XMEMSET(&session, 0, sizeof(session));
|
|
|
|
/* Initialize so TPM2_GetNonceNoLock and dependent code paths have a
|
|
* valid context. Skip if no TPM is reachable. */
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
if (rc != 0) {
|
|
printf("Test TPM Wrapper:\tStartSession salted enc attrs:\tSkipped\n");
|
|
return;
|
|
}
|
|
|
|
/* tpmKey with a non-NULL handle, no auth */
|
|
tpmKey.handle.hndl = 0x80000000;
|
|
|
|
/* The call will fail later (no real key with that handle) but the
|
|
* SetAuth path that sets sessionAttributes runs first. */
|
|
(void)wolfTPM2_StartSession(&dev, &session, &tpmKey, NULL,
|
|
TPM_SE_HMAC, TPM_ALG_CFB);
|
|
|
|
AssertIntEQ((int)(dev.session[0].sessionAttributes & expected),
|
|
(int)expected);
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
printf("Test TPM Wrapper:\tStartSession salted enc attrs:\tPassed\n");
|
|
#endif
|
|
}
|
|
|
|
static void test_wolfTPM2_StartSession_ex_authHash(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFSSL_SHA512)
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_SESSION session;
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&session, 0, sizeof(session));
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
if (rc != 0) {
|
|
printf("Test TPM Wrapper:\tStartSession_ex SHA512:\tSkipped\n");
|
|
return;
|
|
}
|
|
|
|
/* default (SHA-256) selected via TPM_ALG_NULL */
|
|
rc = wolfTPM2_StartSession_ex(&dev, &session, NULL, NULL,
|
|
TPM_SE_POLICY, TPM_ALG_NULL, TPM_ALG_NULL);
|
|
if (rc == TPM_RC_SUCCESS) {
|
|
AssertIntEQ(session.authHash, WOLFTPM2_WRAP_DIGEST);
|
|
wolfTPM2_UnloadHandle(&dev, &session.handle);
|
|
}
|
|
|
|
/* explicit SHA-512 selection */
|
|
rc = wolfTPM2_StartSession_ex(&dev, &session, NULL, NULL,
|
|
TPM_SE_POLICY, TPM_ALG_NULL, TPM_ALG_SHA512);
|
|
if (rc == TPM_RC_SUCCESS) {
|
|
AssertIntEQ(session.authHash, TPM_ALG_SHA512);
|
|
wolfTPM2_UnloadHandle(&dev, &session.handle);
|
|
printf("Test TPM Wrapper:\tStartSession_ex SHA512:\tPassed\n");
|
|
}
|
|
else {
|
|
printf("Test TPM Wrapper:\tStartSession_ex SHA512:\tSkipped\n");
|
|
}
|
|
|
|
/* a session hash weaker than the default is rejected (no TPM needed) */
|
|
#ifndef NO_SHA
|
|
if (TPM2_GetHashDigestSize(TPM_ALG_SHA1) <
|
|
TPM2_GetHashDigestSize(WOLFTPM2_WRAP_DIGEST)) {
|
|
rc = wolfTPM2_StartSession_ex(&dev, &session, NULL, NULL,
|
|
TPM_SE_POLICY, TPM_ALG_NULL, TPM_ALG_SHA1);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
}
|
|
#endif
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
#endif
|
|
}
|
|
|
|
/* Bind an AES-CFB param-enc session to an EmptyAuth SRK and create a child
|
|
* under it. The pre-fix code left a bound EmptyAuth sessionKey empty, breaking
|
|
* the HMAC; this command fails pre-fix. */
|
|
static void test_wolfTPM2_BoundSession_EmptyAuth_ParamEnc(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && \
|
|
(!defined(NO_RSA) || defined(HAVE_ECC))
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY srk;
|
|
WOLFTPM2_KEY child;
|
|
WOLFTPM2_SESSION session;
|
|
TPMT_PUBLIC publicTemplate;
|
|
#if !defined(NO_RSA)
|
|
TPM_ALG_ID srkAlg = TPM_ALG_RSA;
|
|
#else
|
|
TPM_ALG_ID srkAlg = TPM_ALG_ECC;
|
|
#endif
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&srk, 0, sizeof(srk));
|
|
XMEMSET(&child, 0, sizeof(child));
|
|
XMEMSET(&session, 0, sizeof(session));
|
|
XMEMSET(&publicTemplate, 0, sizeof(publicTemplate));
|
|
|
|
/* Skip cleanly when no TPM is reachable. */
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
if (rc != 0) {
|
|
printf("Test TPM Wrapper:\tBound EmptyAuth param-enc:\tSkipped\n");
|
|
return;
|
|
}
|
|
|
|
/* Storage root key with an EmptyAuth (auth NULL, authSz 0). */
|
|
rc = wolfTPM2_CreateSRK(&dev, &srk, srkAlg, NULL, 0);
|
|
if (rc != 0) {
|
|
/* Environmental (TPM busy / unsupported). Treat as skip. */
|
|
wolfTPM2_Cleanup(&dev);
|
|
printf("Test TPM Wrapper:\tBound EmptyAuth param-enc:\tSkipped\n");
|
|
return;
|
|
}
|
|
|
|
/* Bind an HMAC session to the EmptyAuth SRK with AES-CFB param enc. */
|
|
rc = wolfTPM2_StartSession(&dev, &session, NULL, &srk.handle,
|
|
TPM_SE_HMAC, TPM_ALG_CFB);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* Slot 1: the create/load wrappers own slot 0 (parent auth), so the
|
|
* param-enc session lives in slot 1 to survive into the command. */
|
|
rc = wolfTPM2_SetAuthSession(&dev, 1, &session,
|
|
(TPMA_SESSION_decrypt | TPMA_SESSION_encrypt |
|
|
TPMA_SESSION_continueSession));
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* Create+load a child under the EmptyAuth SRK; fails pre-fix. */
|
|
#if !defined(NO_RSA)
|
|
rc = wolfTPM2_GetKeyTemplate_RSA(&publicTemplate,
|
|
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
|
|
TPMA_OBJECT_sign | TPMA_OBJECT_noDA);
|
|
#else
|
|
rc = wolfTPM2_GetKeyTemplate_ECC(&publicTemplate,
|
|
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
|
|
TPMA_OBJECT_sign | TPMA_OBJECT_noDA,
|
|
TPM_ECC_NIST_P256, TPM_ALG_ECDSA);
|
|
#endif
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
rc = wolfTPM2_CreateAndLoadKey(&dev, &child, &srk.handle,
|
|
&publicTemplate, NULL, 0);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* Clear the session slot, then release handles. */
|
|
wolfTPM2_SetAuthSession(&dev, 1, NULL, 0);
|
|
wolfTPM2_UnloadHandle(&dev, &child.handle);
|
|
wolfTPM2_UnloadHandle(&dev, &session.handle);
|
|
wolfTPM2_UnloadHandle(&dev, &srk.handle);
|
|
wolfTPM2_Cleanup(&dev);
|
|
printf("Test TPM Wrapper:\tBound EmptyAuth param-enc:\tPassed\n");
|
|
#endif
|
|
}
|
|
|
|
/* Run TPM2_CreateLoaded under a salted AES-CFB param-enc session. Pre-fix the
|
|
* missing response outHandleCnt mis-parsed the rpHash offset and the reply was
|
|
* rejected with TPM_RC_HMAC. */
|
|
static void test_wolfTPM2_CreateLoaded_ParamEnc(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && \
|
|
(!defined(NO_RSA) || defined(HAVE_ECC))
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY srk;
|
|
WOLFTPM2_KEYBLOB child;
|
|
WOLFTPM2_SESSION session;
|
|
TPMT_PUBLIC publicTemplate;
|
|
#if !defined(NO_RSA)
|
|
TPM_ALG_ID srkAlg = TPM_ALG_RSA;
|
|
#else
|
|
TPM_ALG_ID srkAlg = TPM_ALG_ECC;
|
|
#endif
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&srk, 0, sizeof(srk));
|
|
XMEMSET(&child, 0, sizeof(child));
|
|
XMEMSET(&session, 0, sizeof(session));
|
|
XMEMSET(&publicTemplate, 0, sizeof(publicTemplate));
|
|
|
|
/* Skip cleanly when no TPM is reachable. */
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
if (rc != 0) {
|
|
printf("Test TPM Wrapper:\tCreateLoaded param-enc:\tSkipped\n");
|
|
return;
|
|
}
|
|
|
|
rc = wolfTPM2_CreateSRK(&dev, &srk, srkAlg, NULL, 0);
|
|
if (rc != 0) {
|
|
/* Environmental (TPM busy / unsupported). Treat as skip. */
|
|
wolfTPM2_Cleanup(&dev);
|
|
printf("Test TPM Wrapper:\tCreateLoaded param-enc:\tSkipped\n");
|
|
return;
|
|
}
|
|
|
|
/* Salted AES-CFB parameter-encryption session in slot 1; slot 0 is left
|
|
* for the parent auth that wolfTPM2_CreateLoadedKey sets internally. */
|
|
rc = wolfTPM2_StartSession(&dev, &session, &srk, NULL,
|
|
TPM_SE_HMAC, TPM_ALG_CFB);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
rc = wolfTPM2_SetAuthSession(&dev, 1, &session,
|
|
(TPMA_SESSION_decrypt | TPMA_SESSION_encrypt |
|
|
TPMA_SESSION_continueSession));
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
#if !defined(NO_RSA)
|
|
rc = wolfTPM2_GetKeyTemplate_RSA(&publicTemplate,
|
|
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
|
|
TPMA_OBJECT_sign | TPMA_OBJECT_noDA);
|
|
#else
|
|
rc = wolfTPM2_GetKeyTemplate_ECC(&publicTemplate,
|
|
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
|
|
TPMA_OBJECT_sign | TPMA_OBJECT_noDA,
|
|
TPM_ECC_NIST_P256, TPM_ALG_ECDSA);
|
|
#endif
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* CreateLoaded under the param-enc session. Fails pre-fix with
|
|
* TPM_RC_HMAC; a TPM that does not implement CreateLoaded is a skip. */
|
|
rc = wolfTPM2_CreateLoadedKey(&dev, &child, &srk.handle,
|
|
&publicTemplate, NULL, 0);
|
|
if (WOLFTPM_IS_COMMAND_UNAVAILABLE(rc)) {
|
|
printf("Test TPM Wrapper:\tCreateLoaded param-enc:\tSkipped\n");
|
|
}
|
|
else {
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
wolfTPM2_UnloadHandle(&dev, &child.handle);
|
|
printf("Test TPM Wrapper:\tCreateLoaded param-enc:\tPassed\n");
|
|
}
|
|
|
|
/* Clear the session slot, then release handles. */
|
|
wolfTPM2_SetAuthSession(&dev, 1, NULL, 0);
|
|
wolfTPM2_UnloadHandle(&dev, &session.handle);
|
|
wolfTPM2_UnloadHandle(&dev, &srk.handle);
|
|
wolfTPM2_Cleanup(&dev);
|
|
#else
|
|
printf("Test TPM Wrapper:\tCreateLoaded param-enc:\tSkipped\n");
|
|
#endif
|
|
}
|
|
|
|
/* Exercise the bound-own-entity branch of TPM2_ParamEncBindKey: the
|
|
* parameter-encryption key for a session that authorizes its own bind entity
|
|
* is sessionKey || authValue. An HMAC session cannot authorize in slot 0, so
|
|
* this uses a bound policy session (the examples/nvram/extend.c pattern): write
|
|
* a POLICYWRITE NV index (policy PolicyPCR(16), auth "cpusecret") under a bound
|
|
* AES-CFB policy session, then read it back. A wrong param-enc key corrupts the
|
|
* stored data even though the write command itself succeeds. */
|
|
static void test_wolfTPM2_BoundOwnEntity_ParamEnc(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(WOLFTPM_WINAPI)
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_SESSION session;
|
|
WOLFTPM2_SESSION trial;
|
|
WOLFTPM2_NV nv;
|
|
WOLFTPM2_HANDLE parent;
|
|
const word32 nvIndex = TPM2_DEMO_NV_TEST_AUTH_INDEX;
|
|
const byte nvAuth[] = "cpusecret";
|
|
const int nvAuthSz = (int)sizeof(nvAuth) - 1;
|
|
word32 nvAttributes;
|
|
byte policyDigest[TPM_SHA256_DIGEST_SIZE];
|
|
word32 policyDigestSz = (word32)sizeof(policyDigest);
|
|
byte pcrArray[1];
|
|
byte buf[8];
|
|
byte readBuf[8];
|
|
word32 readSz;
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&session, 0, sizeof(session));
|
|
XMEMSET(&trial, 0, sizeof(trial));
|
|
XMEMSET(&nv, 0, sizeof(nv));
|
|
XMEMSET(&parent, 0, sizeof(parent));
|
|
XMEMSET(policyDigest, 0, sizeof(policyDigest));
|
|
XMEMSET(buf, 0x11, sizeof(buf));
|
|
XMEMSET(readBuf, 0, sizeof(readBuf));
|
|
pcrArray[0] = 16; /* resettable debug PCR */
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
if (rc != 0) {
|
|
printf("Test TPM Wrapper:\tBound own-entity param-enc:\tSkipped\n");
|
|
return;
|
|
}
|
|
|
|
/* Compute the index authPolicy = PolicyPCR(16) with a trial session
|
|
* (the write helper re-runs PolicyPCR with the same selection). */
|
|
rc = wolfTPM2_StartSession(&dev, &trial, NULL, NULL, TPM_SE_TRIAL,
|
|
TPM_ALG_NULL);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
rc = wolfTPM2_PolicyPCR(&dev, trial.handle.hndl, TPM_ALG_SHA256,
|
|
pcrArray, 1);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
rc = wolfTPM2_GetPolicyDigest(&dev, trial.handle.hndl, policyDigest,
|
|
&policyDigestSz);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
wolfTPM2_UnloadHandle(&dev, &trial.handle);
|
|
|
|
parent.hndl = TPM_RH_OWNER;
|
|
nvAttributes = TPMA_NV_POLICYWRITE | TPMA_NV_AUTHREAD | TPMA_NV_NO_DA;
|
|
rc = wolfTPM2_NVCreateAuthPolicy(&dev, &parent, &nv, nvIndex, nvAttributes,
|
|
(word32)sizeof(buf), (byte*)nvAuth, nvAuthSz,
|
|
policyDigest, (int)policyDigestSz);
|
|
if (rc != 0 && rc != TPM_RC_NV_DEFINED) {
|
|
/* Environmental (NV space / unsupported). Treat as skip. */
|
|
wolfTPM2_Cleanup(&dev);
|
|
printf("Test TPM Wrapper:\tBound own-entity param-enc:\tSkipped\n");
|
|
return;
|
|
}
|
|
/* Load the NV handle's auth and Name for the bind. */
|
|
rc = wolfTPM2_NVOpen(&dev, &nv, nvIndex, (byte*)nvAuth, nvAuthSz);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* Bound AES-CFB policy session (slot 0) authorizing its own bind entity. */
|
|
rc = wolfTPM2_StartSession(&dev, &session, NULL, &nv.handle,
|
|
TPM_SE_POLICY, TPM_ALG_CFB);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
rc = wolfTPM2_SetAuthSession(&dev, 0, &session,
|
|
(TPMA_SESSION_decrypt | TPMA_SESSION_encrypt |
|
|
TPMA_SESSION_continueSession));
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* Write under the bound policy session - the data is parameter-encrypted
|
|
* with the folded key. */
|
|
rc = wolfTPM2_NVWriteAuthPolicy(&dev, &session, TPM_ALG_SHA256, pcrArray, 1,
|
|
&nv, nvIndex, buf, (word32)sizeof(buf), 0);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* Read back with plain password auth (no param enc) and verify the data
|
|
* round-tripped. A doubled or dropped bind authValue would have stored
|
|
* garbage even though the write command itself succeeded. */
|
|
wolfTPM2_SetAuthSession(&dev, 0, NULL, 0);
|
|
wolfTPM2_UnloadHandle(&dev, &session.handle);
|
|
wolfTPM2_SetAuthHandle(&dev, 0, &nv.handle);
|
|
readSz = (word32)sizeof(readBuf);
|
|
rc = wolfTPM2_NVReadAuth(&dev, &nv, nvIndex, readBuf, &readSz, 0);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ((int)readSz, (int)sizeof(buf));
|
|
AssertIntEQ(XMEMCMP(readBuf, buf, sizeof(buf)), 0);
|
|
|
|
wolfTPM2_NVDeleteAuth(&dev, &parent, nvIndex);
|
|
wolfTPM2_Cleanup(&dev);
|
|
printf("Test TPM Wrapper:\tBound own-entity param-enc:\tPassed\n");
|
|
#else
|
|
printf("Test TPM Wrapper:\tBound own-entity param-enc:\tSkipped\n");
|
|
#endif
|
|
}
|
|
|
|
/* Multi-chunk NV write plus rewrite under an HMAC parameter encryption
|
|
* session; a stale cached NV index name would fail the session HMAC. */
|
|
static void test_wolfTPM2_NVWriteChunked(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(WOLFTPM_WINAPI)
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_SESSION session;
|
|
WOLFTPM2_NV nv;
|
|
WOLFTPM2_HANDLE parent;
|
|
const word32 nvIndex = TPM2_DEMO_NV_TEST_CHUNKED_INDEX;
|
|
const byte nvAuth[] = "chunkedwriteauth";
|
|
word32 nvAttributes;
|
|
/* 3 chunks; under the 1664 byte SLB9670 NV index max */
|
|
byte buf[MAX_NV_BUFFER_SIZE*2 + 64];
|
|
byte readBuf[sizeof(buf)];
|
|
word32 readSz;
|
|
word32 i;
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&session, 0, sizeof(session));
|
|
XMEMSET(&nv, 0, sizeof(nv));
|
|
XMEMSET(&parent, 0, sizeof(parent));
|
|
for (i = 0; i < (word32)sizeof(buf); i++) {
|
|
buf[i] = (byte)(i & 0xFF);
|
|
}
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
if (rc != 0) {
|
|
printf("Test TPM Wrapper:\tNV write chunked:\tSkipped\n");
|
|
return;
|
|
}
|
|
|
|
parent.hndl = TPM_RH_OWNER;
|
|
rc = wolfTPM2_GetNvAttributesTemplate(parent.hndl, &nvAttributes);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
rc = wolfTPM2_NVCreateAuth(&dev, &parent, &nv, nvIndex, nvAttributes,
|
|
(word32)sizeof(buf), nvAuth, (int)sizeof(nvAuth)-1);
|
|
if (rc == TPM_RC_NV_DEFINED) {
|
|
wolfTPM2_NVDeleteAuth(&dev, &parent, nvIndex);
|
|
XMEMSET(&nv, 0, sizeof(nv));
|
|
rc = wolfTPM2_NVCreateAuth(&dev, &parent, &nv, nvIndex, nvAttributes,
|
|
(word32)sizeof(buf), nvAuth, (int)sizeof(nvAuth)-1);
|
|
}
|
|
if (rc != 0) {
|
|
/* NV limits vary by device. */
|
|
wolfTPM2_Cleanup(&dev);
|
|
printf("Test TPM Wrapper:\tNV write chunked:\tSkipped\n");
|
|
return;
|
|
}
|
|
|
|
/* The parameter session HMAC binds the NV index name. */
|
|
rc = wolfTPM2_StartSession(&dev, &session, NULL, NULL, TPM_SE_HMAC,
|
|
TPM_ALG_CFB);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
rc = wolfTPM2_SetAuthSession(&dev, 1, &session,
|
|
(TPMA_SESSION_decrypt | TPMA_SESSION_encrypt |
|
|
TPMA_SESSION_continueSession));
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
wolfTPM2_SetAuthHandle(&dev, 0, &nv.handle);
|
|
|
|
/* First write sets TPMA_NV_WRITTEN. */
|
|
rc = wolfTPM2_NVWriteAuth(&dev, &nv, nvIndex, buf, (word32)sizeof(buf), 0);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
readSz = (word32)sizeof(readBuf);
|
|
rc = wolfTPM2_NVReadAuth(&dev, &nv, nvIndex, readBuf, &readSz, 0);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ((int)readSz, (int)sizeof(buf));
|
|
AssertIntEQ(XMEMCMP(readBuf, buf, sizeof(buf)), 0);
|
|
|
|
/* Rewrite uses the stable name. */
|
|
for (i = 0; i < (word32)sizeof(buf); i++) {
|
|
buf[i] = (byte)(~i & 0xFF);
|
|
}
|
|
rc = wolfTPM2_NVWriteAuth(&dev, &nv, nvIndex, buf, (word32)sizeof(buf), 0);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
XMEMSET(readBuf, 0, sizeof(readBuf));
|
|
readSz = (word32)sizeof(readBuf);
|
|
rc = wolfTPM2_NVReadAuth(&dev, &nv, nvIndex, readBuf, &readSz, 0);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ((int)readSz, (int)sizeof(buf));
|
|
AssertIntEQ(XMEMCMP(readBuf, buf, sizeof(buf)), 0);
|
|
|
|
wolfTPM2_SetAuthSession(&dev, 1, NULL, 0);
|
|
wolfTPM2_UnloadHandle(&dev, &session.handle);
|
|
wolfTPM2_SetAuthHandle(&dev, 0, &nv.handle);
|
|
wolfTPM2_NVDeleteAuth(&dev, &parent, nvIndex);
|
|
wolfTPM2_Cleanup(&dev);
|
|
printf("Test TPM Wrapper:\tNV write chunked:\tPassed\n");
|
|
#else
|
|
printf("Test TPM Wrapper:\tNV write chunked:\tSkipped\n");
|
|
#endif
|
|
}
|
|
|
|
static void test_wolfTPM2_PolicyHash(void)
|
|
{
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
int rc;
|
|
byte digest[TPM_SHA256_DIGEST_SIZE];
|
|
byte digest0[TPM_SHA256_DIGEST_SIZE];
|
|
byte digestFirst[TPM_SHA256_DIGEST_SIZE];
|
|
word32 digestSz;
|
|
const byte input[] = {0x01, 0x02, 0x03, 0x04};
|
|
|
|
/* Test 1: cc=0 (no command code, used by PolicyRefMake) */
|
|
XMEMSET(digest, 0xAA, sizeof(digest));
|
|
digestSz = TPM_SHA256_DIGEST_SIZE;
|
|
rc = wolfTPM2_PolicyHash(TPM_ALG_SHA256, digest, &digestSz,
|
|
0, input, sizeof(input));
|
|
AssertIntEQ(rc, 0);
|
|
XMEMCPY(digest0, digest, digestSz);
|
|
|
|
/* Test 2: cc=TPM_CC_FIRST (0x11F boundary) - must differ from cc=0 */
|
|
XMEMSET(digest, 0xAA, sizeof(digest));
|
|
digestSz = TPM_SHA256_DIGEST_SIZE;
|
|
rc = wolfTPM2_PolicyHash(TPM_ALG_SHA256, digest, &digestSz,
|
|
TPM_CC_FIRST, input, sizeof(input));
|
|
AssertIntEQ(rc, 0);
|
|
XMEMCPY(digestFirst, digest, digestSz);
|
|
|
|
/* cc=0 and cc=TPM_CC_FIRST must produce different digests */
|
|
AssertIntNE(XMEMCMP(digest0, digestFirst, digestSz), 0);
|
|
|
|
/* Test 3: cc=TPM_CC_PolicyPCR (above boundary) - must differ from both */
|
|
XMEMSET(digest, 0xAA, sizeof(digest));
|
|
digestSz = TPM_SHA256_DIGEST_SIZE;
|
|
rc = wolfTPM2_PolicyHash(TPM_ALG_SHA256, digest, &digestSz,
|
|
TPM_CC_PolicyPCR, input, sizeof(input));
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntNE(XMEMCMP(digest0, digest, digestSz), 0);
|
|
AssertIntNE(XMEMCMP(digestFirst, digest, digestSz), 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "PolicyHash:");
|
|
#else
|
|
printf("Test TPM Wrapper: %-40s Skipped\n", "PolicyHash:");
|
|
#endif
|
|
}
|
|
|
|
static void test_wolfTPM2_SensitiveToPrivate(void)
|
|
{
|
|
#ifdef WOLFTPM2_PRIVATE_IMPORT
|
|
int rc;
|
|
TPM2B_SENSITIVE sens;
|
|
TPM2B_PRIVATE priv;
|
|
TPM2B_NAME name;
|
|
TPM2B_DATA symSeed;
|
|
TPMT_SYM_DEF_OBJECT sym;
|
|
const byte expected[] = {
|
|
0x00, 0x20, 0x2b, 0x59, 0xc0, 0x69, 0xf6, 0x63,
|
|
0x7c, 0x2a, 0xe0, 0x62, 0xcf, 0x42, 0x37, 0x8b,
|
|
0x79, 0x5d, 0xb6, 0x61, 0x4f, 0x9f, 0x93, 0x38,
|
|
0x82, 0x06, 0x2e, 0x28, 0xbf, 0xd3, 0x5c, 0x82,
|
|
0x1c, 0x03, 0xb5, 0x90, 0x49, 0x7a, 0x93, 0x46,
|
|
0x31, 0x51, 0xe2, 0xdd, 0x4f, 0x0a, 0x22, 0x9b,
|
|
0x2e, 0xd7, 0x5d, 0xc6, 0xe3, 0x97, 0xf4, 0x75,
|
|
0xcf, 0xfd, 0xa9, 0xe9, 0xd3, 0xa4, 0x5f, 0x95,
|
|
0xa0, 0x70, 0x2f, 0x71, 0x6c, 0xb8, 0x90, 0x39,
|
|
0x32, 0x54, 0x91, 0x87, 0x34, 0x9b, 0xac, 0xef
|
|
};
|
|
|
|
/* Fixed test inputs */
|
|
XMEMSET(&sens, 0, sizeof(sens));
|
|
XMEMSET(&priv, 0, sizeof(priv));
|
|
XMEMSET(&name, 0, sizeof(name));
|
|
XMEMSET(&symSeed, 0, sizeof(symSeed));
|
|
XMEMSET(&sym, 0, sizeof(sym));
|
|
|
|
/* Set up a minimal sensitive area */
|
|
sens.sensitiveArea.sensitiveType = TPM_ALG_RSA;
|
|
sens.sensitiveArea.authValue.size = 4;
|
|
XMEMSET(sens.sensitiveArea.authValue.buffer, 0xAA, 4);
|
|
sens.sensitiveArea.seedValue.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(sens.sensitiveArea.seedValue.buffer, 0xBB,
|
|
TPM_SHA256_DIGEST_SIZE);
|
|
|
|
/* Set up a name (hash alg + digest) */
|
|
name.size = 2 + TPM_SHA256_DIGEST_SIZE;
|
|
/* name[0..1] = TPM_ALG_SHA256 big-endian */
|
|
name.name[0] = 0x00;
|
|
name.name[1] = 0x0B; /* TPM_ALG_SHA256 */
|
|
XMEMSET(&name.name[2], 0xCC, TPM_SHA256_DIGEST_SIZE);
|
|
|
|
/* Set up symmetric algorithm (AES-128-CFB) */
|
|
sym.algorithm = TPM_ALG_AES;
|
|
sym.keyBits.sym = 128;
|
|
sym.mode.sym = TPM_ALG_CFB;
|
|
|
|
/* Set up a symmetric seed (triggers outer wrap / KDFa) */
|
|
symSeed.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(symSeed.buffer, 0xDD, TPM_SHA256_DIGEST_SIZE);
|
|
|
|
/* Expected output - pins KDFa "STORAGE" and "INTEGRITY" labels.
|
|
* Bytes 0-1: integrity size (0x0020 = 32),
|
|
* Bytes 2-33: HMAC integrity (via "INTEGRITY" label KDFa),
|
|
* Bytes 34-79: AES-CFB encrypted sensitive (via "STORAGE" label KDFa) */
|
|
rc = wolfTPM2_SensitiveToPrivate(&sens, &priv,
|
|
TPM_ALG_SHA256, &name, NULL, &sym, &symSeed);
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntEQ(priv.size, (int)sizeof(expected));
|
|
AssertIntEQ(XMEMCMP(priv.buffer, expected, sizeof(expected)), 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "SensitiveToPrivate:");
|
|
#else
|
|
printf("Test TPM Wrapper: %-40s Skipped\n", "SensitiveToPrivate:");
|
|
#endif
|
|
}
|
|
|
|
static void test_TPM2_KDFa_SessionLabels(void)
|
|
{
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
int rc;
|
|
#define TEST_KDFA_LABEL_KEYSZ TPM_SHA256_DIGEST_SIZE
|
|
TPM2B_DATA keyIn = {
|
|
.size = 16,
|
|
.buffer = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
|
0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10}
|
|
};
|
|
TPM2B_NONCE nonceTPM = {
|
|
.size = 16,
|
|
.buffer = {0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
|
|
0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0}
|
|
};
|
|
TPM2B_NONCE nonceCaller = {
|
|
.size = 16,
|
|
.buffer = {0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8,
|
|
0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0}
|
|
};
|
|
const byte expATH[] = {
|
|
0x0d, 0x17, 0x5f, 0xf7, 0xac, 0xf9, 0x41, 0x9a,
|
|
0x73, 0x75, 0x7c, 0xa6, 0x42, 0x82, 0x49, 0x61,
|
|
0xa2, 0xc9, 0x72, 0xd9, 0x13, 0xdc, 0xbf, 0x72,
|
|
0x06, 0xe6, 0x73, 0xe7, 0x21, 0x5f, 0x99, 0x6a
|
|
};
|
|
const byte expSECRET[] = {
|
|
0x1a, 0xc4, 0xc1, 0x34, 0x78, 0x87, 0x67, 0x5e,
|
|
0x91, 0xd1, 0xa2, 0xcd, 0xcb, 0xac, 0xdb, 0x62,
|
|
0xed, 0x4e, 0xfe, 0x44, 0xed, 0x52, 0x34, 0x3b,
|
|
0xf1, 0x87, 0xfb, 0x8b, 0xa9, 0xec, 0x43, 0x59
|
|
};
|
|
const byte expDUPLICATE[] = {
|
|
0xa3, 0xe5, 0x57, 0xc6, 0x49, 0x4c, 0xe5, 0x4f,
|
|
0x45, 0xae, 0xf7, 0x19, 0x4d, 0x9e, 0x21, 0xa2,
|
|
0x91, 0xeb, 0x05, 0x2d, 0x43, 0x06, 0x9f, 0xfb,
|
|
0x69, 0x67, 0x1f, 0x99, 0x00, 0xb0, 0xcc, 0x39
|
|
};
|
|
byte key[TEST_KDFA_LABEL_KEYSZ];
|
|
|
|
/* Test "ATH" label (session key derivation, TPM 2.0 Part 1 s19.6.8) */
|
|
rc = TPM2_KDFa(TPM_ALG_SHA256, &keyIn, "ATH", &nonceTPM, &nonceCaller,
|
|
key, TEST_KDFA_LABEL_KEYSZ);
|
|
AssertIntEQ(TEST_KDFA_LABEL_KEYSZ, rc);
|
|
AssertIntEQ(XMEMCMP(key, expATH, sizeof(expATH)), 0);
|
|
|
|
/* Test "SECRET" label (salt encryption, TPM 2.0 Part 1 s19.6.8) */
|
|
rc = TPM2_KDFa(TPM_ALG_SHA256, &keyIn, "SECRET", &nonceTPM, &nonceCaller,
|
|
key, TEST_KDFA_LABEL_KEYSZ);
|
|
AssertIntEQ(TEST_KDFA_LABEL_KEYSZ, rc);
|
|
AssertIntEQ(XMEMCMP(key, expSECRET, sizeof(expSECRET)), 0);
|
|
|
|
/* Test "DUPLICATE" label (key import, TPM 2.0 Part 1 s23.3) */
|
|
rc = TPM2_KDFa(TPM_ALG_SHA256, &keyIn, "DUPLICATE", &nonceTPM, &nonceCaller,
|
|
key, TEST_KDFA_LABEL_KEYSZ);
|
|
AssertIntEQ(TEST_KDFA_LABEL_KEYSZ, rc);
|
|
AssertIntEQ(XMEMCMP(key, expDUPLICATE, sizeof(expDUPLICATE)), 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "KDFa Session Labels:");
|
|
#else
|
|
printf("Test TPM Wrapper: %-40s Skipped\n", "KDFa Session Labels:");
|
|
#endif
|
|
}
|
|
|
|
static void test_wolfTPM2_EncryptSecret(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY tpmKey;
|
|
TPM2B_DATA data;
|
|
TPM2B_ENCRYPTED_SECRET secret;
|
|
#if defined(WOLFTPM_MLKEM) && !defined(WOLFTPM2_NO_WOLFCRYPT) && \
|
|
(defined(WOLFSSL_HAVE_MLKEM) || defined(WOLFSSL_KYBER512) || \
|
|
defined(WOLFSSL_KYBER768) || defined(WOLFSSL_KYBER1024))
|
|
WOLFTPM2_KEY mlkemKey;
|
|
TPMT_PUBLIC mlkemPub;
|
|
#endif
|
|
|
|
XMEMSET(&tpmKey, 0, sizeof(tpmKey));
|
|
XMEMSET(&data, 0, sizeof(data));
|
|
XMEMSET(&secret, 0, sizeof(secret));
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Test NULL tpmKey returns success (unsalted session) */
|
|
rc = wolfTPM2_EncryptSecret(&dev, NULL, &data, &secret, "SECRET");
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* Test NULL dev returns BAD_FUNC_ARG */
|
|
rc = wolfTPM2_EncryptSecret(NULL, &tpmKey, &data, &secret, "SECRET");
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* Test NULL data returns BAD_FUNC_ARG */
|
|
rc = wolfTPM2_EncryptSecret(&dev, &tpmKey, NULL, &secret, "SECRET");
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* Test NULL secret returns BAD_FUNC_ARG */
|
|
rc = wolfTPM2_EncryptSecret(&dev, &tpmKey, &data, NULL, "SECRET");
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
#if defined(WOLFTPM_MLKEM) && !defined(WOLFTPM2_NO_WOLFCRYPT) && \
|
|
(defined(WOLFSSL_HAVE_MLKEM) || defined(WOLFSSL_KYBER512) || \
|
|
defined(WOLFSSL_KYBER768) || defined(WOLFSSL_KYBER1024))
|
|
/* MLKEM path (v1.85 Part 1 Sec.24): caller encapsulates under the TPM's
|
|
* ML-KEM public key; the shared secret (32 bytes) becomes the session
|
|
* salt, the ciphertext (1088 bytes for MLKEM-768) goes on the wire. */
|
|
XMEMSET(&mlkemKey, 0, sizeof(mlkemKey));
|
|
XMEMSET(&mlkemPub, 0, sizeof(mlkemPub));
|
|
XMEMSET(&data, 0, sizeof(data));
|
|
XMEMSET(&secret, 0, sizeof(secret));
|
|
|
|
rc = wolfTPM2_GetKeyTemplate_MLKEM(&mlkemPub,
|
|
TPMA_OBJECT_decrypt | TPMA_OBJECT_fixedTPM |
|
|
TPMA_OBJECT_fixedParent | TPMA_OBJECT_sensitiveDataOrigin |
|
|
TPMA_OBJECT_userWithAuth, TPM_MLKEM_768);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
rc = wolfTPM2_CreatePrimaryKey(&dev, &mlkemKey, TPM_RH_OWNER,
|
|
&mlkemPub, NULL, 0);
|
|
if (rc == TPM_RC_VALUE || rc == TPM_RC_SCHEME ||
|
|
rc == TPM_RC_COMMAND_CODE || rc == (int)(RC_VER1 + 0x043)) {
|
|
printf("Test TPM Wrapper: %-40s Skipped (not supported)\n",
|
|
"EncryptSecret MLKEM:");
|
|
}
|
|
else {
|
|
AssertIntEQ(rc, 0);
|
|
|
|
rc = wolfTPM2_EncryptSecret(&dev, &mlkemKey, &data, &secret,
|
|
"SECRET");
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntEQ(data.size, 32); /* MLKEM shared secret */
|
|
AssertIntEQ(secret.size, 1088); /* MLKEM-768 ciphertext */
|
|
printf("Test TPM Wrapper: %-40s Passed\n",
|
|
"EncryptSecret MLKEM:");
|
|
|
|
wolfTPM2_UnloadHandle(&dev, &mlkemKey.handle);
|
|
}
|
|
#endif
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper: %-40s %s\n", "EncryptSecret:",
|
|
rc == 0 || rc == BAD_FUNC_ARG ? "Passed" : "Failed");
|
|
}
|
|
|
|
static void test_wolfTPM2_Cleanup(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
|
|
/* Test arguments */
|
|
rc = wolfTPM2_Cleanup(NULL);
|
|
AssertIntNE(rc, 0);
|
|
|
|
/* Test success */
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
rc = wolfTPM2_Cleanup(&dev);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
#ifndef WOLFTPM2_NO_HEAP
|
|
/* Test Free functions handle NULL safely (security fix) */
|
|
rc = wolfTPM2_FreeKeyBlob(NULL);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
rc = wolfTPM2_FreeKey(NULL);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
rc = wolfTPM2_FreeSession(NULL);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
rc = wolfTPM2_FreePublicTemplate(NULL);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
#endif
|
|
|
|
printf("Test TPM Wrapper: %-40s %s\n", "Cleanup:",
|
|
rc == 0 ? "Passed" : "Failed");
|
|
}
|
|
|
|
static void test_TPM2_KDFa(void)
|
|
{
|
|
int rc;
|
|
#define TEST_KDFA_KEYSZ 20
|
|
TPM2B_DATA keyIn = {
|
|
.size = TEST_KDFA_KEYSZ,
|
|
.buffer = {0x27, 0x1F, 0xA0, 0x8B, 0xBD, 0xC5, 0x06, 0x0E, 0xC3, 0xDF,
|
|
0xA9, 0x28, 0xFF, 0x9B, 0x73, 0x12, 0x3A, 0x12, 0xDA, 0x0C}
|
|
};
|
|
const char label[] = "KDFSELFTESTLABEL";
|
|
TPM2B_NONCE contextU = {
|
|
.size = 8,
|
|
.buffer = {0xCE, 0x24, 0x4F, 0x39, 0x5D, 0xCA, 0x73, 0x91}
|
|
};
|
|
TPM2B_NONCE contextV = {
|
|
.size = 8,
|
|
.buffer = {0xDA, 0x50, 0x40, 0x31, 0xDD, 0xF1, 0x2E, 0x83}
|
|
};
|
|
byte key[TEST_KDFA_KEYSZ];
|
|
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
const byte keyExp[TEST_KDFA_KEYSZ] = {
|
|
0xbb, 0x02, 0x59, 0xe1, 0xc8, 0xba, 0x60, 0x7e, 0x6a, 0x2c,
|
|
0xd7, 0x04, 0xb6, 0x9a, 0x90, 0x2e, 0x9a, 0xde, 0x84, 0xc4};
|
|
#endif
|
|
|
|
rc = TPM2_KDFa_ex(TPM_ALG_SHA256, keyIn.buffer, keyIn.size, label,
|
|
contextU.buffer, contextU.size, contextV.buffer, contextV.size,
|
|
key, keyIn.size);
|
|
#ifdef WOLFTPM2_NO_WOLFCRYPT
|
|
AssertIntEQ(NOT_COMPILED_IN, rc);
|
|
#else
|
|
AssertIntEQ(sizeof(keyExp), rc);
|
|
AssertIntEQ(XMEMCMP(key, keyExp, sizeof(keyExp)), 0);
|
|
#endif
|
|
|
|
printf("Test TPM Wrapper: %-40s %s\n", "KDFa:",
|
|
rc >= 0 ? "Passed" : "Failed");
|
|
}
|
|
|
|
static void test_TPM2_KDFe(void)
|
|
{
|
|
int rc;
|
|
enum { TEST_KDFE_KEYSZ = 32 };
|
|
/* Use a simple known Z, label, and party info */
|
|
const byte Z[TEST_KDFE_KEYSZ] = {
|
|
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};
|
|
const char label[] = "IDENTITY";
|
|
const byte partyU[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11};
|
|
const byte partyV[8] = {0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99};
|
|
byte key[TEST_KDFE_KEYSZ];
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
byte key2[TEST_KDFE_KEYSZ];
|
|
/* KAT: SHA256(counter(1) || Z || "IDENTITY\0" || partyU || partyV) */
|
|
const byte keyExp[TEST_KDFE_KEYSZ] = {
|
|
0x36, 0xa3, 0xbc, 0x51, 0x5f, 0xe0, 0x12, 0xb8,
|
|
0x1b, 0xac, 0x81, 0xd6, 0x21, 0x83, 0x74, 0x75,
|
|
0xb9, 0x17, 0xf8, 0x9b, 0xcd, 0x94, 0xd4, 0xa3,
|
|
0xa3, 0x7f, 0x31, 0x49, 0xaa, 0xe2, 0x9b, 0xa1};
|
|
#endif
|
|
|
|
rc = TPM2_KDFe_ex(TPM_ALG_SHA256, Z, sizeof(Z), label,
|
|
partyU, sizeof(partyU), partyV, sizeof(partyV),
|
|
key, sizeof(key));
|
|
#ifdef WOLFTPM2_NO_WOLFCRYPT
|
|
AssertIntEQ(NOT_COMPILED_IN, rc);
|
|
#else
|
|
AssertIntEQ((int)sizeof(key), rc);
|
|
/* Pin the exact output so counter, label and party order are verified */
|
|
AssertIntEQ(0, XMEMCMP(key, keyExp, sizeof(keyExp)));
|
|
/* Verify deterministic: same inputs produce same output */
|
|
rc = TPM2_KDFe_ex(TPM_ALG_SHA256, Z, sizeof(Z), label,
|
|
partyU, sizeof(partyU), partyV, sizeof(partyV),
|
|
key2, sizeof(key2));
|
|
AssertIntEQ((int)sizeof(key2), rc);
|
|
AssertIntEQ(0, XMEMCMP(key, key2, sizeof(key)));
|
|
#endif
|
|
|
|
printf("Test TPM Wrapper: %-40s %s\n", "KDFe:",
|
|
rc >= 0 ? "Passed" : "Failed");
|
|
}
|
|
|
|
static void test_TPM2_HmacCompute(void)
|
|
{
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
int rc;
|
|
/* RFC 4231 Test Case 2: HMAC-SHA256 with "Jefe" key and "what do ya want
|
|
* for nothing?" data */
|
|
const byte hmacKey[] = "Jefe";
|
|
const byte hmacData[] = "what do ya want for nothing?";
|
|
const byte hmacExp[] = {
|
|
0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e,
|
|
0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7,
|
|
0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83,
|
|
0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43};
|
|
byte digest[TPM_MAX_DIGEST_SIZE];
|
|
word32 digestSz = sizeof(digest);
|
|
|
|
rc = TPM2_HmacCompute(TPM_ALG_SHA256,
|
|
hmacKey, 4, /* "Jefe" without null terminator */
|
|
hmacData, 28, /* "what do ya want for nothing?" without null */
|
|
NULL, 0,
|
|
digest, &digestSz);
|
|
AssertIntEQ(0, rc);
|
|
AssertIntEQ(32, (int)digestSz);
|
|
AssertIntEQ(0, XMEMCMP(digest, hmacExp, sizeof(hmacExp)));
|
|
|
|
/* Test HmacVerify with correct expected value */
|
|
rc = TPM2_HmacVerify(TPM_ALG_SHA256,
|
|
hmacKey, 4, hmacData, 28, NULL, 0,
|
|
hmacExp, sizeof(hmacExp));
|
|
AssertIntEQ(0, rc);
|
|
|
|
/* Test HmacVerify with wrong expected value */
|
|
digest[0] ^= 0xFF;
|
|
rc = TPM2_HmacVerify(TPM_ALG_SHA256,
|
|
hmacKey, 4, hmacData, 28, NULL, 0,
|
|
digest, digestSz);
|
|
AssertIntEQ(TPM_RC_INTEGRITY, rc);
|
|
|
|
/* A truncated (short) expected HMAC must be rejected on the length check */
|
|
rc = TPM2_HmacVerify(TPM_ALG_SHA256,
|
|
hmacKey, 4, hmacData, 28, NULL, 0,
|
|
hmacExp, sizeof(hmacExp) - 1);
|
|
AssertIntEQ(TPM_RC_INTEGRITY, rc);
|
|
|
|
/* An output buffer smaller than the digest must be rejected */
|
|
digestSz = 31;
|
|
rc = TPM2_HmacCompute(TPM_ALG_SHA256,
|
|
hmacKey, 4, hmacData, 28, NULL, 0,
|
|
digest, &digestSz);
|
|
AssertIntEQ(BUFFER_E, rc);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "HmacCompute:");
|
|
#else
|
|
printf("Test TPM Wrapper: %-40s Skipped\n", "HmacCompute:");
|
|
#endif
|
|
}
|
|
|
|
static void test_TPM2_HashCompute(void)
|
|
{
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
int rc;
|
|
/* SHA-256 of empty string */
|
|
const byte hashExp[] = {
|
|
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
|
|
0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
|
|
0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
|
|
0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55};
|
|
byte digest[TPM_MAX_DIGEST_SIZE];
|
|
word32 digestSz = sizeof(digest);
|
|
|
|
rc = TPM2_HashCompute(TPM_ALG_SHA256,
|
|
(const byte*)"", 0,
|
|
digest, &digestSz);
|
|
AssertIntEQ(0, rc);
|
|
AssertIntEQ(32, (int)digestSz);
|
|
AssertIntEQ(XMEMCMP(digest, hashExp, sizeof(hashExp)), 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "HashCompute:");
|
|
#else
|
|
printf("Test TPM Wrapper: %-40s Skipped\n", "HashCompute:");
|
|
#endif
|
|
}
|
|
|
|
static void test_TPM2_ConstantCompare(void)
|
|
{
|
|
const byte a[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
|
|
const byte b[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
|
|
const byte c[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09};
|
|
const byte d[] = {0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8};
|
|
|
|
/* Identical buffers must return 0 */
|
|
AssertIntEQ(0, TPM2_ConstantCompare(a, b, sizeof(a)));
|
|
|
|
/* Single-byte difference must return non-zero */
|
|
AssertIntNE(0, TPM2_ConstantCompare(a, c, sizeof(a)));
|
|
|
|
/* Completely different buffers must return non-zero */
|
|
AssertIntNE(0, TPM2_ConstantCompare(a, d, sizeof(a)));
|
|
|
|
/* Zero length must return 0 (no bytes to compare) */
|
|
AssertIntEQ(0, TPM2_ConstantCompare(a, d, 0));
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "ConstantCompare:");
|
|
}
|
|
|
|
/* WOLFTPM_IS_COMMAND_UNAVAILABLE must match TPM_RC_COMMAND_CODE even with
|
|
* vendor bits set (NS350 returns 0x000b0143), without aliasing other codes. */
|
|
static void test_WOLFTPM_IS_COMMAND_UNAVAILABLE(void)
|
|
{
|
|
AssertIntNE(0, WOLFTPM_IS_COMMAND_UNAVAILABLE((int)TPM_RC_COMMAND_CODE));
|
|
AssertIntNE(0, WOLFTPM_IS_COMMAND_UNAVAILABLE(0x000b0143)); /* vendor bits */
|
|
AssertIntEQ(0, WOLFTPM_IS_COMMAND_UNAVAILABLE((int)TPM_RC_COMMAND_SIZE));
|
|
AssertIntEQ(0, WOLFTPM_IS_COMMAND_UNAVAILABLE((int)TPM_RC_SUCCESS));
|
|
/* Negative wolfCrypt errors must not alias onto the command code. */
|
|
AssertIntEQ(0, WOLFTPM_IS_COMMAND_UNAVAILABLE(-189)); /* ASN_CRL_CONFIRM_E */
|
|
AssertIntEQ(0, WOLFTPM_IS_COMMAND_UNAVAILABLE(-1)); /* generic negative */
|
|
/* Positive layer/vendor bits must not alias either. */
|
|
AssertIntEQ(0, WOLFTPM_IS_COMMAND_UNAVAILABLE(0x00000343)); /* layer bits */
|
|
AssertIntEQ(0, WOLFTPM_IS_COMMAND_UNAVAILABLE(0x000b0142)); /* vendor, non-cc */
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "IsCommandUnavailable:");
|
|
}
|
|
|
|
static void test_TPM2_AesCfbRoundtrip(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_AES) && \
|
|
defined(WOLFSSL_AES_CFB)
|
|
int rc;
|
|
int i;
|
|
const int keySizes[3] = {16, 24, 32};
|
|
byte key[32];
|
|
byte iv[16];
|
|
byte pt[64];
|
|
byte ct[64];
|
|
|
|
XMEMSET(key, 0xA5, sizeof(key));
|
|
XMEMSET(iv, 0x5A, sizeof(iv));
|
|
for (i = 0; i < (int)sizeof(pt); i++) {
|
|
pt[i] = (byte)i;
|
|
}
|
|
|
|
/* Encrypt -> decrypt round trip for each supported key size */
|
|
for (i = 0; i < 3; i++) {
|
|
XMEMCPY(ct, pt, sizeof(pt));
|
|
rc = TPM2_AesCfbEncrypt(key, keySizes[i], iv, ct, sizeof(ct));
|
|
AssertIntEQ(0, rc);
|
|
AssertIntNE(0, XMEMCMP(ct, pt, sizeof(pt)));
|
|
rc = TPM2_AesCfbDecrypt(key, keySizes[i], iv, ct, sizeof(ct));
|
|
AssertIntEQ(0, rc);
|
|
AssertIntEQ(0, XMEMCMP(ct, pt, sizeof(pt)));
|
|
}
|
|
|
|
/* NULL-IV path must be accepted (zero-fill default) */
|
|
XMEMCPY(ct, pt, sizeof(pt));
|
|
rc = TPM2_AesCfbEncrypt(key, 16, NULL, ct, sizeof(ct));
|
|
AssertIntEQ(0, rc);
|
|
rc = TPM2_AesCfbDecrypt(key, 16, NULL, ct, sizeof(ct));
|
|
AssertIntEQ(0, rc);
|
|
AssertIntEQ(0, XMEMCMP(ct, pt, sizeof(pt)));
|
|
|
|
/* Reject invalid key size */
|
|
rc = TPM2_AesCfbEncrypt(key, 15, iv, ct, sizeof(ct));
|
|
AssertIntNE(0, rc);
|
|
rc = TPM2_AesCfbDecrypt(key, 15, iv, ct, sizeof(ct));
|
|
AssertIntNE(0, rc);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "AesCfbRoundtrip:");
|
|
#else
|
|
printf("Test TPM Wrapper: %-40s Skipped\n", "AesCfbRoundtrip:");
|
|
#endif
|
|
}
|
|
|
|
/* Cover KDFa multi-iteration loop (keySz > hash digest size) and
|
|
* SHA-384 / SHA-512 code paths, with non-empty context inputs. */
|
|
static void test_TPM2_KDFa_MultiHash(void)
|
|
{
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
int rc;
|
|
size_t iAlg;
|
|
size_t iSz;
|
|
const UINT32 sizes[] = {1, 31, 32, 33, 64, 96};
|
|
const byte keyIn[32] = {
|
|
0x00, 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};
|
|
const char label[] = "MULTIHASHLABEL";
|
|
const byte ctxU[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, 0x11};
|
|
const byte ctxV[8] = {0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99};
|
|
static const TPM_ALG_ID algs[] = {
|
|
TPM_ALG_SHA256,
|
|
#ifdef WOLFSSL_SHA384
|
|
TPM_ALG_SHA384,
|
|
#endif
|
|
#ifdef WOLFSSL_SHA512
|
|
TPM_ALG_SHA512,
|
|
#endif
|
|
};
|
|
byte key1[96];
|
|
byte key2[96];
|
|
|
|
for (iAlg = 0; iAlg < sizeof(algs)/sizeof(algs[0]); iAlg++) {
|
|
for (iSz = 0; iSz < sizeof(sizes)/sizeof(sizes[0]); iSz++) {
|
|
UINT32 sz = sizes[iSz];
|
|
rc = TPM2_KDFa_ex(algs[iAlg], keyIn, sizeof(keyIn), label,
|
|
ctxU, sizeof(ctxU), ctxV, sizeof(ctxV), key1, sz);
|
|
AssertIntEQ((int)sz, rc);
|
|
rc = TPM2_KDFa_ex(algs[iAlg], keyIn, sizeof(keyIn), label,
|
|
ctxU, sizeof(ctxU), ctxV, sizeof(ctxV), key2, sz);
|
|
AssertIntEQ((int)sz, rc);
|
|
AssertIntEQ(0, XMEMCMP(key1, key2, sz));
|
|
}
|
|
}
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "KDFa multi-hash:");
|
|
#else
|
|
printf("Test TPM Wrapper: %-40s Skipped\n", "KDFa multi-hash:");
|
|
#endif
|
|
}
|
|
|
|
static void test_TPM2_KDFe_MultiHash(void)
|
|
{
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
int rc;
|
|
size_t iAlg;
|
|
size_t iSz;
|
|
const UINT32 sizes[] = {1, 31, 32, 33, 64, 96};
|
|
const byte Z[32] = {
|
|
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};
|
|
const char label[] = "IDENTITY";
|
|
const byte partyU[8] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17};
|
|
const byte partyV[8] = {0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87};
|
|
static const TPM_ALG_ID algs[] = {
|
|
TPM_ALG_SHA256,
|
|
#ifdef WOLFSSL_SHA384
|
|
TPM_ALG_SHA384,
|
|
#endif
|
|
#ifdef WOLFSSL_SHA512
|
|
TPM_ALG_SHA512,
|
|
#endif
|
|
};
|
|
byte key1[96];
|
|
byte key2[96];
|
|
|
|
for (iAlg = 0; iAlg < sizeof(algs)/sizeof(algs[0]); iAlg++) {
|
|
for (iSz = 0; iSz < sizeof(sizes)/sizeof(sizes[0]); iSz++) {
|
|
UINT32 sz = sizes[iSz];
|
|
rc = TPM2_KDFe_ex(algs[iAlg], Z, sizeof(Z), label,
|
|
partyU, sizeof(partyU), partyV, sizeof(partyV), key1, sz);
|
|
AssertIntEQ((int)sz, rc);
|
|
rc = TPM2_KDFe_ex(algs[iAlg], Z, sizeof(Z), label,
|
|
partyU, sizeof(partyU), partyV, sizeof(partyV), key2, sz);
|
|
AssertIntEQ((int)sz, rc);
|
|
AssertIntEQ(0, XMEMCMP(key1, key2, sz));
|
|
}
|
|
}
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "KDFe multi-hash:");
|
|
#else
|
|
printf("Test TPM Wrapper: %-40s Skipped\n", "KDFe multi-hash:");
|
|
#endif
|
|
}
|
|
|
|
/* Exercise HmacCompute concat path (data2 != NULL) and multi-hash
|
|
* branches. Reference result is computed by feeding the same bytes in
|
|
* one call (data1 || data2) and comparing. */
|
|
static void test_TPM2_HmacCompute_MultiHash(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_HMAC)
|
|
int rc;
|
|
size_t iAlg;
|
|
static const TPM_ALG_ID algs[] = {
|
|
TPM_ALG_SHA256,
|
|
#ifdef WOLFSSL_SHA384
|
|
TPM_ALG_SHA384,
|
|
#endif
|
|
#ifdef WOLFSSL_SHA512
|
|
TPM_ALG_SHA512,
|
|
#endif
|
|
};
|
|
const byte hmacKey[16] = {
|
|
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
|
|
0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, 0x01};
|
|
const byte data1[] = "concat-HMAC-left-half";
|
|
const byte data2[] = "concat-HMAC-right-half";
|
|
byte full[sizeof(data1) - 1 + sizeof(data2) - 1];
|
|
byte d_split[TPM_MAX_DIGEST_SIZE];
|
|
byte d_full[TPM_MAX_DIGEST_SIZE];
|
|
word32 splitSz;
|
|
word32 fullSz;
|
|
|
|
XMEMCPY(full, data1, sizeof(data1) - 1);
|
|
XMEMCPY(full + sizeof(data1) - 1, data2, sizeof(data2) - 1);
|
|
|
|
for (iAlg = 0; iAlg < sizeof(algs)/sizeof(algs[0]); iAlg++) {
|
|
splitSz = sizeof(d_split);
|
|
fullSz = sizeof(d_full);
|
|
rc = TPM2_HmacCompute(algs[iAlg], hmacKey, sizeof(hmacKey),
|
|
data1, sizeof(data1) - 1,
|
|
data2, sizeof(data2) - 1,
|
|
d_split, &splitSz);
|
|
AssertIntEQ(0, rc);
|
|
rc = TPM2_HmacCompute(algs[iAlg], hmacKey, sizeof(hmacKey),
|
|
full, sizeof(full),
|
|
NULL, 0,
|
|
d_full, &fullSz);
|
|
AssertIntEQ(0, rc);
|
|
AssertIntEQ((int)fullSz, (int)splitSz);
|
|
AssertIntEQ(0, XMEMCMP(d_split, d_full, splitSz));
|
|
}
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "HmacCompute multi-hash:");
|
|
#else
|
|
printf("Test TPM Wrapper: %-40s Skipped\n", "HmacCompute multi-hash:");
|
|
#endif
|
|
}
|
|
|
|
static void test_TPM2_HashCompute_MultiHash(void)
|
|
{
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
int rc;
|
|
size_t i;
|
|
static const struct { TPM_ALG_ID alg; int dsz; } cases[] = {
|
|
{ TPM_ALG_SHA256, 32 },
|
|
#ifdef WOLFSSL_SHA384
|
|
{ TPM_ALG_SHA384, 48 },
|
|
#endif
|
|
#ifdef WOLFSSL_SHA512
|
|
{ TPM_ALG_SHA512, 64 },
|
|
#endif
|
|
};
|
|
const byte msg[] = "wolfTPM-unit-test-hash";
|
|
byte d1[TPM_MAX_DIGEST_SIZE];
|
|
byte d2[TPM_MAX_DIGEST_SIZE];
|
|
word32 sz1;
|
|
word32 sz2;
|
|
|
|
for (i = 0; i < sizeof(cases)/sizeof(cases[0]); i++) {
|
|
sz1 = sizeof(d1);
|
|
sz2 = sizeof(d2);
|
|
rc = TPM2_HashCompute(cases[i].alg, msg, sizeof(msg) - 1, d1, &sz1);
|
|
AssertIntEQ(0, rc);
|
|
AssertIntEQ(cases[i].dsz, (int)sz1);
|
|
rc = TPM2_HashCompute(cases[i].alg, msg, sizeof(msg) - 1, d2, &sz2);
|
|
AssertIntEQ(0, rc);
|
|
AssertIntEQ(0, XMEMCMP(d1, d2, sz1));
|
|
}
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "HashCompute multi-hash:");
|
|
#else
|
|
printf("Test TPM Wrapper: %-40s Skipped\n", "HashCompute multi-hash:");
|
|
#endif
|
|
}
|
|
|
|
/* Negative / input-validation coverage for KDFa_ex and KDFe_ex. */
|
|
static void test_TPM2_KDF_Errors(void)
|
|
{
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
int rc;
|
|
byte key[32];
|
|
const byte buf[16] = {0};
|
|
const char label[] = "LABEL";
|
|
|
|
/* KDFa: NULL key returns BAD_FUNC_ARG */
|
|
rc = TPM2_KDFa_ex(TPM_ALG_SHA256, buf, sizeof(buf), label,
|
|
NULL, 0, NULL, 0, NULL, sizeof(key));
|
|
AssertIntEQ(BAD_FUNC_ARG, rc);
|
|
|
|
/* KDFa: unsupported hash returns NOT_COMPILED_IN */
|
|
rc = TPM2_KDFa_ex(TPM_ALG_NULL, buf, sizeof(buf), label,
|
|
NULL, 0, NULL, 0, key, sizeof(key));
|
|
AssertIntEQ(NOT_COMPILED_IN, rc);
|
|
|
|
/* KDFe: NULL key returns BAD_FUNC_ARG */
|
|
rc = TPM2_KDFe_ex(TPM_ALG_SHA256, buf, sizeof(buf), label,
|
|
NULL, 0, NULL, 0, NULL, sizeof(key));
|
|
AssertIntEQ(BAD_FUNC_ARG, rc);
|
|
|
|
/* KDFe: NULL Z returns BAD_FUNC_ARG */
|
|
rc = TPM2_KDFe_ex(TPM_ALG_SHA256, NULL, 0, label,
|
|
NULL, 0, NULL, 0, key, sizeof(key));
|
|
AssertIntEQ(BAD_FUNC_ARG, rc);
|
|
|
|
/* KDFe: unsupported hash returns NOT_COMPILED_IN */
|
|
rc = TPM2_KDFe_ex(TPM_ALG_NULL, buf, sizeof(buf), label,
|
|
NULL, 0, NULL, 0, key, sizeof(key));
|
|
AssertIntEQ(NOT_COMPILED_IN, rc);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "KDF error paths:");
|
|
#else
|
|
printf("Test TPM Wrapper: %-40s Skipped\n", "KDF error paths:");
|
|
#endif
|
|
}
|
|
|
|
/* Round-trip TPM2_GetHashType -> TPM2_GetTpmHashType for each TPM
|
|
* hash algorithm compiled into wolfCrypt. Unknown inputs map to
|
|
* TPM_ALG_ERROR. */
|
|
static void test_TPM2_GetTpmHashType(void)
|
|
{
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
static const TPMI_ALG_HASH algs[] = {
|
|
#ifndef NO_SHA
|
|
TPM_ALG_SHA1,
|
|
#endif
|
|
TPM_ALG_SHA256,
|
|
#ifdef WOLFSSL_SHA384
|
|
TPM_ALG_SHA384,
|
|
#endif
|
|
#ifdef WOLFSSL_SHA512
|
|
TPM_ALG_SHA512,
|
|
#endif
|
|
};
|
|
size_t i;
|
|
int wcType;
|
|
TPMI_ALG_HASH roundTrip;
|
|
|
|
for (i = 0; i < sizeof(algs)/sizeof(algs[0]); i++) {
|
|
wcType = TPM2_GetHashType(algs[i]);
|
|
AssertIntNE((int)WC_HASH_TYPE_NONE, wcType);
|
|
roundTrip = TPM2_GetTpmHashType(wcType);
|
|
AssertIntEQ(algs[i], roundTrip);
|
|
}
|
|
|
|
/* Unknown wolfCrypt hash type returns TPM_ALG_ERROR */
|
|
AssertIntEQ(TPM_ALG_ERROR, TPM2_GetTpmHashType(0xFFFF));
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "GetTpmHashType:");
|
|
#else
|
|
printf("Test TPM Wrapper: %-40s Skipped\n", "GetTpmHashType:");
|
|
#endif
|
|
}
|
|
|
|
static void test_TPM2_ResponseHmacVerification(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_HMAC)
|
|
int rc;
|
|
TPM2B_AUTH auth;
|
|
TPM2B_DIGEST hash;
|
|
TPM2B_NONCE nonceNew, nonceOld;
|
|
TPMA_SESSION sessionAttr = TPMA_SESSION_continueSession;
|
|
TPM2B_AUTH hmac1, hmac2;
|
|
|
|
/* Set up known auth key */
|
|
auth.size = 8;
|
|
XMEMSET(auth.buffer, 0xAA, auth.size);
|
|
|
|
/* Set up known cpHash/rpHash */
|
|
hash.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(hash.buffer, 0x55, hash.size);
|
|
|
|
/* Set up nonces */
|
|
nonceNew.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(nonceNew.buffer, 0x11, nonceNew.size);
|
|
nonceOld.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(nonceOld.buffer, 0x22, nonceOld.size);
|
|
|
|
/* Compute valid HMAC */
|
|
rc = TPM2_CalcHmac(TPM_ALG_SHA256, &auth, &hash, &nonceNew, &nonceOld,
|
|
sessionAttr, &hmac1);
|
|
AssertIntEQ(0, rc);
|
|
AssertIntGT(hmac1.size, 0);
|
|
|
|
/* Compute same HMAC again — must be identical */
|
|
rc = TPM2_CalcHmac(TPM_ALG_SHA256, &auth, &hash, &nonceNew, &nonceOld,
|
|
sessionAttr, &hmac2);
|
|
AssertIntEQ(0, rc);
|
|
AssertIntEQ(0, TPM2_ConstantCompare(hmac1.buffer, hmac2.buffer,
|
|
hmac1.size));
|
|
|
|
/* Tamper one byte of the HMAC — verification must detect mismatch */
|
|
hmac2.buffer[0] ^= 0xFF;
|
|
AssertIntNE(0, TPM2_ConstantCompare(hmac1.buffer, hmac2.buffer,
|
|
hmac1.size));
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "ResponseHmacVerification:");
|
|
#endif
|
|
}
|
|
|
|
static void test_TPM2_CalcHmac(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_HMAC)
|
|
int rc;
|
|
TPM2B_AUTH auth;
|
|
TPM2B_DIGEST hash;
|
|
TPM2B_NONCE nonceA, nonceB;
|
|
TPMA_SESSION attr = TPMA_SESSION_continueSession;
|
|
TPM2B_AUTH hmac1, hmac2;
|
|
/* KAT: HMAC-SHA256("test", 0xAB*32 || 0x11*32 || 0x22*32 || attr(0x01)) */
|
|
const byte hmacExp[TPM_SHA256_DIGEST_SIZE] = {
|
|
0x42, 0x7f, 0xbf, 0xe1, 0x1b, 0xc3, 0x4d, 0xff,
|
|
0x89, 0x73, 0x43, 0x79, 0x8f, 0xb6, 0xaa, 0x88,
|
|
0xcd, 0xb3, 0xde, 0xae, 0x88, 0x21, 0xe9, 0xe6,
|
|
0x40, 0x9a, 0x51, 0x3c, 0x68, 0xd5, 0x90, 0xdf};
|
|
|
|
/* Known auth key */
|
|
auth.size = 4;
|
|
auth.buffer[0] = 't'; auth.buffer[1] = 'e';
|
|
auth.buffer[2] = 's'; auth.buffer[3] = 't';
|
|
|
|
/* Known cpHash */
|
|
hash.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(hash.buffer, 0xAB, hash.size);
|
|
|
|
/* Two distinct nonces */
|
|
nonceA.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(nonceA.buffer, 0x11, nonceA.size);
|
|
nonceB.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(nonceB.buffer, 0x22, nonceB.size);
|
|
|
|
/* Compute HMAC with (nonceA, nonceB) order */
|
|
rc = TPM2_CalcHmac(TPM_ALG_SHA256, &auth, &hash, &nonceA, &nonceB,
|
|
attr, &hmac1);
|
|
AssertIntEQ(0, rc);
|
|
|
|
/* Pin the exact HMAC so the cpHash and sessionAttributes contributions
|
|
* are verified, not just relative nonce ordering */
|
|
AssertIntEQ(hmac1.size, (int)sizeof(hmacExp));
|
|
AssertIntEQ(0, XMEMCMP(hmac1.buffer, hmacExp, sizeof(hmacExp)));
|
|
|
|
/* Compute HMAC with (nonceB, nonceA) — reversed order */
|
|
rc = TPM2_CalcHmac(TPM_ALG_SHA256, &auth, &hash, &nonceB, &nonceA,
|
|
attr, &hmac2);
|
|
AssertIntEQ(0, rc);
|
|
|
|
/* Reversed nonces MUST produce different HMAC */
|
|
AssertIntNE(0, XMEMCMP(hmac1.buffer, hmac2.buffer, hmac1.size));
|
|
|
|
/* Changing only the cpHash MUST change the HMAC (binds command params) */
|
|
XMEMSET(hash.buffer, 0xCD, hash.size);
|
|
rc = TPM2_CalcHmac(TPM_ALG_SHA256, &auth, &hash, &nonceA, &nonceB,
|
|
attr, &hmac2);
|
|
AssertIntEQ(0, rc);
|
|
AssertIntNE(0, XMEMCMP(hmac1.buffer, hmac2.buffer, hmac1.size));
|
|
|
|
/* Changing only the sessionAttributes MUST change the HMAC */
|
|
XMEMSET(hash.buffer, 0xAB, hash.size);
|
|
rc = TPM2_CalcHmac(TPM_ALG_SHA256, &auth, &hash, &nonceA, &nonceB,
|
|
(TPMA_SESSION)0, &hmac2);
|
|
AssertIntEQ(0, rc);
|
|
AssertIntNE(0, XMEMCMP(hmac1.buffer, hmac2.buffer, hmac1.size));
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "CalcHmac:");
|
|
#endif
|
|
}
|
|
|
|
static void test_TPM2_ParamEnc_XOR_Vector(void)
|
|
{
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
int rc;
|
|
TPMI_ALG_HASH authHash = TPM_ALG_SHA256;
|
|
TPM2B_AUTH sessKey;
|
|
TPM2B_NONCE nonceCaller, nonceTPM;
|
|
const byte original[] = "XOR parameter encryption round-trip test";
|
|
byte data[sizeof(original)];
|
|
|
|
sessKey.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(sessKey.buffer, 0xCC, sessKey.size);
|
|
|
|
nonceCaller.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(nonceCaller.buffer, 0x11, nonceCaller.size);
|
|
nonceTPM.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(nonceTPM.buffer, 0x22, nonceTPM.size);
|
|
|
|
XMEMCPY(data, original, sizeof(original));
|
|
|
|
/* Encrypt */
|
|
rc = TPM2_ParamEnc_XOR(authHash, sessKey.buffer, sessKey.size,
|
|
nonceCaller.buffer, nonceCaller.size,
|
|
nonceTPM.buffer, nonceTPM.size,
|
|
data, sizeof(data));
|
|
AssertIntEQ(TPM_RC_SUCCESS, rc);
|
|
|
|
/* Data must differ from original */
|
|
AssertIntNE(0, XMEMCMP(data, original, sizeof(original)));
|
|
|
|
/* Encrypt again with same args — XOR is self-inverse */
|
|
rc = TPM2_ParamEnc_XOR(authHash, sessKey.buffer, sessKey.size,
|
|
nonceCaller.buffer, nonceCaller.size,
|
|
nonceTPM.buffer, nonceTPM.size,
|
|
data, sizeof(data));
|
|
AssertIntEQ(TPM_RC_SUCCESS, rc);
|
|
|
|
/* Must match original */
|
|
AssertIntEQ(0, XMEMCMP(data, original, sizeof(original)));
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "ParamEnc_XOR:");
|
|
#endif
|
|
}
|
|
|
|
static void test_TPM2_ParamEnc_XOR_MaskBoundary(void)
|
|
{
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
int rc;
|
|
TPMI_ALG_HASH authHash = TPM_ALG_SHA256;
|
|
TPM2B_AUTH sessKey;
|
|
TPM2B_NONCE nonceCaller, nonceTPM;
|
|
byte data[TPM2_XOR_MASK_MAX + 1];
|
|
|
|
sessKey.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(sessKey.buffer, 0xCC, sessKey.size);
|
|
nonceCaller.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(nonceCaller.buffer, 0x11, nonceCaller.size);
|
|
nonceTPM.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(nonceTPM.buffer, 0x22, nonceTPM.size);
|
|
XMEMSET(data, 0, sizeof(data));
|
|
|
|
/* exactly at capacity must succeed */
|
|
rc = TPM2_ParamEnc_XOR(authHash, sessKey.buffer, sessKey.size,
|
|
nonceCaller.buffer, nonceCaller.size,
|
|
nonceTPM.buffer, nonceTPM.size,
|
|
data, TPM2_XOR_MASK_MAX);
|
|
AssertIntEQ(TPM_RC_SUCCESS, rc);
|
|
|
|
/* one byte past capacity must be rejected */
|
|
rc = TPM2_ParamEnc_XOR(authHash, sessKey.buffer, sessKey.size,
|
|
nonceCaller.buffer, nonceCaller.size,
|
|
nonceTPM.buffer, nonceTPM.size,
|
|
data, TPM2_XOR_MASK_MAX + 1);
|
|
AssertIntEQ(BUFFER_E, rc);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "ParamEnc_XOR mask boundary:");
|
|
#endif
|
|
}
|
|
|
|
static void test_TPM2_ParamEnc_AESCFB_Vector(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFSSL_AES_CFB)
|
|
int rc;
|
|
TPMI_ALG_HASH authHash = TPM_ALG_SHA256;
|
|
UINT16 keyBits = MAX_AES_KEY_BITS;
|
|
TPM2B_AUTH sessKey;
|
|
TPM2B_NONCE nonceCaller, nonceTPM;
|
|
const byte original[] = "AES-CFB parameter encryption round-trip test";
|
|
byte data[sizeof(original)];
|
|
|
|
sessKey.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(sessKey.buffer, 0xDD, sessKey.size);
|
|
|
|
nonceCaller.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(nonceCaller.buffer, 0x33, nonceCaller.size);
|
|
nonceTPM.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(nonceTPM.buffer, 0x44, nonceTPM.size);
|
|
|
|
XMEMCPY(data, original, sizeof(original));
|
|
|
|
/* Encrypt with (nonceCaller, nonceTPM) */
|
|
rc = TPM2_ParamEnc_AESCFB(authHash, keyBits,
|
|
sessKey.buffer, sessKey.size,
|
|
nonceCaller.buffer, nonceCaller.size,
|
|
nonceTPM.buffer, nonceTPM.size,
|
|
data, sizeof(data), 1);
|
|
AssertIntEQ(TPM_RC_SUCCESS, rc);
|
|
|
|
/* Data must differ from original */
|
|
AssertIntNE(0, XMEMCMP(data, original, sizeof(original)));
|
|
|
|
/* Decrypt: same nonce order, doEncrypt=0 */
|
|
rc = TPM2_ParamEnc_AESCFB(authHash, keyBits,
|
|
sessKey.buffer, sessKey.size,
|
|
nonceCaller.buffer, nonceCaller.size,
|
|
nonceTPM.buffer, nonceTPM.size,
|
|
data, sizeof(data), 0);
|
|
AssertIntEQ(TPM_RC_SUCCESS, rc);
|
|
|
|
/* Must match original */
|
|
AssertIntEQ(0, XMEMCMP(data, original, sizeof(original)));
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "ParamEnc_AESCFB:");
|
|
#endif
|
|
}
|
|
|
|
static void test_TPM2_ParamEnc_AESCFB_KeyBoundary(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFSSL_AES_CFB)
|
|
int rc;
|
|
TPMI_ALG_HASH authHash = TPM_ALG_SHA256;
|
|
TPM2B_AUTH sessKey;
|
|
TPM2B_NONCE nonceCaller, nonceTPM;
|
|
byte data[32];
|
|
|
|
sessKey.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(sessKey.buffer, 0xDD, sessKey.size);
|
|
nonceCaller.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(nonceCaller.buffer, 0x33, nonceCaller.size);
|
|
nonceTPM.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(nonceTPM.buffer, 0x44, nonceTPM.size);
|
|
XMEMSET(data, 0, sizeof(data));
|
|
|
|
/* keyBits above 256 (symKeySz > 32) must be rejected, not overflow symKey */
|
|
rc = TPM2_ParamEnc_AESCFB(authHash, 512,
|
|
sessKey.buffer, sessKey.size,
|
|
nonceCaller.buffer, nonceCaller.size,
|
|
nonceTPM.buffer, nonceTPM.size,
|
|
data, sizeof(data), 1);
|
|
AssertIntEQ(BUFFER_E, rc);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "ParamEnc_AESCFB key boundary:");
|
|
#endif
|
|
}
|
|
|
|
/* Known-answer test cross-checking TPM2_ParamEnc_AESCFB against an
|
|
* independent KDFa + AES-CFB reference built from wolfCrypt primitives.
|
|
* The pure round-trip test above cannot detect mutations that affect
|
|
* encrypt and decrypt symmetrically (IV-offset, label, KDFa output
|
|
* split); this KAT does. */
|
|
static void test_TPM2_ParamEnc_AESCFB_KAT(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFSSL_AES_CFB)
|
|
int rc;
|
|
TPMI_ALG_HASH authHash = TPM_ALG_SHA256;
|
|
UINT16 keyBits = MAX_AES_KEY_BITS;
|
|
int keyBytes = (int)keyBits / 8;
|
|
TPM2B_AUTH sessKey;
|
|
TPM2B_NONCE nonceCaller, nonceTPM;
|
|
const byte original[] = "AES-CFB KAT vector";
|
|
byte tpmCt[sizeof(original)];
|
|
byte refCt[sizeof(original)];
|
|
byte symKey[MAX_AES_KEY_BYTES + MAX_AES_BLOCK_SIZE_BYTES];
|
|
Aes aes;
|
|
|
|
sessKey.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(sessKey.buffer, 0xCC, sessKey.size);
|
|
nonceCaller.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(nonceCaller.buffer, 0x11, nonceCaller.size);
|
|
nonceTPM.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(nonceTPM.buffer, 0x22, nonceTPM.size);
|
|
|
|
XMEMCPY(tpmCt, original, sizeof(original));
|
|
XMEMCPY(refCt, original, sizeof(original));
|
|
|
|
rc = TPM2_ParamEnc_AESCFB(authHash, keyBits,
|
|
sessKey.buffer, sessKey.size,
|
|
nonceCaller.buffer, nonceCaller.size,
|
|
nonceTPM.buffer, nonceTPM.size,
|
|
tpmCt, sizeof(tpmCt), 1);
|
|
AssertIntEQ(TPM_RC_SUCCESS, rc);
|
|
|
|
rc = TPM2_KDFa(authHash, (TPM2B_DATA*)&sessKey,
|
|
"CFB", &nonceCaller, &nonceTPM,
|
|
symKey, (UINT32)(keyBytes + MAX_AES_BLOCK_SIZE_BYTES));
|
|
AssertIntEQ(keyBytes + MAX_AES_BLOCK_SIZE_BYTES, rc);
|
|
|
|
AssertIntEQ(0, wc_AesInit(&aes, NULL, INVALID_DEVID));
|
|
AssertIntEQ(0, wc_AesSetKey(&aes, symKey, (word32)keyBytes,
|
|
&symKey[keyBytes], AES_ENCRYPTION));
|
|
AssertIntEQ(0, wc_AesCfbEncrypt(&aes, refCt, refCt, sizeof(refCt)));
|
|
wc_AesFree(&aes);
|
|
|
|
AssertIntEQ(0, XMEMCMP(tpmCt, refCt, sizeof(refCt)));
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "ParamEnc_AESCFB KAT:");
|
|
#endif
|
|
}
|
|
|
|
static void test_TPM2_ParamDec_XOR_Roundtrip(void)
|
|
{
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
int rc;
|
|
TPMI_ALG_HASH authHash = TPM_ALG_SHA256;
|
|
TPM2B_AUTH sessKey;
|
|
TPM2B_NONCE nonceCaller, nonceTPM;
|
|
const byte original[] = "XOR parameter decryption round-trip test";
|
|
byte data[sizeof(original)];
|
|
|
|
sessKey.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(sessKey.buffer, 0xEE, sessKey.size);
|
|
|
|
nonceCaller.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(nonceCaller.buffer, 0x55, nonceCaller.size);
|
|
nonceTPM.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(nonceTPM.buffer, 0x66, nonceTPM.size);
|
|
|
|
XMEMCPY(data, original, sizeof(original));
|
|
|
|
/* Decrypt direction uses (nonceTPM, nonceCaller) order. XOR is symmetric
|
|
* so the same TPM2_ParamEnc_XOR call performs decryption. */
|
|
rc = TPM2_ParamEnc_XOR(authHash, sessKey.buffer, sessKey.size,
|
|
nonceTPM.buffer, nonceTPM.size,
|
|
nonceCaller.buffer, nonceCaller.size,
|
|
data, sizeof(data));
|
|
AssertIntEQ(TPM_RC_SUCCESS, rc);
|
|
|
|
/* Data must differ from original */
|
|
AssertIntNE(0, XMEMCMP(data, original, sizeof(original)));
|
|
|
|
/* Apply same XOR again — self-inverse recovers original */
|
|
rc = TPM2_ParamEnc_XOR(authHash, sessKey.buffer, sessKey.size,
|
|
nonceTPM.buffer, nonceTPM.size,
|
|
nonceCaller.buffer, nonceCaller.size,
|
|
data, sizeof(data));
|
|
AssertIntEQ(TPM_RC_SUCCESS, rc);
|
|
|
|
/* Must match original */
|
|
AssertIntEQ(0, XMEMCMP(data, original, sizeof(original)));
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "ParamDec_XOR_Roundtrip:");
|
|
#endif
|
|
}
|
|
|
|
static void test_TPM2_ParamDec_AESCFB_Roundtrip(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFSSL_AES_CFB)
|
|
int rc;
|
|
TPMI_ALG_HASH authHash = TPM_ALG_SHA256;
|
|
UINT16 keyBits = MAX_AES_KEY_BITS;
|
|
TPM2B_AUTH sessKey;
|
|
TPM2B_NONCE nonceCaller, nonceTPM;
|
|
const byte original[] = "AES-CFB parameter decryption round-trip test";
|
|
byte data[sizeof(original)];
|
|
|
|
sessKey.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(sessKey.buffer, 0xFF, sessKey.size);
|
|
|
|
nonceCaller.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(nonceCaller.buffer, 0x77, nonceCaller.size);
|
|
nonceTPM.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(nonceTPM.buffer, 0x88, nonceTPM.size);
|
|
|
|
XMEMCPY(data, original, sizeof(original));
|
|
|
|
/* Encrypt: command direction uses (nonceCaller, nonceTPM), doEncrypt=1 */
|
|
rc = TPM2_ParamEnc_AESCFB(authHash, keyBits,
|
|
sessKey.buffer, sessKey.size,
|
|
nonceCaller.buffer, nonceCaller.size,
|
|
nonceTPM.buffer, nonceTPM.size,
|
|
data, sizeof(data), 1);
|
|
AssertIntEQ(TPM_RC_SUCCESS, rc);
|
|
|
|
/* Data must differ from original */
|
|
AssertIntNE(0, XMEMCMP(data, original, sizeof(original)));
|
|
|
|
/* Decrypt: same nonce order so KDFa produces the same key, doEncrypt=0 */
|
|
rc = TPM2_ParamEnc_AESCFB(authHash, keyBits,
|
|
sessKey.buffer, sessKey.size,
|
|
nonceCaller.buffer, nonceCaller.size,
|
|
nonceTPM.buffer, nonceTPM.size,
|
|
data, sizeof(data), 0);
|
|
AssertIntEQ(TPM_RC_SUCCESS, rc);
|
|
|
|
/* Must match original */
|
|
AssertIntEQ(0, XMEMCMP(data, original, sizeof(original)));
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "ParamDec_AESCFB_Roundtrip:");
|
|
#endif
|
|
}
|
|
|
|
/* Test dispatch-level CmdRequest/CmdResponse nonce mapping.
|
|
* Command direction: host encrypts with KDFa(nonceCaller, nonceTPM).
|
|
* Response direction: TPM encrypts with KDFa(nonceTPM, nonceCaller),
|
|
* so host decryption (CmdResponse) must derive the same key.
|
|
* We simulate the TPM's response encryption using the standalone function
|
|
* with the response-direction nonce order, then verify CmdResponse decrypts. */
|
|
static void test_TPM2_ParamEncDec_Dispatch_Roundtrip(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFSSL_AES_CFB)
|
|
int rc;
|
|
TPM2_AUTH_SESSION session;
|
|
const byte original[] = "Dispatch-level param enc/dec roundtrip test data";
|
|
byte data[sizeof(original)];
|
|
|
|
/* Set up session with distinct nonces to catch any swap mutation */
|
|
XMEMSET(&session, 0, sizeof(session));
|
|
session.authHash = TPM_ALG_SHA256;
|
|
session.symmetric.algorithm = TPM_ALG_AES;
|
|
session.symmetric.keyBits.aes = MAX_AES_KEY_BITS;
|
|
session.symmetric.mode.aes = TPM_ALG_CFB;
|
|
|
|
session.auth.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(session.auth.buffer, 0xAA, session.auth.size);
|
|
|
|
session.nonceCaller.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(session.nonceCaller.buffer, 0x11, session.nonceCaller.size);
|
|
session.nonceTPM.size = TPM_SHA256_DIGEST_SIZE;
|
|
XMEMSET(session.nonceTPM.buffer, 0x22, session.nonceTPM.size);
|
|
|
|
XMEMCPY(data, original, sizeof(original));
|
|
|
|
/* Test 1: Command direction — CmdRequest enc, TPM-side dec recovers.
|
|
* Simulate TPM decryption with raw AES-CFB using command-direction
|
|
* nonce order: KDFa(nonceCaller, nonceTPM). doEncrypt=0 */
|
|
rc = TPM2_ParamEnc_CmdRequest(&session, data, sizeof(data));
|
|
AssertIntEQ(TPM_RC_SUCCESS, rc);
|
|
AssertIntNE(0, XMEMCMP(data, original, sizeof(original)));
|
|
|
|
rc = TPM2_ParamEnc_AESCFB(session.authHash,
|
|
session.symmetric.keyBits.aes,
|
|
session.auth.buffer, session.auth.size,
|
|
session.nonceCaller.buffer, session.nonceCaller.size,
|
|
session.nonceTPM.buffer, session.nonceTPM.size,
|
|
data, sizeof(data), 0);
|
|
AssertIntEQ(TPM_RC_SUCCESS, rc);
|
|
AssertIntEQ(0, XMEMCMP(data, original, sizeof(original)));
|
|
|
|
/* Test 2: Response direction — TPM-side enc, CmdResponse dec recovers.
|
|
* Simulate TPM encrypting a response with response-direction nonce order:
|
|
* KDFa(nonceTPM, nonceCaller). doEncrypt=1 */
|
|
XMEMCPY(data, original, sizeof(original));
|
|
rc = TPM2_ParamEnc_AESCFB(session.authHash,
|
|
session.symmetric.keyBits.aes,
|
|
session.auth.buffer, session.auth.size,
|
|
session.nonceTPM.buffer, session.nonceTPM.size,
|
|
session.nonceCaller.buffer, session.nonceCaller.size,
|
|
data, sizeof(data), 1);
|
|
AssertIntEQ(TPM_RC_SUCCESS, rc);
|
|
AssertIntNE(0, XMEMCMP(data, original, sizeof(original)));
|
|
|
|
rc = TPM2_ParamDec_CmdResponse(&session, data, sizeof(data));
|
|
AssertIntEQ(TPM_RC_SUCCESS, rc);
|
|
AssertIntEQ(0, XMEMCMP(data, original, sizeof(original)));
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "ParamEncDec_Dispatch:");
|
|
#else
|
|
printf("Test TPM Wrapper: %-40s Skipped\n", "ParamEncDec_Dispatch:");
|
|
#endif
|
|
}
|
|
|
|
/* Known-answer test for TPM2_HashNvPublic serialization and hashing.
|
|
* Reference: independently computed SHA-256 over the marshaled NV public
|
|
* area fields in TPM 2.0 canonical order. */
|
|
static void test_TPM2_HashNvPublic(void)
|
|
{
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
int rc;
|
|
TPMS_NV_PUBLIC nvPublic;
|
|
byte nameBuffer[2 + WC_MAX_DIGEST_SIZE];
|
|
UINT16 nameSize = 0;
|
|
/* Expected Name: nameAlg(BE) || SHA256(nvIndex||nameAlg||attributes||
|
|
* authPolicy.size||dataSize) */
|
|
static const byte expectedName[] = {
|
|
0x00, 0x0b, 0x95, 0x61, 0x47, 0xe5, 0x81, 0xbd, 0xe0, 0xad, 0x4d, 0x95,
|
|
0x83, 0x8d, 0x2c, 0x6b, 0x7b, 0xa5, 0x1c, 0xc0, 0xad, 0x56, 0xd8, 0xec,
|
|
0xb7, 0x30, 0x24, 0xfa, 0x34, 0xb9, 0x95, 0x8f, 0xee, 0x45
|
|
};
|
|
|
|
XMEMSET(&nvPublic, 0, sizeof(nvPublic));
|
|
nvPublic.nvIndex = 0x01500020;
|
|
nvPublic.nameAlg = TPM_ALG_SHA256;
|
|
nvPublic.attributes = TPMA_NV_AUTHWRITE | TPMA_NV_AUTHREAD | TPMA_NV_NO_DA;
|
|
nvPublic.authPolicy.size = 0;
|
|
nvPublic.dataSize = 32;
|
|
|
|
XMEMSET(nameBuffer, 0, sizeof(nameBuffer));
|
|
rc = TPM2_HashNvPublic(&nvPublic, nameBuffer, &nameSize);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(nameSize, (int)sizeof(expectedName));
|
|
AssertIntEQ(0, XMEMCMP(nameBuffer, expectedName, sizeof(expectedName)));
|
|
|
|
/* Test NULL args */
|
|
rc = TPM2_HashNvPublic(NULL, nameBuffer, &nameSize);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = TPM2_HashNvPublic(&nvPublic, NULL, &nameSize);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = TPM2_HashNvPublic(&nvPublic, nameBuffer, NULL);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "HashNvPublic:");
|
|
#else
|
|
printf("Test TPM Wrapper: %-40s Skipped\n", "HashNvPublic:");
|
|
#endif
|
|
}
|
|
|
|
/* Known-answer test for wolfTPM2_ComputeName.
|
|
* Reference: nameAlg(BE) || SHA256(serialized TPMT_PUBLIC) computed
|
|
* independently for an ECC P-256 key with known field values. */
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC)
|
|
static void test_wolfTPM2_ComputeName(void)
|
|
{
|
|
int rc;
|
|
TPM2B_PUBLIC pub;
|
|
TPM2B_NAME name;
|
|
static const byte expectedName[] = {
|
|
0x00, 0x0b, 0x35, 0xc3, 0x57, 0x9d, 0xf1, 0xb5,
|
|
0x24, 0x6a, 0xb7, 0x9a, 0x0a, 0xf2, 0xd5, 0x44,
|
|
0xcb, 0x63, 0x2a, 0x80, 0xe2, 0x24, 0x1d, 0xd3,
|
|
0x84, 0x06, 0x34, 0xe4, 0x38, 0x00, 0x61, 0xc0,
|
|
0x2e, 0x6f
|
|
};
|
|
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
pub.publicArea.type = TPM_ALG_ECC;
|
|
pub.publicArea.nameAlg = TPM_ALG_SHA256;
|
|
pub.publicArea.objectAttributes = (TPMA_OBJECT_sign | TPMA_OBJECT_decrypt |
|
|
TPMA_OBJECT_userWithAuth | TPMA_OBJECT_noDA);
|
|
pub.publicArea.authPolicy.size = 0;
|
|
pub.publicArea.parameters.eccDetail.symmetric.algorithm = TPM_ALG_NULL;
|
|
pub.publicArea.parameters.eccDetail.scheme.scheme = TPM_ALG_NULL;
|
|
pub.publicArea.parameters.eccDetail.curveID = TPM_ECC_NIST_P256;
|
|
pub.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
|
|
pub.publicArea.unique.ecc.x.size = 32;
|
|
XMEMSET(pub.publicArea.unique.ecc.x.buffer, 0x11, 32);
|
|
pub.publicArea.unique.ecc.y.size = 32;
|
|
XMEMSET(pub.publicArea.unique.ecc.y.buffer, 0x22, 32);
|
|
|
|
XMEMSET(&name, 0, sizeof(name));
|
|
rc = wolfTPM2_ComputeName(&pub, &name);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(name.size, (int)sizeof(expectedName));
|
|
AssertIntEQ(0, XMEMCMP(name.name, expectedName, sizeof(expectedName)));
|
|
|
|
/* Test NULL args */
|
|
rc = wolfTPM2_ComputeName(NULL, &name);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_ComputeName(&pub, NULL);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* Test nameAlg = TPM_ALG_NULL returns success with empty name */
|
|
pub.publicArea.nameAlg = TPM_ALG_NULL;
|
|
XMEMSET(&name, 0xFF, sizeof(name));
|
|
rc = wolfTPM2_ComputeName(&pub, &name);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(name.size, 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "ComputeName:");
|
|
}
|
|
#endif
|
|
|
|
/* Test ECC ECDAA scheme serialization roundtrip — verifies count field
|
|
* is preserved, and RSA RSAES scheme produces no spurious hashAlg */
|
|
static void test_TPM2_SchemeSerialize(void)
|
|
{
|
|
TPM2_Packet packet;
|
|
byte buf[256];
|
|
TPMT_SIG_SCHEME eccSchemeIn, eccSchemeOut;
|
|
#ifndef NO_RSA
|
|
TPMT_RSA_SCHEME rsaSchemeIn, rsaSchemeOut;
|
|
#endif
|
|
|
|
/* Test 1: ECDAA scheme roundtrip — count field must survive */
|
|
XMEMSET(&eccSchemeIn, 0, sizeof(eccSchemeIn));
|
|
eccSchemeIn.scheme = TPM_ALG_ECDAA;
|
|
eccSchemeIn.details.ecdaa.hashAlg = TPM_ALG_SHA256;
|
|
eccSchemeIn.details.ecdaa.count = 5;
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendEccScheme(&packet, &eccSchemeIn);
|
|
|
|
/* For ECDAA: scheme(2) + hashAlg(2) + count(2) = 6 bytes */
|
|
AssertIntEQ(packet.pos, 6);
|
|
|
|
/* Parse back */
|
|
packet.pos = 0;
|
|
XMEMSET(&eccSchemeOut, 0, sizeof(eccSchemeOut));
|
|
TPM2_Packet_ParseEccScheme(&packet, &eccSchemeOut);
|
|
|
|
AssertIntEQ(eccSchemeOut.scheme, TPM_ALG_ECDAA);
|
|
AssertIntEQ(eccSchemeOut.details.ecdaa.hashAlg, TPM_ALG_SHA256);
|
|
AssertIntEQ(eccSchemeOut.details.ecdaa.count, 5);
|
|
|
|
#ifndef NO_RSA
|
|
/* Test 2: RSAES scheme roundtrip — no hashAlg field (TPMS_EMPTY) */
|
|
XMEMSET(&rsaSchemeIn, 0, sizeof(rsaSchemeIn));
|
|
rsaSchemeIn.scheme = TPM_ALG_RSAES;
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendRsaScheme(&packet, &rsaSchemeIn);
|
|
|
|
/* For RSAES: scheme(2) only, no hashAlg */
|
|
AssertIntEQ(packet.pos, 2);
|
|
|
|
/* Parse back */
|
|
packet.pos = 0;
|
|
XMEMSET(&rsaSchemeOut, 0, sizeof(rsaSchemeOut));
|
|
TPM2_Packet_ParseRsaScheme(&packet, &rsaSchemeOut);
|
|
|
|
AssertIntEQ(rsaSchemeOut.scheme, TPM_ALG_RSAES);
|
|
#endif
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "SchemeSerialize:");
|
|
}
|
|
|
|
/* Exercise the parse sequence used by TPM2_ECC_Parameters response: sign
|
|
* scheme = ECDAA (scheme + hashAlg + count) followed by a trailing U16
|
|
* size field. Ensures the ECDAA count field is consumed so the next read
|
|
* lands at the correct offset. The wire bytes are built by hand to avoid
|
|
* relying on non-exported packet helpers. */
|
|
static void test_TPM2_ECC_Parameters_EcdaaResponseParse(void)
|
|
{
|
|
TPM2_Packet packet;
|
|
byte buf[32];
|
|
TPMT_SIG_SCHEME signOut;
|
|
UINT16 pSizeOut = 0;
|
|
|
|
/* Hand-built wire: TPM2B wire is big-endian.
|
|
* [0-1] sign.scheme = TPM_ALG_ECDAA (0x001A)
|
|
* [2-3] sign.hashAlg = TPM_ALG_SHA256 (0x000B)
|
|
* [4-5] sign.count = 0x0007
|
|
* [6-7] p.size sentinel= 0x0030
|
|
*/
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
buf[0] = 0x00; buf[1] = (byte)TPM_ALG_ECDAA;
|
|
buf[2] = 0x00; buf[3] = (byte)TPM_ALG_SHA256;
|
|
buf[4] = 0x00; buf[5] = 0x07;
|
|
buf[6] = 0x00; buf[7] = 0x30;
|
|
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
packet.pos = 0;
|
|
|
|
XMEMSET(&signOut, 0, sizeof(signOut));
|
|
TPM2_Packet_ParseEccScheme(&packet, &signOut);
|
|
AssertIntEQ(signOut.scheme, TPM_ALG_ECDAA);
|
|
AssertIntEQ(signOut.details.ecdaa.hashAlg, TPM_ALG_SHA256);
|
|
AssertIntEQ(signOut.details.ecdaa.count, 7);
|
|
|
|
/* After parsing the ECDAA scheme, packet.pos must be at byte 6 so the
|
|
* next U16 read returns the sentinel 0x0030 (the simulated p.size).
|
|
* The buggy inline parser in TPM2_ECC_Parameters consumed only
|
|
* scheme+hashAlg (4 bytes) and left the count on the wire, which
|
|
* would make p.size read 0x0007 instead. */
|
|
AssertIntEQ(packet.pos, 6);
|
|
pSizeOut = (UINT16)((buf[packet.pos] << 8) | buf[packet.pos + 1]);
|
|
AssertIntEQ(pSizeOut, 0x0030);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "EcdaaResponseParse:");
|
|
}
|
|
|
|
/* TPM2_Packet_AppendSignature / ParseSignature must explicitly recognize
|
|
* TPM_ALG_NULL as a zero-payload signature so subsequent fields stay
|
|
* aligned. The previous default-fallthrough lumped TPM_ALG_NULL together
|
|
* with unknown algorithms, making the property "Parse(Append(NULL))
|
|
* consumes exactly the sigAlg bytes" depend on undocumented behavior. */
|
|
static void test_TPM2_ParseSignature_NullAlg(void)
|
|
{
|
|
TPM2_Packet packet;
|
|
byte buf[16];
|
|
TPMT_SIGNATURE sig;
|
|
UINT16 sentinel;
|
|
int pos = 0;
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
|
|
/* sigAlg = TPM_ALG_NULL */
|
|
buf[pos++] = (byte)((TPM_ALG_NULL >> 8) & 0xFF);
|
|
buf[pos++] = (byte)(TPM_ALG_NULL & 0xFF);
|
|
/* sentinel right after the (zero-length) signature payload */
|
|
buf[pos++] = 0xDE;
|
|
buf[pos++] = 0xAD;
|
|
|
|
XMEMSET(&sig, 0, sizeof(sig));
|
|
packet.buf = buf;
|
|
packet.size = pos;
|
|
packet.pos = 0;
|
|
|
|
TPM2_Packet_ParseSignature(&packet, &sig);
|
|
AssertIntEQ(sig.sigAlg, TPM_ALG_NULL);
|
|
AssertIntEQ(packet.pos, 2);
|
|
sentinel = (UINT16)((buf[packet.pos] << 8) | buf[packet.pos + 1]);
|
|
AssertIntEQ(sentinel, 0xDEAD);
|
|
|
|
/* Round-trip: Append a TPM_ALG_NULL signature into a fresh packet and
|
|
* verify only the 2-byte sigAlg was written. A future regression that
|
|
* drops the explicit case (defaulting to silent fallthrough) would
|
|
* still pass for Parse but the Append side is also locked in here. */
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
XMEMSET(&sig, 0, sizeof(sig));
|
|
sig.sigAlg = TPM_ALG_NULL;
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
packet.pos = 0;
|
|
TPM2_Packet_AppendSignature(&packet, &sig);
|
|
AssertIntEQ(packet.pos, 2);
|
|
AssertIntEQ(buf[0], (byte)((TPM_ALG_NULL >> 8) & 0xFF));
|
|
AssertIntEQ(buf[1], (byte)(TPM_ALG_NULL & 0xFF));
|
|
|
|
/* Re-parse confirms the round-trip. */
|
|
XMEMSET(&sig, 0, sizeof(sig));
|
|
packet.pos = 0;
|
|
TPM2_Packet_ParseSignature(&packet, &sig);
|
|
AssertIntEQ(sig.sigAlg, TPM_ALG_NULL);
|
|
AssertIntEQ(packet.pos, 2);
|
|
|
|
printf("Test TPM Wrapper:\tParseSignature NULL alg:\tPassed\n");
|
|
}
|
|
|
|
#ifdef WOLFTPM_MLDSA_VERIFY
|
|
/* TPM2_PolicyAuthorize must emit the 2-byte checkTicket metaAlg on the wire
|
|
* for a non-NULL DIGEST_VERIFIED ticket and omit it for VERIFIED / NULL
|
|
* tickets, mirroring the response parse side. Drives the real marshaling and
|
|
* inspects the finalized command left in ctx->cmdBuf (the send fails with no
|
|
* server, so the command buffer survives). */
|
|
static void test_TPM2_PolicyAuthorize_DigestVerifiedMetaAlg(void)
|
|
{
|
|
#if defined(WOLFTPM_SWTPM) && !defined(NO_GETENV)
|
|
TPM2_CTX ctx;
|
|
PolicyAuthorize_In in;
|
|
const byte* cmd;
|
|
word32 digestLen, cmdLenDigest, cmdLenVerified, cmdLenNull;
|
|
char savedPort[32];
|
|
const char* envPort;
|
|
int hadPort;
|
|
int rc;
|
|
int i;
|
|
|
|
/* Force the SWTPM connect to fail so the finalized command is left intact
|
|
* in ctx->cmdBuf instead of being overwritten by a live server response. */
|
|
envPort = getenv("TPM2_SWTPM_PORT");
|
|
hadPort = (envPort != NULL);
|
|
if (hadPort) {
|
|
XSTRNCPY(savedPort, envPort, sizeof(savedPort) - 1);
|
|
savedPort[sizeof(savedPort) - 1] = '\0';
|
|
}
|
|
AssertIntEQ(setenv("TPM2_SWTPM_PORT", "1", 1), 0);
|
|
|
|
XMEMSET(&ctx, 0, sizeof(ctx));
|
|
|
|
/* Register the active ctx and skip chip startup (timeoutTries = 0). */
|
|
AssertIntEQ(TPM2_Init_ex(&ctx, NULL, NULL, 0), TPM_RC_SUCCESS);
|
|
|
|
digestLen = TPM_SHA256_DIGEST_SIZE;
|
|
|
|
/* DIGEST_VERIFIED with a non-NULL hierarchy carries metaAlg. */
|
|
XMEMSET(&in, 0, sizeof(in));
|
|
in.checkTicket.tag = TPM_ST_DIGEST_VERIFIED;
|
|
in.checkTicket.hierarchy = TPM_RH_OWNER;
|
|
in.checkTicket.metaAlg = TPM_ALG_SHA256;
|
|
in.checkTicket.digest.size = (UINT16)digestLen;
|
|
for (i = 0; i < (int)digestLen; i++)
|
|
in.checkTicket.digest.buffer[i] = (byte)(0xA0 + i);
|
|
|
|
/* The send must fail (no server) so the marshaled command survives. */
|
|
rc = TPM2_PolicyAuthorize(&in);
|
|
AssertIntNE(rc, TPM_RC_SUCCESS);
|
|
|
|
cmd = ctx.cmdBuf;
|
|
/* Confirm cmdBuf still holds our command (no live server clobbered it). */
|
|
AssertIntEQ(cmd[6], 0x00);
|
|
AssertIntEQ(cmd[7], 0x00);
|
|
AssertIntEQ(cmd[8], 0x01);
|
|
AssertIntEQ(cmd[9], 0x6A); /* TPM_CC_PolicyAuthorize */
|
|
|
|
cmdLenDigest = ((word32)cmd[2] << 24) | ((word32)cmd[3] << 16) |
|
|
((word32)cmd[4] << 8) | (word32)cmd[5];
|
|
|
|
/* Under NO_ABORT a bypassed assert above must not let a short length
|
|
* underflow the trailing-offset math into an OOB index. */
|
|
AssertIntGT(cmdLenDigest, digestLen + 6);
|
|
|
|
/* Trailing layout: hierarchy(4) | metaAlg(2) | digest.size(2) | digest. */
|
|
AssertIntEQ(cmd[cmdLenDigest - digestLen - 2], (byte)(digestLen >> 8));
|
|
AssertIntEQ(cmd[cmdLenDigest - digestLen - 1], (byte)(digestLen & 0xFF));
|
|
AssertIntEQ(cmd[cmdLenDigest - digestLen - 4],
|
|
(byte)((TPM_ALG_SHA256 >> 8) & 0xFF));
|
|
AssertIntEQ(cmd[cmdLenDigest - digestLen - 3],
|
|
(byte)(TPM_ALG_SHA256 & 0xFF));
|
|
AssertIntEQ(cmd[cmdLenDigest - digestLen - 6], 0x00);
|
|
AssertIntEQ(cmd[cmdLenDigest - digestLen - 5], 0x01); /* hierarchy low */
|
|
|
|
/* VERIFIED omits metaAlg: command is 2 bytes shorter and the hierarchy
|
|
* low half sits directly before digest.size. */
|
|
in.checkTicket.tag = TPM_ST_VERIFIED;
|
|
rc = TPM2_PolicyAuthorize(&in);
|
|
AssertIntNE(rc, TPM_RC_SUCCESS);
|
|
|
|
cmd = ctx.cmdBuf;
|
|
cmdLenVerified = ((word32)cmd[2] << 24) | ((word32)cmd[3] << 16) |
|
|
((word32)cmd[4] << 8) | (word32)cmd[5];
|
|
|
|
AssertIntGT(cmdLenVerified, digestLen + 6);
|
|
AssertIntEQ((int)(cmdLenDigest - cmdLenVerified), 2);
|
|
AssertIntEQ(cmd[cmdLenVerified - digestLen - 4], 0x00);
|
|
AssertIntEQ(cmd[cmdLenVerified - digestLen - 3], 0x01); /* hierarchy low */
|
|
|
|
/* DIGEST_VERIFIED with a NULL hierarchy also omits metaAlg, so the command
|
|
* length matches the VERIFIED case (2 bytes shorter than non-NULL). */
|
|
in.checkTicket.tag = TPM_ST_DIGEST_VERIFIED;
|
|
in.checkTicket.hierarchy = TPM_RH_NULL;
|
|
rc = TPM2_PolicyAuthorize(&in);
|
|
AssertIntNE(rc, TPM_RC_SUCCESS);
|
|
|
|
cmd = ctx.cmdBuf;
|
|
cmdLenNull = ((word32)cmd[2] << 24) | ((word32)cmd[3] << 16) |
|
|
((word32)cmd[4] << 8) | (word32)cmd[5];
|
|
AssertIntEQ((int)cmdLenNull, (int)cmdLenVerified);
|
|
|
|
TPM2_Cleanup(&ctx);
|
|
|
|
if (hadPort)
|
|
setenv("TPM2_SWTPM_PORT", savedPort, 1);
|
|
else
|
|
unsetenv("TPM2_SWTPM_PORT");
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n",
|
|
"PolicyAuthorize DIGEST_VERIFIED metaAlg:");
|
|
#else
|
|
printf("Test TPM Wrapper: %-40s Skipped (requires SWTPM)\n",
|
|
"PolicyAuthorize DIGEST_VERIFIED metaAlg:");
|
|
#endif /* WOLFTPM_SWTPM && !NO_GETENV */
|
|
}
|
|
#endif /* WOLFTPM_MLDSA_VERIFY */
|
|
|
|
/* TPM2_Packet_ParsePoint must resync to outerStart + point->size so a
|
|
* malformed wire blob with inner x.size / y.size disagreement can't
|
|
* desynchronize subsequent fields. */
|
|
static void test_TPM2_ParsePoint_OuterResync(void)
|
|
{
|
|
TPM2_Packet packet;
|
|
byte buf[64];
|
|
TPM2B_ECC_POINT point;
|
|
UINT16 sentinel;
|
|
int outerStart, fakeOuterSz;
|
|
int pos = 0;
|
|
int innerStart;
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
|
|
/* Build TPM2B_ECC_POINT: outer.size + x(2+0) + y(2+0). Declare outer
|
|
* size larger than actual inner consumption (4 bytes). */
|
|
outerStart = pos;
|
|
pos += 2; /* size placeholder */
|
|
innerStart = pos;
|
|
/* x.size = 0 */
|
|
buf[pos++] = 0; buf[pos++] = 0;
|
|
/* y.size = 0 */
|
|
buf[pos++] = 0; buf[pos++] = 0;
|
|
|
|
fakeOuterSz = (pos - innerStart) + 6; /* 6 padding bytes */
|
|
buf[outerStart] = (byte)((fakeOuterSz >> 8) & 0xFF);
|
|
buf[outerStart + 1] = (byte)(fakeOuterSz & 0xFF);
|
|
pos += 6;
|
|
|
|
/* Sentinel U16 right after outer end */
|
|
buf[pos++] = 0xBE;
|
|
buf[pos++] = 0xEF;
|
|
|
|
XMEMSET(&point, 0, sizeof(point));
|
|
packet.buf = buf;
|
|
packet.size = pos;
|
|
packet.pos = 0;
|
|
|
|
TPM2_Packet_ParsePoint(&packet, &point);
|
|
|
|
/* Position must land at outerStart + 2 + outer.size (= 2 + 10 = 12).
|
|
* Read sentinel by hand. */
|
|
AssertIntEQ(packet.pos, 2 + fakeOuterSz);
|
|
sentinel = (UINT16)((buf[packet.pos] << 8) | buf[packet.pos + 1]);
|
|
AssertIntEQ(sentinel, 0xBEEF);
|
|
|
|
printf("Test TPM Wrapper:\tParsePoint outer resync:\tPassed\n");
|
|
}
|
|
|
|
/* TPM2_Packet_ParsePublic must resync the packet position to outerStart +
|
|
* pub->size so a malformed wire blob with inner-size disagreement can't
|
|
* desynchronize subsequent fields. Pre-fix the parser left the position
|
|
* wherever the inner parses ended, drifting from the declared outer size. */
|
|
static void test_TPM2_ParsePublic_OuterResync(void)
|
|
{
|
|
TPM2_Packet packet;
|
|
byte buf[256];
|
|
TPM2B_PUBLIC pub;
|
|
UINT16 sentinel = 0;
|
|
int outerStart, fakeOuterSz;
|
|
int pos = 0;
|
|
int innerStart;
|
|
|
|
/* Build a TPM2B_PUBLIC blob by hand with type=RSA, valid inner fields,
|
|
* but outer.size declared larger than the actual inner consumption. A
|
|
* sentinel is placed at outerStart + 2 + outer.size; only a parser
|
|
* that resyncs to that anchor will read the sentinel correctly. */
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
|
|
outerStart = pos;
|
|
pos += 2; /* size placeholder */
|
|
innerStart = pos;
|
|
/* type = RSA (0x0001) */
|
|
buf[pos++] = 0x00; buf[pos++] = 0x01;
|
|
/* nameAlg = SHA256 (0x000B) */
|
|
buf[pos++] = 0x00; buf[pos++] = 0x0B;
|
|
/* objectAttributes = 0 */
|
|
buf[pos++] = 0; buf[pos++] = 0; buf[pos++] = 0; buf[pos++] = 0;
|
|
/* authPolicy: size=0 */
|
|
buf[pos++] = 0; buf[pos++] = 0;
|
|
/* RSA params: sym.alg=NULL(2), scheme=NULL(2), keyBits=2048(2),
|
|
* exponent=0(4) */
|
|
buf[pos++] = 0x00; buf[pos++] = 0x10; /* TPM_ALG_NULL */
|
|
buf[pos++] = 0x00; buf[pos++] = 0x10; /* scheme NULL */
|
|
buf[pos++] = 0x08; buf[pos++] = 0x00; /* keyBits = 2048 */
|
|
buf[pos++] = 0; buf[pos++] = 0; buf[pos++] = 0; buf[pos++] = 0; /* exp */
|
|
/* unique.size = 0 */
|
|
buf[pos++] = 0; buf[pos++] = 0;
|
|
|
|
/* Declared outer size = actual inner + 8 padding bytes. */
|
|
fakeOuterSz = (pos - innerStart) + 8;
|
|
buf[outerStart] = (byte)((fakeOuterSz >> 8) & 0xFF);
|
|
buf[outerStart + 1] = (byte)(fakeOuterSz & 0xFF);
|
|
/* 8 zero pad bytes */
|
|
pos += 8;
|
|
|
|
/* Sentinel U16 at outerStart + 2 + fakeOuterSz */
|
|
buf[pos++] = 0xCA;
|
|
buf[pos++] = 0xFE;
|
|
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
packet.buf = buf;
|
|
packet.size = pos;
|
|
packet.pos = 0;
|
|
|
|
TPM2_Packet_ParsePublic(&packet, &pub);
|
|
AssertIntEQ(pub.publicArea.type, TPM_ALG_RSA);
|
|
AssertIntEQ(pub.publicArea.nameAlg, TPM_ALG_SHA256);
|
|
|
|
/* Position must land at outerStart + 2 + outer.size; the sentinel U16
|
|
* lives at that offset. Read by hand to avoid pulling in WOLFTPM_LOCAL
|
|
* parser helpers from the test binary. */
|
|
AssertIntEQ(packet.pos, 2 + fakeOuterSz);
|
|
sentinel = (UINT16)((buf[packet.pos] << 8) | buf[packet.pos + 1]);
|
|
AssertIntEQ(sentinel, 0xCAFE);
|
|
|
|
printf("Test TPM Wrapper:\tParsePublic outer resync:\tPassed\n");
|
|
}
|
|
|
|
/* TPM2_ParseAttest must handle TPM_ST_ATTEST_NV_DIGEST (0x801C) and decode
|
|
* TPMS_NV_DIGEST_CERTIFY_INFO. Pre-fix, the switch fell through to default
|
|
* and left out->attested zeroed. */
|
|
static void test_TPM2_ParseAttest_NvDigest(void)
|
|
{
|
|
TPM2B_ATTEST attestBlob;
|
|
TPMS_ATTEST out;
|
|
const byte name[] = {0x00, 0x0B, 0x11, 0x22, 0x33, 0x44}; /* alg + 4 bytes */
|
|
const byte digest[] = {0xAA, 0xBB, 0xCC, 0xDD};
|
|
byte* buf;
|
|
int pos = 0;
|
|
int rc;
|
|
|
|
XMEMSET(&attestBlob, 0, sizeof(attestBlob));
|
|
buf = attestBlob.attestationData;
|
|
|
|
/* magic */
|
|
buf[pos++] = (byte)((TPM_GENERATED_VALUE >> 24) & 0xFF);
|
|
buf[pos++] = (byte)((TPM_GENERATED_VALUE >> 16) & 0xFF);
|
|
buf[pos++] = (byte)((TPM_GENERATED_VALUE >> 8) & 0xFF);
|
|
buf[pos++] = (byte)(TPM_GENERATED_VALUE & 0xFF);
|
|
/* type = TPM_ST_ATTEST_NV_DIGEST (0x801C) */
|
|
buf[pos++] = 0x80; buf[pos++] = 0x1C;
|
|
/* qualifiedSigner: empty */
|
|
buf[pos++] = 0; buf[pos++] = 0;
|
|
/* extraData: empty */
|
|
buf[pos++] = 0; buf[pos++] = 0;
|
|
/* clockInfo: clock(8)+resetCount(4)+restartCount(4)+safe(1) */
|
|
XMEMSET(buf + pos, 0, 17); pos += 17;
|
|
/* firmwareVersion */
|
|
XMEMSET(buf + pos, 0, 8); pos += 8;
|
|
/* TPMS_NV_DIGEST_CERTIFY_INFO: indexName + nvDigest */
|
|
buf[pos++] = 0; buf[pos++] = (byte)sizeof(name);
|
|
XMEMCPY(buf + pos, name, sizeof(name)); pos += sizeof(name);
|
|
buf[pos++] = 0; buf[pos++] = (byte)sizeof(digest);
|
|
XMEMCPY(buf + pos, digest, sizeof(digest)); pos += sizeof(digest);
|
|
|
|
attestBlob.size = (UINT16)pos;
|
|
|
|
XMEMSET(&out, 0, sizeof(out));
|
|
rc = TPM2_ParseAttest(&attestBlob, &out);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
AssertIntEQ(out.magic, TPM_GENERATED_VALUE);
|
|
AssertIntEQ(out.type, 0x801C);
|
|
AssertIntEQ(out.attested.nvDigest.indexName.size, sizeof(name));
|
|
AssertIntEQ(XMEMCMP(out.attested.nvDigest.indexName.name, name,
|
|
sizeof(name)), 0);
|
|
AssertIntEQ(out.attested.nvDigest.nvDigest.size, sizeof(digest));
|
|
AssertIntEQ(XMEMCMP(out.attested.nvDigest.nvDigest.buffer, digest,
|
|
sizeof(digest)), 0);
|
|
|
|
printf("Test TPM Wrapper:\tParseAttest NV_DIGEST:\t\tPassed\n");
|
|
}
|
|
|
|
#if defined(WOLFTPM_MFG_IDENTITY) && \
|
|
!defined(WOLFTPM_ST33) && !defined(WOLFTPM_AUTODETECT)
|
|
/* On non-ST33 targets, omitting the master password must fail closed rather
|
|
* than silently deriving auth from the public sample password. */
|
|
static void test_wolfTPM2_SetIdentityAuth_RequiresPassword(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_HANDLE handle;
|
|
byte pw[16];
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&handle, 0, sizeof(handle));
|
|
XMEMSET(pw, 0x11, sizeof(pw));
|
|
|
|
(void)wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
|
|
/* NULL / zero-length master password is rejected */
|
|
rc = wolfTPM2_SetIdentityAuth(&dev, &handle, NULL, 0);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* NULL handle is rejected */
|
|
rc = wolfTPM2_SetIdentityAuth(&dev, NULL, pw, sizeof(pw));
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper:\tSetIdentityAuth requires password:\tPassed\n");
|
|
}
|
|
#endif /* WOLFTPM_MFG_IDENTITY && !WOLFTPM_ST33 && !WOLFTPM_AUTODETECT */
|
|
|
|
/* wolfTPM2_EccKey_TpmToWolf must right-align coordinates: a spec-valid TPM
|
|
* coordinate with a stripped leading-zero byte (size < field size) would be
|
|
* left-aligned and scaled up, corrupting the imported point. */
|
|
static void test_wolfTPM2_EccKey_TpmToWolf_ShortCoord(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) && \
|
|
defined(HAVE_ECC_KEY_IMPORT) && defined(HAVE_ECC_KEY_EXPORT) && \
|
|
!defined(NO_ECC256)
|
|
int rc;
|
|
ecc_key impKey;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY tpmKey;
|
|
byte xImp[32], yImp[32];
|
|
word32 xImpSz, yImpSz;
|
|
/* Valid P-256 point whose x has a zero MSB, so the spec-valid stripped
|
|
* TPM form (size 31) is shorter than the 32-byte field */
|
|
const byte xRaw[32] = {
|
|
0x00, 0x4d, 0xb3, 0x2d, 0x25, 0x8e, 0x4d, 0xfd,
|
|
0x3f, 0x47, 0xdc, 0x30, 0x9f, 0x36, 0x7a, 0x84,
|
|
0x17, 0x7d, 0x47, 0x71, 0x47, 0x76, 0x5d, 0x04,
|
|
0xd7, 0x11, 0xca, 0x8f, 0xba, 0x92, 0x2f, 0x2c};
|
|
const byte yRaw[32] = {
|
|
0xb3, 0x3d, 0x81, 0xc0, 0x73, 0x66, 0xfd, 0x51,
|
|
0xd5, 0x6f, 0x53, 0xed, 0xac, 0x11, 0x36, 0x40,
|
|
0xb0, 0xb5, 0x23, 0xee, 0x7e, 0x32, 0x99, 0x35,
|
|
0x5e, 0x0d, 0x99, 0xfa, 0xb3, 0x75, 0xc7, 0x57};
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&tpmKey, 0, sizeof(tpmKey));
|
|
tpmKey.pub.publicArea.type = TPM_ALG_ECC;
|
|
tpmKey.pub.publicArea.parameters.eccDetail.curveID = TPM_ECC_NIST_P256;
|
|
tpmKey.pub.publicArea.unique.ecc.x.size = 31; /* leading zero stripped */
|
|
XMEMCPY(tpmKey.pub.publicArea.unique.ecc.x.buffer, xRaw + 1, 31);
|
|
tpmKey.pub.publicArea.unique.ecc.y.size = 32;
|
|
XMEMCPY(tpmKey.pub.publicArea.unique.ecc.y.buffer, yRaw, 32);
|
|
|
|
AssertIntEQ(0, wc_ecc_init(&impKey));
|
|
rc = wolfTPM2_EccKey_TpmToWolf(&dev, &tpmKey, &impKey);
|
|
AssertIntEQ(0, rc);
|
|
|
|
/* Imported point must equal the original full-width coordinates */
|
|
xImpSz = sizeof(xImp);
|
|
yImpSz = sizeof(yImp);
|
|
AssertIntEQ(0, wc_ecc_export_public_raw(&impKey, xImp, &xImpSz,
|
|
yImp, &yImpSz));
|
|
AssertIntEQ(0, XMEMCMP(xImp, xRaw, 32));
|
|
AssertIntEQ(0, XMEMCMP(yImp, yRaw, 32));
|
|
|
|
wc_ecc_free(&impKey);
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "EccKey_TpmToWolf short coord:");
|
|
#endif
|
|
}
|
|
|
|
/* wolfTPM2_RsaKey_TpmToWolf must preserve the exponent for multi-byte
|
|
* non-palindromic values. The exponent bytes are big-endian on the wolfCrypt
|
|
* side, so a little-endian build would corrupt e.g. 0x010003. */
|
|
static void test_wolfTPM2_RsaKey_TpmToWolf_Exponent(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_RSA)
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY tpmKey;
|
|
RsaKey wolfKey;
|
|
byte eOut[8];
|
|
byte nOut[256];
|
|
word32 eOutSz = (word32)sizeof(eOut);
|
|
word32 nOutSz = (word32)sizeof(nOut);
|
|
word32 exponent = 0x010003; /* non-palindromic multi-byte exponent */
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&tpmKey, 0, sizeof(tpmKey));
|
|
|
|
tpmKey.pub.publicArea.type = TPM_ALG_RSA;
|
|
tpmKey.pub.publicArea.parameters.rsaDetail.exponent = exponent;
|
|
tpmKey.pub.publicArea.unique.rsa.size = 128;
|
|
XMEMSET(tpmKey.pub.publicArea.unique.rsa.buffer, 0xC7, 128);
|
|
|
|
rc = wc_InitRsaKey(&wolfKey, NULL);
|
|
AssertIntEQ(0, rc);
|
|
|
|
rc = wolfTPM2_RsaKey_TpmToWolf(&dev, &tpmKey, &wolfKey);
|
|
AssertIntEQ(0, rc);
|
|
|
|
rc = wc_RsaFlattenPublicKey(&wolfKey, eOut, &eOutSz, nOut, &nOutSz);
|
|
AssertIntEQ(0, rc);
|
|
|
|
/* Round-trip: decoded exponent must equal the original TPM exponent */
|
|
AssertIntEQ((int)exponent, (int)wolfTPM2_RsaKey_Exponent(eOut, eOutSz));
|
|
|
|
wc_FreeRsaKey(&wolfKey);
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "RsaKey_TpmToWolf exponent:");
|
|
#endif
|
|
}
|
|
|
|
/* The ECDH shared-secret copy must reject a TPM response x-coordinate larger
|
|
* than the caller's output buffer. TPM2_Packet_ParseEccPoint clamps only to
|
|
* MAX_ECC_KEY_BYTES, so a MITM/crafted response can report a point.x.size
|
|
* bigger than a curve-sized caller buffer; copying it would overflow. */
|
|
static void test_wolfTPM2_EccZToBuffer(void)
|
|
{
|
|
int rc;
|
|
int outSz;
|
|
TPM2B_ECC_PARAMETER z;
|
|
byte out[32];
|
|
|
|
XMEMSET(&z, 0, sizeof(z));
|
|
XMEMSET(out, 0, sizeof(out));
|
|
|
|
/* Response larger than caller capacity must be rejected, not copied */
|
|
z.size = (UINT16)(sizeof(out) + 16);
|
|
outSz = (int)sizeof(out);
|
|
rc = wolfTPM2_EccZToBuffer(out, &outSz, &z);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
|
|
/* Exact-fit response is accepted and reports its own size */
|
|
z.size = (UINT16)sizeof(out);
|
|
outSz = (int)sizeof(out);
|
|
rc = wolfTPM2_EccZToBuffer(out, &outSz, &z);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(outSz, (int)sizeof(out));
|
|
|
|
/* Smaller response shrinks outSz to the response size */
|
|
z.size = 20;
|
|
outSz = (int)sizeof(out);
|
|
rc = wolfTPM2_EccZToBuffer(out, &outSz, &z);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(outSz, 20);
|
|
|
|
/* NULL arguments rejected */
|
|
outSz = (int)sizeof(out);
|
|
AssertIntEQ(wolfTPM2_EccZToBuffer(NULL, &outSz, &z), BAD_FUNC_ARG);
|
|
AssertIntEQ(wolfTPM2_EccZToBuffer(out, NULL, &z), BAD_FUNC_ARG);
|
|
AssertIntEQ(wolfTPM2_EccZToBuffer(out, &outSz, NULL), BAD_FUNC_ARG);
|
|
|
|
printf("Test TPM Wrapper:\tECDH shared secret bounds:\tPassed\n");
|
|
}
|
|
|
|
/* wolfTPM2_LoadEccPublicKey_ex must honor caller-provided scheme, hashAlg
|
|
* and objectAttributes (in particular, allow TPMA_OBJECT_decrypt for ECDH
|
|
* peer keys). The legacy wolfTPM2_LoadEccPublicKey must continue to default
|
|
* to ECDSA + sign attribute. */
|
|
static void test_wolfTPM2_LoadEccPublicKey_Ex(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC)
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY srk;
|
|
WOLFTPM2_KEY peer;
|
|
TPMT_PUBLIC pub;
|
|
byte xBuf[MAX_ECC_KEY_BYTES];
|
|
byte yBuf[MAX_ECC_KEY_BYTES];
|
|
word32 xSz, ySz;
|
|
TPM_ECC_CURVE curve;
|
|
TPM_ALG_ID nameAlg;
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&srk, 0, sizeof(srk));
|
|
XMEMSET(&peer, 0, sizeof(peer));
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
if (rc != 0) {
|
|
printf("Test TPM Wrapper:\tLoadEccPublicKey_ex:\tSkipped\n");
|
|
return;
|
|
}
|
|
/* Flush any transient objects left by previous tests so CreatePrimary
|
|
* does not get TPM_RC_OBJECT_MEMORY on a busy simulator. */
|
|
(void)wolfTPM2_UnloadHandles_AllTransient(&dev);
|
|
|
|
/* Create an ECC SRK to harvest valid X/Y coordinates from. The SRK follows
|
|
* WOLFTPM2_ECC_DEFAULT_CURVE, so use its actual curve/nameAlg (not a
|
|
* hardcoded P256) and size the buffers for any curve. */
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
rc = wolfTPM2_GetKeyTemplate_ECC_SRK(&pub);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
rc = wolfTPM2_CreatePrimaryKey(&dev, &srk, TPM_RH_OWNER, &pub, NULL, 0);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
curve = srk.pub.publicArea.parameters.eccDetail.curveID;
|
|
nameAlg = srk.pub.publicArea.nameAlg;
|
|
xSz = srk.pub.publicArea.unique.ecc.x.size;
|
|
ySz = srk.pub.publicArea.unique.ecc.y.size;
|
|
AssertIntGT(xSz, 0);
|
|
AssertIntGT(ySz, 0);
|
|
AssertIntLE(xSz, (word32)sizeof(xBuf));
|
|
AssertIntLE(ySz, (word32)sizeof(yBuf));
|
|
XMEMCPY(xBuf, srk.pub.publicArea.unique.ecc.x.buffer, xSz);
|
|
XMEMCPY(yBuf, srk.pub.publicArea.unique.ecc.y.buffer, ySz);
|
|
|
|
/* Load same coordinates as a peer ECDH key with the decrypt attribute */
|
|
rc = wolfTPM2_LoadEccPublicKey_ex(&dev, &peer, curve,
|
|
xBuf, xSz, yBuf, ySz,
|
|
TPM_ALG_ECDH, nameAlg,
|
|
TPMA_OBJECT_decrypt | TPMA_OBJECT_userWithAuth | TPMA_OBJECT_noDA);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(peer.pub.publicArea.parameters.eccDetail.scheme.scheme,
|
|
TPM_ALG_ECDH);
|
|
AssertIntEQ((int)(peer.pub.publicArea.objectAttributes &
|
|
TPMA_OBJECT_decrypt), (int)TPMA_OBJECT_decrypt);
|
|
AssertIntEQ((int)(peer.pub.publicArea.objectAttributes &
|
|
TPMA_OBJECT_sign), 0);
|
|
wolfTPM2_UnloadHandle(&dev, &peer.handle);
|
|
|
|
/* Legacy wolfTPM2_LoadEccPublicKey: still defaults to ECDSA + sign */
|
|
XMEMSET(&peer, 0, sizeof(peer));
|
|
rc = wolfTPM2_LoadEccPublicKey(&dev, &peer, curve,
|
|
xBuf, xSz, yBuf, ySz);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(peer.pub.publicArea.parameters.eccDetail.scheme.scheme,
|
|
TPM_ALG_ECDSA);
|
|
AssertIntEQ((int)(peer.pub.publicArea.objectAttributes &
|
|
TPMA_OBJECT_sign), (int)TPMA_OBJECT_sign);
|
|
AssertIntEQ((int)(peer.pub.publicArea.objectAttributes &
|
|
TPMA_OBJECT_decrypt), 0);
|
|
wolfTPM2_UnloadHandle(&dev, &peer.handle);
|
|
|
|
wolfTPM2_UnloadHandle(&dev, &srk.handle);
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper:\tLoadEccPublicKey_ex:\t\tPassed\n");
|
|
#endif
|
|
}
|
|
|
|
/* wolfTPM2_GetKeyTemplate_KeyedHash must default scheme to TPM_ALG_NULL
|
|
* when neither isSign nor isDecrypt is set; an HMAC scheme without the
|
|
* sign attribute produces an unusable keyed-hash object. */
|
|
static void test_wolfTPM2_GetKeyTemplate_KeyedHash_Scheme(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT)
|
|
int rc;
|
|
TPMT_PUBLIC tpl;
|
|
|
|
/* Data/seal-style: isSign=0, isDecrypt=0 -> scheme must be NULL */
|
|
XMEMSET(&tpl, 0, sizeof(tpl));
|
|
rc = wolfTPM2_GetKeyTemplate_KeyedHash(&tpl, TPM_ALG_SHA256, 0, 0);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(tpl.parameters.keyedHashDetail.scheme.scheme, TPM_ALG_NULL);
|
|
AssertIntEQ((int)(tpl.objectAttributes & TPMA_OBJECT_sign), 0);
|
|
AssertIntEQ((int)(tpl.objectAttributes & TPMA_OBJECT_decrypt), 0);
|
|
|
|
/* HMAC-style: isSign=1 -> scheme HMAC + hashAlg + sign attribute */
|
|
XMEMSET(&tpl, 0, sizeof(tpl));
|
|
rc = wolfTPM2_GetKeyTemplate_KeyedHash(&tpl, TPM_ALG_SHA256, 1, 0);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(tpl.parameters.keyedHashDetail.scheme.scheme, TPM_ALG_HMAC);
|
|
AssertIntEQ(tpl.parameters.keyedHashDetail.scheme.details.hmac.hashAlg,
|
|
TPM_ALG_SHA256);
|
|
AssertIntEQ((int)(tpl.objectAttributes & TPMA_OBJECT_sign),
|
|
(int)TPMA_OBJECT_sign);
|
|
|
|
printf("Test TPM Wrapper:\tKeyedHash template scheme:\tPassed\n");
|
|
#endif
|
|
}
|
|
|
|
/* wolfTPM2_VerifyHashTicket must apply the same RSA-strict / ECDSA-permissive
|
|
* digest size policy as wolfTPM2_SignHashScheme. The bounds check fires
|
|
* before any TPM call so this test does not require a working TPM. */
|
|
static void test_wolfTPM2_VerifyHashTicket_DigestSize(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_RSA)
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY key;
|
|
byte digest[TPM_MAX_DIGEST_SIZE];
|
|
byte sig[MAX_RSA_KEY_BYTES];
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
XMEMSET(digest, 0xCC, sizeof(digest));
|
|
XMEMSET(sig, 0, sizeof(sig));
|
|
key.handle.hndl = 0x80000000;
|
|
key.pub.publicArea.type = TPM_ALG_RSA;
|
|
|
|
/* SHA-256 digest (32) + hashAlg=SHA512 -> RSA mismatch -> BUFFER_E */
|
|
rc = wolfTPM2_VerifyHashTicket(&dev, &key, sig, 256, digest, 32,
|
|
TPM_ALG_RSASSA, TPM_ALG_SHA512, NULL);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
|
|
/* Oversized digest (64) + hashAlg=SHA256 -> BUFFER_E */
|
|
rc = wolfTPM2_VerifyHashTicket(&dev, &key, sig, 256, digest, 64,
|
|
TPM_ALG_RSASSA, TPM_ALG_SHA256, NULL);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
|
|
/* Negative digestSz -> BUFFER_E */
|
|
rc = wolfTPM2_VerifyHashTicket(&dev, &key, sig, 256, digest, -1,
|
|
TPM_ALG_RSASSA, TPM_ALG_SHA256, NULL);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
|
|
printf("Test TPM Wrapper:\tVerifyHashTicket size:\t\tPassed\n");
|
|
#endif
|
|
}
|
|
|
|
#ifndef WOLFTPM_NO_RETRY
|
|
/* Transparent TPM_RC_RETRY resubmit is configurable. Verify the default seeded
|
|
* by init, the setter/getter round-trip, and rejection of bad arguments. */
|
|
static void test_TPM2_CommandRetries(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
|
|
rc = TPM2_SetCommandRetries(NULL, 1);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = TPM2_GetCommandRetries(NULL);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = TPM2_SetCommandRetries(&dev.ctx, -1);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* retries is seeded before any HAL IO setup, so the default holds even on
|
|
* builds without a default IO callback where init returns an error */
|
|
(void)TPM2_Init_minimal(&dev.ctx);
|
|
AssertIntEQ(TPM2_GetCommandRetries(&dev.ctx), WOLFTPM_MAX_RETRIES);
|
|
|
|
rc = TPM2_SetCommandRetries(&dev.ctx, 0);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(TPM2_GetCommandRetries(&dev.ctx), 0);
|
|
|
|
rc = TPM2_SetCommandRetries(&dev.ctx, 7);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(TPM2_GetCommandRetries(&dev.ctx), 7);
|
|
|
|
TPM2_Cleanup(&dev.ctx);
|
|
|
|
printf("Test TPM Wrapper:\tCommandRetries config:\t\tPassed\n");
|
|
}
|
|
|
|
/* Exercise the TPM_RC_RETRY resubmit bookkeeping directly: header/size restore,
|
|
* command-body preservation, retry-count decrement, and surfacing TPM_RC_RETRY
|
|
* once the count is exhausted - the parts of the loop most likely to regress. */
|
|
static void test_TPM2_Packet_RetryRestore(void)
|
|
{
|
|
TPM2_Packet packet;
|
|
byte buf[TPM2_HEADER_SIZE + 4];
|
|
byte hdr[TPM2_HEADER_SIZE];
|
|
int retries;
|
|
|
|
XMEMSET(hdr, 0xAA, sizeof(hdr));
|
|
XMEMSET(buf, 0x00, sizeof(buf));
|
|
buf[TPM2_HEADER_SIZE] = 0xBB; /* command body byte, must survive a resend */
|
|
packet.buf = buf;
|
|
packet.pos = 0;
|
|
packet.size = TPM2_HEADER_SIZE; /* shrunk by a prior TPM2_Packet_Parse */
|
|
|
|
/* A non-retry response must not resubmit and must leave state untouched */
|
|
retries = 2;
|
|
AssertIntEQ(TPM2_Packet_RetryRestore(TPM_RC_SUCCESS, &retries, &packet,
|
|
hdr, (int)sizeof(buf)), 0);
|
|
AssertIntEQ(retries, 2);
|
|
AssertIntEQ(packet.size, TPM2_HEADER_SIZE);
|
|
|
|
/* RETRY with budget: resubmit, header + size restored, body preserved */
|
|
AssertIntEQ(TPM2_Packet_RetryRestore(TPM_RC_RETRY, &retries, &packet,
|
|
hdr, (int)sizeof(buf)), 1);
|
|
AssertIntEQ(retries, 1);
|
|
AssertIntEQ(packet.size, (int)sizeof(buf));
|
|
AssertIntEQ(buf[0], 0xAA);
|
|
AssertIntEQ(buf[TPM2_HEADER_SIZE], 0xBB);
|
|
|
|
/* Second RETRY exhausts the budget */
|
|
AssertIntEQ(TPM2_Packet_RetryRestore(TPM_RC_RETRY, &retries, &packet,
|
|
hdr, (int)sizeof(buf)), 1);
|
|
AssertIntEQ(retries, 0);
|
|
|
|
/* Budget exhausted: RETRY is surfaced to the caller, no resubmit */
|
|
AssertIntEQ(TPM2_Packet_RetryRestore(TPM_RC_RETRY, &retries, &packet,
|
|
hdr, (int)sizeof(buf)), 0);
|
|
AssertIntEQ(retries, 0);
|
|
|
|
/* NULL guards */
|
|
retries = 1;
|
|
AssertIntEQ(TPM2_Packet_RetryRestore(TPM_RC_RETRY, NULL, &packet,
|
|
hdr, (int)sizeof(buf)), 0);
|
|
AssertIntEQ(TPM2_Packet_RetryRestore(TPM_RC_RETRY, &retries, NULL,
|
|
hdr, (int)sizeof(buf)), 0);
|
|
|
|
printf("Test TPM Wrapper:\tRetryRestore logic:\t\tPassed\n");
|
|
}
|
|
#endif /* !WOLFTPM_NO_RETRY */
|
|
|
|
#if defined(WOLFTPM_FIRMWARE_UPGRADE) && \
|
|
(defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT))
|
|
/* Counts transport calls so the test can prove nothing was transmitted */
|
|
static int test_ovf_ioCalls;
|
|
#ifdef WOLFTPM_ADV_IO
|
|
static int test_ovf_ioCb(TPM2_CTX* ctx, INT32 isRead, UINT32 addr,
|
|
BYTE* xferBuf, UINT16 xferSz, void* userCtx)
|
|
{
|
|
(void)ctx; (void)isRead; (void)addr; (void)xferBuf; (void)xferSz;
|
|
(void)userCtx;
|
|
test_ovf_ioCalls++;
|
|
return TPM_RC_FAILURE;
|
|
}
|
|
#else
|
|
static int test_ovf_ioCb(TPM2_CTX* ctx, const BYTE* txBuf, BYTE* rxBuf,
|
|
UINT16 xferSz, void* userCtx)
|
|
{
|
|
(void)ctx; (void)txBuf; (void)rxBuf; (void)xferSz; (void)userCtx;
|
|
test_ovf_ioCalls++;
|
|
return TPM_RC_FAILURE;
|
|
}
|
|
#endif
|
|
|
|
/* The guard that acts on the flag. TPM2_DispatchCommand and
|
|
* TPM2_TransmitCommand are static, so drive it through a public builder that
|
|
* takes a caller sized payload: TPM2_ST33_FieldUpgradeCommand does
|
|
* Packet_Init + AppendBytes + SendCommand. An oversized payload must return
|
|
* BUFFER_E without reaching the transport; a small one must reach it, which
|
|
* is what proves the guard is selective rather than always-on. */
|
|
static void test_TPM2_DispatchCommand_overflow(void)
|
|
{
|
|
TPM2_CTX ctx;
|
|
byte small[16];
|
|
byte* big;
|
|
word32 bigSz = (word32)XFER_MAX_SIZE + 64;
|
|
int usesIoCb;
|
|
int rc;
|
|
|
|
big = (byte*)XMALLOC(bigSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
AssertNotNull(big);
|
|
XMEMSET(big, 0xA5, bigSz);
|
|
XMEMSET(small, 0x5A, sizeof(small));
|
|
XMEMSET(&ctx, 0, sizeof(ctx));
|
|
|
|
/* timeoutTries 0, so this performs no IO but does set the active ctx. */
|
|
#if defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_SWTPM) || \
|
|
defined(WOLFTPM_WINAPI)
|
|
AssertIntEQ(TPM2_Init_minimal(&ctx), TPM_RC_SUCCESS);
|
|
ctx.ioCb = test_ovf_ioCb;
|
|
#else
|
|
AssertIntEQ(TPM2_Init_ex(&ctx, test_ovf_ioCb, NULL, 0),
|
|
TPM_RC_SUCCESS);
|
|
#endif
|
|
|
|
/* Control: a payload that fits is not refused here, it goes on to the
|
|
* transport and fails there instead. This is what makes the guard
|
|
* selective rather than always-on. Builds whose transport is not the HAL
|
|
* callback (swtpm socket, /dev/tpm0) leave the counter at zero, so it is
|
|
* only load bearing when this control moved it. */
|
|
test_ovf_ioCalls = 0;
|
|
rc = TPM2_ST33_FieldUpgradeCommand(TPM_CC_FieldUpgradeDataVendor_ST33,
|
|
small, (word32)sizeof(small));
|
|
AssertIntNE(rc, BUFFER_E);
|
|
usesIoCb = (test_ovf_ioCalls > 0);
|
|
|
|
/* Too large for ctx->cmdBuf: refused before anything is transmitted */
|
|
test_ovf_ioCalls = 0;
|
|
rc = TPM2_ST33_FieldUpgradeCommand(TPM_CC_FieldUpgradeDataVendor_ST33,
|
|
big, bigSz);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
if (usesIoCb) {
|
|
AssertIntEQ(test_ovf_ioCalls, 0);
|
|
}
|
|
|
|
XFREE(big, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
TPM2_Cleanup(&ctx);
|
|
|
|
printf("Test TPM Wrapper:\tDispatch overflow guard:\tPassed\n");
|
|
}
|
|
#endif /* WOLFTPM_FIRMWARE_UPGRADE && (WOLFTPM_ST33 || WOLFTPM_AUTODETECT) */
|
|
|
|
/* Vendor string chunks used to be appended at the current string length, so a
|
|
* chunk starting with a zero byte left the length at zero and the next chunk
|
|
* overwrote it - a part reporting a binary vendor string kept only its last
|
|
* chunk. Each chunk must land at the offset its property owns, whatever the
|
|
* bytes are and whatever order the properties arrive in. */
|
|
static void test_wolfTPM2_ParseCapabilities_vendorStr(void)
|
|
{
|
|
WOLFTPM2_CAPS caps;
|
|
TPML_TAGGED_TPM_PROPERTY props;
|
|
static const byte expected[16] = {
|
|
0x00, 0x01, 0x01, 0x02, /* leading zero, used to be overwritten */
|
|
0x00, 0x02, 0x01, 0x02, /* leading zero */
|
|
0x41, 0x42, 0x43, 0x44, /* "ABCD" */
|
|
0x45, 0x00, 0x00, 0x00 /* "E" plus embedded terminator */
|
|
};
|
|
static const UINT32 vals[4] = {
|
|
0x00010102, 0x00020102, 0x41424344, 0x45000000
|
|
};
|
|
int i, pass;
|
|
|
|
for (pass = 0; pass < 2; pass++) {
|
|
XMEMSET(&caps, 0, sizeof(caps));
|
|
XMEMSET(&props, 0, sizeof(props));
|
|
props.count = 4;
|
|
for (i = 0; i < 4; i++) {
|
|
/* second pass delivers the same chunks in reverse, since the
|
|
* offset has to come from the property and not the array index */
|
|
int src = (pass == 0) ? i : (3 - i);
|
|
props.tpmProperty[i].property =
|
|
(TPM_PT)(TPM_PT_VENDOR_STRING_1 + src);
|
|
props.tpmProperty[i].value = vals[src];
|
|
}
|
|
|
|
AssertIntEQ(wolfTPM2_ParseCapabilities(&caps, &props), TPM_RC_SUCCESS);
|
|
for (i = 0; i < 16; i++) {
|
|
AssertIntEQ((int)(byte)caps.vendorStr[i], (int)expected[i]);
|
|
}
|
|
/* still terminated, so printing the field stays safe */
|
|
AssertIntEQ((int)caps.vendorStr[16], 0);
|
|
}
|
|
|
|
printf("Test TPM Wrapper:\tVendor string placement:\tPassed\n");
|
|
}
|
|
|
|
/* st33_detect_blob0 decides where the manifest ends and firmware data begins,
|
|
* including in upgrade mode where the TPM cannot be consulted at all, so a
|
|
* false positive splits the image at the wrong byte. */
|
|
static void test_st33_detect_blob0(void)
|
|
{
|
|
byte* buf;
|
|
size_t bufSz = 8192;
|
|
size_t cand[ST33_BLOB0_SIZE_CNT];
|
|
size_t candCnt, i, off, len;
|
|
static const size_t recSz = 500;
|
|
|
|
buf = (byte*)XMALLOC(bufSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
AssertNotNull(buf);
|
|
|
|
/* Version to expected size, grouped by silicon family. ST33TPHF2X spans
|
|
* majors 1, 2 and the older 74 line and always signs with RSA-PSS, so a
|
|
* 74.9 part upgrading to 2.512 must expect the same 321 bytes at both
|
|
* ends. Getting this wrong refuses a legitimate image client side. */
|
|
AssertIntEQ((int)st33_expected_blob0(1, 258), ST33_BLOB0_SIZE_NON_LMS_RSA);
|
|
AssertIntEQ((int)st33_expected_blob0(1, 771), ST33_BLOB0_SIZE_NON_LMS_RSA);
|
|
AssertIntEQ((int)st33_expected_blob0(2, 512), ST33_BLOB0_SIZE_NON_LMS_RSA);
|
|
AssertIntEQ((int)st33_expected_blob0(74, 8), ST33_BLOB0_SIZE_NON_LMS_RSA);
|
|
AssertIntEQ((int)st33_expected_blob0(74, 9), ST33_BLOB0_SIZE_NON_LMS_RSA);
|
|
AssertIntEQ((int)st33_expected_blob0(9, 257), ST33_BLOB0_SIZE_NON_LMS);
|
|
AssertIntEQ((int)st33_expected_blob0(9, 512), ST33_BLOB0_SIZE_LMS);
|
|
AssertIntEQ((int)st33_expected_blob0(10, 512), ST33_BLOB0_SIZE_LMS);
|
|
|
|
/* An unknown family asserts no size rather than guessing one that would
|
|
* reject a valid image (an ST33KTPMQ reports major 11) */
|
|
AssertIntEQ((int)st33_expected_blob0(11, 1), 0);
|
|
AssertIntEQ(st33_blob0_family(11), ST33_BLOB0_FAMILY_UNKNOWN);
|
|
AssertIntEQ(st33_blob0_family(2), ST33_BLOB0_FAMILY_TPHF2X);
|
|
AssertIntEQ(st33_blob0_family(74), ST33_BLOB0_FAMILY_TPHF2X);
|
|
AssertIntEQ(st33_blob0_family(9), ST33_BLOB0_FAMILY_KTPM);
|
|
|
|
/* With no size to prefer, every candidate is still offered exactly once */
|
|
candCnt = st33_blob0_candidates(11, 1, 1, cand);
|
|
AssertIntEQ((int)candCnt, ST33_BLOB0_SIZE_CNT);
|
|
|
|
/* Preferred candidate leads, every size still present exactly once */
|
|
candCnt = st33_blob0_candidates(1, 771, 1, cand);
|
|
AssertIntEQ((int)candCnt, ST33_BLOB0_SIZE_CNT);
|
|
AssertIntEQ((int)cand[0], ST33_BLOB0_SIZE_NON_LMS_RSA);
|
|
candCnt = st33_blob0_candidates(9, 512, 1, cand);
|
|
AssertIntEQ((int)candCnt, ST33_BLOB0_SIZE_CNT);
|
|
AssertIntEQ((int)cand[0], ST33_BLOB0_SIZE_LMS);
|
|
/* No caps (upgrade mode): the fixed order, chain alone decides */
|
|
candCnt = st33_blob0_candidates(0, 0, 0, cand);
|
|
AssertIntEQ((int)candCnt, ST33_BLOB0_SIZE_CNT);
|
|
|
|
/* Exact-fit chain at each known manifest size */
|
|
for (i = 0; i < ST33_BLOB0_SIZE_CNT; i++) {
|
|
XMEMSET(buf, 0xAB, bufSz);
|
|
off = st33_blob0_sizes[i];
|
|
while (off + 3 + recSz <= bufSz) {
|
|
buf[off] = 0x01;
|
|
buf[off + 1] = (byte)(recSz >> 8);
|
|
buf[off + 2] = (byte)(recSz & 0xFF);
|
|
off += 3 + recSz;
|
|
}
|
|
/* final short record lands exactly on the end */
|
|
len = bufSz - off - 3;
|
|
buf[off] = 0xFF;
|
|
buf[off + 1] = (byte)(len >> 8);
|
|
buf[off + 2] = (byte)(len & 0xFF);
|
|
candCnt = st33_blob0_candidates(0, 0, 0, cand);
|
|
AssertIntEQ((int)st33_detect_blob0(buf, bufSz, cand, candCnt),
|
|
(int)st33_blob0_sizes[i]);
|
|
|
|
/* Same buffer one byte short: the chain overshoots, no size fits */
|
|
AssertIntEQ((int)st33_detect_blob0(buf, bufSz - 1, cand, candCnt), 0);
|
|
/* And one byte long: the chain stops short */
|
|
AssertIntEQ((int)st33_detect_blob0(buf, bufSz + 1, cand, candCnt), 0);
|
|
}
|
|
|
|
/* Zero-length record mid-chain is rejected, not walked forever */
|
|
XMEMSET(buf, 0xAB, bufSz);
|
|
off = ST33_BLOB0_SIZE_NON_LMS;
|
|
buf[off] = 0x01; buf[off + 1] = 0; buf[off + 2] = 0;
|
|
candCnt = st33_blob0_candidates(0, 0, 0, cand);
|
|
AssertIntEQ((int)st33_detect_blob0(buf, bufSz, cand, candCnt), 0);
|
|
|
|
/* A file no larger than the candidate is skipped, not read past the end */
|
|
candCnt = st33_blob0_candidates(0, 0, 0, cand);
|
|
AssertIntEQ((int)st33_detect_blob0(buf, ST33_BLOB0_SIZE_NON_LMS_RSA, cand,
|
|
candCnt), 0);
|
|
AssertIntEQ((int)st33_detect_blob0(buf, 0, cand, candCnt), 0);
|
|
|
|
/* Ambiguous image: the chain closes on the last byte from BOTH 177 and
|
|
* 321, so only the caps-preferred candidate may break the tie. */
|
|
bufSz = 2000;
|
|
XMEMSET(buf, 0xAB, bufSz);
|
|
len = bufSz - ST33_BLOB0_SIZE_NON_LMS - 3;
|
|
buf[ST33_BLOB0_SIZE_NON_LMS] = 0x01;
|
|
buf[ST33_BLOB0_SIZE_NON_LMS + 1] = (byte)(len >> 8);
|
|
buf[ST33_BLOB0_SIZE_NON_LMS + 2] = (byte)(len & 0xFF);
|
|
len = bufSz - ST33_BLOB0_SIZE_NON_LMS_RSA - 3;
|
|
buf[ST33_BLOB0_SIZE_NON_LMS_RSA] = 0x01;
|
|
buf[ST33_BLOB0_SIZE_NON_LMS_RSA + 1] = (byte)(len >> 8);
|
|
buf[ST33_BLOB0_SIZE_NON_LMS_RSA + 2] = (byte)(len & 0xFF);
|
|
candCnt = st33_blob0_candidates(1, 771, 1, cand); /* gen 1 -> prefers 321 */
|
|
AssertIntEQ((int)st33_detect_blob0(buf, bufSz, cand, candCnt),
|
|
ST33_BLOB0_SIZE_NON_LMS_RSA);
|
|
candCnt = st33_blob0_candidates(9, 257, 1, cand); /* gen 9 -> prefers 177 */
|
|
AssertIntEQ((int)st33_detect_blob0(buf, bufSz, cand, candCnt),
|
|
ST33_BLOB0_SIZE_NON_LMS);
|
|
|
|
/* NULL guards */
|
|
AssertIntEQ((int)st33_detect_blob0(NULL, bufSz, cand, candCnt), 0);
|
|
AssertIntEQ((int)st33_detect_blob0(buf, bufSz, NULL, candCnt), 0);
|
|
AssertIntEQ((int)st33_blob0_candidates(1, 1, 1, NULL), 0);
|
|
|
|
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
|
|
printf("Test TPM Wrapper:\tST33 blob0 detection:\t\tPassed\n");
|
|
}
|
|
|
|
/* The manifest header carries the firmware line the image upgrades, and that
|
|
* plus the running minor version picks the field upgrade command codes when
|
|
* the TPM's command list cannot settle it. The wrong pair is answered with
|
|
* TPM_RC_COMMAND_CODE and no upgrade happens, so every combination that
|
|
* reaches real parts is pinned here. These are the library's own helpers, so
|
|
* this covers the code that actually drives hardware. */
|
|
#if defined(WOLFTPM_FIRMWARE_UPGRADE) && \
|
|
(defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT))
|
|
static void test_st33_fu_ordinals(void)
|
|
{
|
|
byte blob0[16];
|
|
word16 major, minor;
|
|
TPM_CC ccStart, ccData;
|
|
|
|
/* Header: 00 | major:2 | minor:2, big endian. 2.512 is ST33TPHF2XI2C */
|
|
XMEMSET(blob0, 0, sizeof(blob0));
|
|
blob0[1] = 0x00; blob0[2] = 0x02; blob0[3] = 0x02; blob0[4] = 0x00;
|
|
major = minor = 0xFFFF;
|
|
AssertIntEQ(wolfTPM2_ST33_ManifestVersion(blob0, sizeof(blob0), &major,
|
|
&minor), TPM_RC_SUCCESS);
|
|
AssertIntEQ((int)major, 2);
|
|
AssertIntEQ((int)minor, 512);
|
|
|
|
/* 9.512 is ST33KTPM2X */
|
|
blob0[2] = 0x09;
|
|
AssertIntEQ(wolfTPM2_ST33_ManifestVersion(blob0, sizeof(blob0), &major,
|
|
&minor), TPM_RC_SUCCESS);
|
|
AssertIntEQ((int)major, 9);
|
|
AssertIntEQ((int)minor, 512);
|
|
|
|
/* Either output may be dropped, and a short buffer is refused rather
|
|
* than read past. The version needs offset 1 through 4. */
|
|
AssertIntEQ(wolfTPM2_ST33_ManifestVersion(blob0, sizeof(blob0), NULL,
|
|
NULL), TPM_RC_SUCCESS);
|
|
AssertIntEQ(wolfTPM2_ST33_ManifestVersion(blob0, 5, &major, &minor),
|
|
TPM_RC_SUCCESS);
|
|
AssertIntEQ(wolfTPM2_ST33_ManifestVersion(blob0, 4, &major, &minor),
|
|
BAD_FUNC_ARG);
|
|
AssertIntEQ(wolfTPM2_ST33_ManifestVersion(NULL, sizeof(blob0), &major,
|
|
&minor), BAD_FUNC_ARG);
|
|
|
|
/* Generation 2 images use the standard codes whatever the TPM reports */
|
|
AssertIntEQ(wolfTPM2_ST33_FwUpgradeCommands(512, 1, 2, &ccStart, &ccData),
|
|
TPM_RC_SUCCESS);
|
|
AssertIntEQ((int)ccStart, (int)TPM_CC_FieldUpgradeStart);
|
|
AssertIntEQ((int)ccData, (int)TPM_CC_FieldUpgradeData);
|
|
|
|
/* A running minor version below 256 also uses the standard codes */
|
|
AssertIntEQ(wolfTPM2_ST33_FwUpgradeCommands(8, 1, 74, &ccStart, &ccData),
|
|
TPM_RC_SUCCESS);
|
|
AssertIntEQ((int)ccStart, (int)TPM_CC_FieldUpgradeStart);
|
|
AssertIntEQ((int)ccData, (int)TPM_CC_FieldUpgradeData);
|
|
AssertIntEQ(wolfTPM2_ST33_FwUpgradeCommands(255, 1, 9, &ccStart, &ccData),
|
|
TPM_RC_SUCCESS);
|
|
AssertIntEQ((int)ccStart, (int)TPM_CC_FieldUpgradeStart);
|
|
|
|
/* Everything else is the ST33KTPM vendor pair */
|
|
AssertIntEQ(wolfTPM2_ST33_FwUpgradeCommands(258, 1, 1, &ccStart, &ccData),
|
|
TPM_RC_SUCCESS);
|
|
AssertIntEQ((int)ccStart, (int)TPM_CC_FieldUpgradeStartVendor_ST33);
|
|
AssertIntEQ((int)ccData, (int)TPM_CC_FieldUpgradeDataVendor_ST33);
|
|
AssertIntEQ(wolfTPM2_ST33_FwUpgradeCommands(256, 1, 9, &ccStart, &ccData),
|
|
TPM_RC_SUCCESS);
|
|
AssertIntEQ((int)ccStart, (int)TPM_CC_FieldUpgradeStartVendor_ST33);
|
|
AssertIntEQ(wolfTPM2_ST33_FwUpgradeCommands(512, 1, 10, &ccStart, &ccData),
|
|
TPM_RC_SUCCESS);
|
|
AssertIntEQ((int)ccStart, (int)TPM_CC_FieldUpgradeStartVendor_ST33);
|
|
|
|
/* An ST33KTPMQ at 11.1 is why the version rule is only a fallback: the
|
|
* rule says standard, the part implements only the vendor pair, and the
|
|
* TPM_CAP_COMMANDS probe is what gets it right on hardware. */
|
|
AssertIntEQ(wolfTPM2_ST33_FwUpgradeCommands(1, 1, 11, &ccStart, &ccData),
|
|
TPM_RC_SUCCESS);
|
|
AssertIntEQ((int)ccStart, (int)TPM_CC_FieldUpgradeStart);
|
|
|
|
/* In upgrade mode the running version is unknown, so the manifest alone
|
|
* decides and the stale minor version must not be consulted */
|
|
AssertIntEQ(wolfTPM2_ST33_FwUpgradeCommands(8, 0, 9, &ccStart, &ccData),
|
|
TPM_RC_SUCCESS);
|
|
AssertIntEQ((int)ccStart, (int)TPM_CC_FieldUpgradeStartVendor_ST33);
|
|
AssertIntEQ(wolfTPM2_ST33_FwUpgradeCommands(8, 0, 2, &ccStart, &ccData),
|
|
TPM_RC_SUCCESS);
|
|
AssertIntEQ((int)ccStart, (int)TPM_CC_FieldUpgradeStart);
|
|
|
|
/* NULL outputs are refused rather than dereferenced */
|
|
AssertIntEQ(wolfTPM2_ST33_FwUpgradeCommands(512, 1, 2, NULL, &ccData),
|
|
BAD_FUNC_ARG);
|
|
AssertIntEQ(wolfTPM2_ST33_FwUpgradeCommands(512, 1, 2, &ccStart, NULL),
|
|
BAD_FUNC_ARG);
|
|
|
|
printf("Test TPM Wrapper:\tST33 field upgrade ordinals:\tPassed\n");
|
|
}
|
|
#endif /* WOLFTPM_FIRMWARE_UPGRADE && (WOLFTPM_ST33 || WOLFTPM_AUTODETECT) */
|
|
|
|
/* A sessioned response whose attacker-controlled parameterSize wraps UINT32
|
|
* when added to packet->pos must be rejected up front. Without the bounds
|
|
* check the wrapped authPos passes the "respSz > authPos" guard and the
|
|
* oversized parameterSize flows into TPM2_CalcRpHash as an out-of-bounds
|
|
* read length. The bounds check itself is build-independent. */
|
|
static void test_TPM2_ResponseProcess_ParamSizeOverflow(void)
|
|
{
|
|
TPM2_CTX ctx;
|
|
TPM2_AUTH_SESSION session[1];
|
|
TPM2_Packet packet;
|
|
CmdInfo_t info;
|
|
byte buf[128];
|
|
int rc;
|
|
|
|
XMEMSET(&ctx, 0, sizeof(ctx));
|
|
XMEMSET(session, 0, sizeof(session));
|
|
XMEMSET(&info, 0, sizeof(info));
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
|
|
/* parameterSize field at offset TPM2_HEADER_SIZE: 0xFFFFFFF8 makes
|
|
* authPos = pos + paramSz wrap to a small in-range value */
|
|
buf[TPM2_HEADER_SIZE + 0] = 0xFF;
|
|
buf[TPM2_HEADER_SIZE + 1] = 0xFF;
|
|
buf[TPM2_HEADER_SIZE + 2] = 0xFF;
|
|
buf[TPM2_HEADER_SIZE + 3] = 0xF8;
|
|
|
|
session[0].sessionHandle = HMAC_SESSION_FIRST;
|
|
session[0].authHash = TPM_ALG_SHA256;
|
|
ctx.session = session;
|
|
|
|
info.authCnt = 1;
|
|
info.inHandleCnt = 0;
|
|
info.outHandleCnt = 0;
|
|
info.flags = 0;
|
|
|
|
packet.buf = buf;
|
|
packet.pos = 0;
|
|
packet.size = (int)sizeof(buf);
|
|
|
|
rc = TPM2_ResponseProcess(&ctx, &packet, &info, (TPM_CC)0,
|
|
(UINT32)sizeof(buf));
|
|
AssertIntEQ(rc, TPM_RC_SIZE);
|
|
|
|
/* A valid in-range parameterSize must not be rejected */
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
buf[TPM2_HEADER_SIZE + 3] = 0x04;
|
|
info.authCnt = 0;
|
|
packet.pos = 0;
|
|
packet.size = (int)sizeof(buf);
|
|
|
|
rc = TPM2_ResponseProcess(&ctx, &packet, &info, (TPM_CC)0,
|
|
(UINT32)sizeof(buf));
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* A response too small to hold the parsed parameterSize must be rejected */
|
|
packet.pos = 0;
|
|
packet.size = (int)sizeof(buf);
|
|
rc = TPM2_ResponseProcess(&ctx, &packet, &info, (TPM_CC)0,
|
|
(UINT32)(TPM2_HEADER_SIZE + 2));
|
|
AssertIntEQ(rc, TPM_RC_SIZE);
|
|
|
|
/* An exact-fit parameterSize (paramSz == respSz - pos) must be accepted */
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
buf[TPM2_HEADER_SIZE + 3] = (byte)(sizeof(buf) - (TPM2_HEADER_SIZE + 4));
|
|
packet.pos = 0;
|
|
packet.size = (int)sizeof(buf);
|
|
rc = TPM2_ResponseProcess(&ctx, &packet, &info, (TPM_CC)0,
|
|
(UINT32)sizeof(buf));
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
printf("Test TPM Wrapper:\tResponseProcess paramSize overflow:\tPassed\n");
|
|
}
|
|
|
|
static void test_TPM2_ResponseProcess_DecParamSizeOverflow(void)
|
|
{
|
|
TPM2_CTX ctx;
|
|
TPM2_AUTH_SESSION session[1];
|
|
TPM2_Packet packet;
|
|
CmdInfo_t info;
|
|
byte buf[128];
|
|
int rc;
|
|
|
|
XMEMSET(&ctx, 0, sizeof(ctx));
|
|
XMEMSET(session, 0, sizeof(session));
|
|
XMEMSET(&info, 0, sizeof(info));
|
|
ctx.session = session;
|
|
packet.buf = buf;
|
|
|
|
/* DEC2: first-parameter size larger than the parameter area must be
|
|
* rejected before any decrypt (paramSz=4, decParamSz=0xFFFF) */
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
buf[TPM2_HEADER_SIZE + 3] = 0x04;
|
|
buf[TPM2_HEADER_SIZE + 4] = 0xFF;
|
|
buf[TPM2_HEADER_SIZE + 5] = 0xFF;
|
|
info.authCnt = 0;
|
|
info.flags = CMD_FLAG_DEC2;
|
|
packet.pos = 0;
|
|
packet.size = (int)sizeof(buf);
|
|
rc = TPM2_ResponseProcess(&ctx, &packet, &info, (TPM_CC)0,
|
|
(UINT32)sizeof(buf));
|
|
AssertIntEQ(rc, TPM_RC_SIZE);
|
|
|
|
/* DEC2: paramSz smaller than the size prefix (underflow guard, paramSz=1) */
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
buf[TPM2_HEADER_SIZE + 3] = 0x01;
|
|
info.flags = CMD_FLAG_DEC2;
|
|
packet.pos = 0;
|
|
packet.size = (int)sizeof(buf);
|
|
rc = TPM2_ResponseProcess(&ctx, &packet, &info, (TPM_CC)0,
|
|
(UINT32)sizeof(buf));
|
|
AssertIntEQ(rc, TPM_RC_SIZE);
|
|
|
|
/* DEC2: exact-fit first-parameter (decParamSz == paramSz - 2) is accepted */
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
buf[TPM2_HEADER_SIZE + 3] = 0x04;
|
|
buf[TPM2_HEADER_SIZE + 5] = 0x02;
|
|
info.flags = CMD_FLAG_DEC2;
|
|
packet.pos = 0;
|
|
packet.size = (int)sizeof(buf);
|
|
rc = TPM2_ResponseProcess(&ctx, &packet, &info, (TPM_CC)0,
|
|
(UINT32)sizeof(buf));
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* DEC4: 32-bit first-parameter size overflow is likewise rejected
|
|
* (paramSz=8, decParamSz=0xFFFFFFFF) */
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
buf[TPM2_HEADER_SIZE + 3] = 0x08;
|
|
buf[TPM2_HEADER_SIZE + 4] = 0xFF;
|
|
buf[TPM2_HEADER_SIZE + 5] = 0xFF;
|
|
buf[TPM2_HEADER_SIZE + 6] = 0xFF;
|
|
buf[TPM2_HEADER_SIZE + 7] = 0xFF;
|
|
info.flags = CMD_FLAG_DEC4;
|
|
packet.pos = 0;
|
|
packet.size = (int)sizeof(buf);
|
|
rc = TPM2_ResponseProcess(&ctx, &packet, &info, (TPM_CC)0,
|
|
(UINT32)sizeof(buf));
|
|
AssertIntEQ(rc, TPM_RC_SIZE);
|
|
|
|
printf("Test TPM Wrapper:\tResponseProcess decParamSize overflow:\tPassed\n");
|
|
}
|
|
|
|
static void test_TPM2_ResponseProcess_HmacVerify(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_HMAC)
|
|
TPM2_CTX ctx;
|
|
TPM2_AUTH_SESSION session[1];
|
|
TPM2_Packet packet;
|
|
CmdInfo_t info;
|
|
TPM2B_DIGEST rpHash;
|
|
TPM2B_AUTH expHmac;
|
|
TPM2B_NONCE nonceTPM;
|
|
byte buf[128];
|
|
int rc, i;
|
|
TPM_CC cmdCode = 0x17F;
|
|
UINT16 hmacSz = 32, nonceSz = 32;
|
|
UINT32 paramSz = 4;
|
|
UINT32 pos, hmacOff, respSz;
|
|
byte attr = 0x01;
|
|
|
|
XMEMSET(&ctx, 0, sizeof(ctx));
|
|
XMEMSET(session, 0, sizeof(session));
|
|
XMEMSET(&info, 0, sizeof(info));
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
|
|
/* HMAC session with a known auth value and nonces */
|
|
session[0].sessionHandle = HMAC_SESSION_FIRST;
|
|
session[0].authHash = TPM_ALG_SHA256;
|
|
session[0].auth.size = 4;
|
|
XMEMSET(session[0].auth.buffer, 0xA5, 4);
|
|
session[0].nonceCaller.size = 32;
|
|
XMEMSET(session[0].nonceCaller.buffer, 0x5C, 32);
|
|
session[0].nonceTPM.size = 32;
|
|
XMEMSET(session[0].nonceTPM.buffer, 0xC5, 32);
|
|
ctx.session = session;
|
|
info.authCnt = 1;
|
|
|
|
/* header + paramSize(U32) + params + auth area (nonce, attr, hmac) */
|
|
pos = TPM2_HEADER_SIZE;
|
|
buf[pos++] = 0; buf[pos++] = 0; buf[pos++] = 0; buf[pos++] = (byte)paramSz;
|
|
buf[pos++] = 0xDE; buf[pos++] = 0xAD; buf[pos++] = 0xBE; buf[pos++] = 0xEF;
|
|
buf[pos++] = (byte)(nonceSz >> 8); buf[pos++] = (byte)(nonceSz & 0xFF);
|
|
for (i = 0; i < nonceSz; i++)
|
|
buf[pos++] = 0x99; /* response nonceTPM */
|
|
buf[pos++] = attr; /* sessionAttributes */
|
|
buf[pos++] = (byte)(hmacSz >> 8); buf[pos++] = (byte)(hmacSz & 0xFF);
|
|
hmacOff = pos;
|
|
pos += hmacSz;
|
|
respSz = pos;
|
|
|
|
/* expected HMAC uses the response nonce as nonceTPM */
|
|
nonceTPM.size = nonceSz;
|
|
XMEMSET(nonceTPM.buffer, 0x99, nonceSz);
|
|
rc = TPM2_CalcRpHash(TPM_ALG_SHA256, cmdCode, &buf[TPM2_HEADER_SIZE + 4],
|
|
paramSz, &rpHash);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
XMEMSET(&expHmac, 0, sizeof(expHmac));
|
|
rc = TPM2_CalcHmac(TPM_ALG_SHA256, &session[0].auth, &rpHash,
|
|
&nonceTPM, &session[0].nonceCaller, attr, &expHmac);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
XMEMCPY(&buf[hmacOff], expHmac.buffer, hmacSz);
|
|
|
|
/* untampered response HMAC must verify and update nonceTPM */
|
|
packet.buf = buf; packet.pos = 0; packet.size = (int)respSz;
|
|
rc = TPM2_ResponseProcess(&ctx, &packet, &info, cmdCode, respSz);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(session[0].nonceTPM.buffer[0], 0x99);
|
|
|
|
/* flipping one HMAC byte must be detected */
|
|
buf[hmacOff] ^= 0xFF;
|
|
packet.buf = buf; packet.pos = 0; packet.size = (int)respSz;
|
|
rc = TPM2_ResponseProcess(&ctx, &packet, &info, cmdCode, respSz);
|
|
AssertIntEQ(rc, TPM_RC_HMAC);
|
|
|
|
printf("Test TPM Wrapper:\tResponseProcess HMAC verify:\tPassed\n");
|
|
#endif
|
|
}
|
|
|
|
/* wolfTPM2_NVCreateAuthPolicy must derive nameAlg from authPolicySz so
|
|
* the policy digest hash matches the index's nameAlg. Bug-mode hardcoded
|
|
* SHA-256 nameAlg, which made SHA-384/SHA-512 policies unsatisfiable.
|
|
* Mismatched digest sizes must be rejected up front. */
|
|
static void test_wolfTPM2_NVCreateAuthPolicy_NameAlg(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT)
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_HANDLE parent;
|
|
WOLFTPM2_NV nv;
|
|
byte policy[64];
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&parent, 0, sizeof(parent));
|
|
XMEMSET(&nv, 0, sizeof(nv));
|
|
XMEMSET(policy, 0xAB, sizeof(policy));
|
|
|
|
/* No real TPM call required to exercise the new size validation - the
|
|
* mismatch check fires before TPM2_NV_DefineSpace is contacted. */
|
|
parent.hndl = TPM_RH_OWNER;
|
|
|
|
/* 33 bytes is not a recognized hash digest size -> BAD_FUNC_ARG. */
|
|
rc = wolfTPM2_NVCreateAuthPolicy(&dev, &parent, &nv, 0x01400001,
|
|
TPMA_NV_OWNERWRITE | TPMA_NV_OWNERREAD | TPMA_NV_NO_DA, 64,
|
|
NULL, 0, policy, 33);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* 17 bytes is not a recognized hash digest size -> BAD_FUNC_ARG. */
|
|
rc = wolfTPM2_NVCreateAuthPolicy(&dev, &parent, &nv, 0x01400002,
|
|
TPMA_NV_OWNERWRITE | TPMA_NV_OWNERREAD | TPMA_NV_NO_DA, 64,
|
|
NULL, 0, policy, 17);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
printf("Test TPM Wrapper:\tNVCreateAuthPolicy nameAlg:\tPassed\n");
|
|
#endif
|
|
}
|
|
|
|
/* wolfTPM2_SignHashScheme must reject digest sizes that don't match the
|
|
* declared hashAlg for RSA keys, instead of silently zero-padding. The
|
|
* pad-to-hash-size convention is preserved for ECDSA per spec. */
|
|
static void test_wolfTPM2_SignHashScheme_DigestSize(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_RSA)
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY key;
|
|
byte digest[TPM_MAX_DIGEST_SIZE];
|
|
byte sig[MAX_RSA_KEY_BYTES];
|
|
int sigSz = (int)sizeof(sig);
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
XMEMSET(digest, 0xCC, sizeof(digest));
|
|
key.handle.hndl = 0x80000000;
|
|
key.pub.publicArea.type = TPM_ALG_RSA;
|
|
|
|
/* SHA-256 digest (32) but caller declared SHA-512 (64): for RSA this
|
|
* was previously silently zero-padded; now must return BUFFER_E. */
|
|
rc = wolfTPM2_SignHashScheme(&dev, &key, digest, 32, sig, &sigSz,
|
|
TPM_ALG_RSASSA, TPM_ALG_SHA512);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
|
|
/* Oversized digest (larger than declared hashAlg) is also BUFFER_E. */
|
|
sigSz = (int)sizeof(sig);
|
|
rc = wolfTPM2_SignHashScheme(&dev, &key, digest, 64, sig, &sigSz,
|
|
TPM_ALG_RSASSA, TPM_ALG_SHA256);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
|
|
printf("Test TPM Wrapper:\tSignHashScheme size:\t\tPassed\n");
|
|
#endif
|
|
}
|
|
|
|
/* wolfTPM2_RsaEncrypt and wolfTPM2_RsaDecrypt must reject oversized inputs
|
|
* with BUFFER_E rather than silently truncating to the message buffer
|
|
* length. The bounds check fires before the TPM is contacted, so this
|
|
* test does not require a working TPM connection. */
|
|
static void test_wolfTPM2_RsaEncryptDecrypt_OversizedBufferE(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_RSA)
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY key;
|
|
byte oversized[MAX_RSA_KEY_BYTES + 16];
|
|
byte out[MAX_RSA_KEY_BYTES];
|
|
int outSz = (int)sizeof(out);
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
XMEMSET(oversized, 0xAB, sizeof(oversized));
|
|
key.handle.hndl = 0x80000000;
|
|
|
|
rc = wolfTPM2_RsaEncrypt(&dev, &key, TPM_ALG_NULL,
|
|
oversized, (int)sizeof(oversized), out, &outSz);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
|
|
outSz = (int)sizeof(out);
|
|
rc = wolfTPM2_RsaDecrypt(&dev, &key, TPM_ALG_NULL,
|
|
oversized, (int)sizeof(oversized), out, &outSz);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
|
|
printf("Test TPM Wrapper:\tRsaEncDec oversized:\t\tPassed\n");
|
|
#endif
|
|
}
|
|
|
|
/* Exercise the _ex padding-scheme hash selection (e.g. SHA-512 OAEP). The
|
|
* BAD_FUNC_ARG and BUFFER_E paths fire before the TPM is contacted, so this
|
|
* does not require a working TPM connection. */
|
|
static void test_wolfTPM2_RsaEncryptDecrypt_ex(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_RSA)
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY key;
|
|
byte oversized[MAX_RSA_KEY_BYTES + 16];
|
|
byte out[MAX_RSA_KEY_BYTES];
|
|
int outSz = (int)sizeof(out);
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
XMEMSET(oversized, 0xAB, sizeof(oversized));
|
|
key.handle.hndl = 0x80000000;
|
|
|
|
rc = wolfTPM2_RsaEncrypt_ex(NULL, &key, TPM_ALG_OAEP,
|
|
oversized, 1, out, &outSz, TPM_ALG_SHA512);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
outSz = (int)sizeof(out);
|
|
rc = wolfTPM2_RsaEncrypt_ex(&dev, &key, TPM_ALG_OAEP,
|
|
oversized, (int)sizeof(oversized), out, &outSz, TPM_ALG_SHA512);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
|
|
outSz = (int)sizeof(out);
|
|
rc = wolfTPM2_RsaDecrypt_ex(&dev, &key, TPM_ALG_OAEP,
|
|
oversized, (int)sizeof(oversized), out, &outSz, TPM_ALG_SHA512);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
|
|
#ifndef NO_SHA
|
|
/* OAEP with a hash weaker than the default is rejected */
|
|
if (TPM2_GetHashDigestSize(TPM_ALG_SHA1) <
|
|
TPM2_GetHashDigestSize(WOLFTPM2_WRAP_DIGEST)) {
|
|
outSz = (int)sizeof(out);
|
|
rc = wolfTPM2_RsaEncrypt_ex(&dev, &key, TPM_ALG_OAEP,
|
|
oversized, 1, out, &outSz, TPM_ALG_SHA1);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
}
|
|
#endif
|
|
|
|
printf("Test TPM Wrapper:\tRsaEncDec_ex SHA512:\t\tPassed\n");
|
|
#endif
|
|
}
|
|
|
|
/* Verify the PQC key-template _ex wrappers select the object name algorithm
|
|
* and that the original wrappers keep the default. Pure struct population,
|
|
* no TPM required. */
|
|
static void test_wolfTPM2_GetKeyTemplate_ex_nameAlg(void)
|
|
{
|
|
#if defined(WOLFTPM_PQC) && !defined(WOLFTPM2_NO_WOLFCRYPT)
|
|
int rc;
|
|
TPMT_PUBLIC pub;
|
|
TPMA_OBJECT attr = TPMA_OBJECT_fixedTPM | TPMA_OBJECT_fixedParent |
|
|
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth;
|
|
|
|
#ifdef WOLFTPM_MLDSA
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
rc = wolfTPM2_GetKeyTemplate_MLDSA(&pub, attr, TPM_MLDSA_65, 0);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(pub.nameAlg, WOLFTPM2_WRAP_DIGEST);
|
|
|
|
rc = wolfTPM2_GetKeyTemplate_MLDSA_ex(&pub, attr, TPM_MLDSA_65, 0,
|
|
TPM_ALG_SHA512);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(pub.nameAlg, TPM_ALG_SHA512);
|
|
|
|
rc = wolfTPM2_GetKeyTemplate_MLDSA_ex(&pub, attr, TPM_MLDSA_65, 0,
|
|
TPM_ALG_NULL);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(pub.nameAlg, WOLFTPM2_WRAP_DIGEST);
|
|
#endif
|
|
|
|
#ifdef WOLFTPM_HASH_MLDSA
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
rc = wolfTPM2_GetKeyTemplate_HASH_MLDSA(&pub, attr, TPM_MLDSA_65,
|
|
TPM_ALG_SHA256);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(pub.nameAlg, WOLFTPM2_WRAP_DIGEST);
|
|
|
|
rc = wolfTPM2_GetKeyTemplate_HASH_MLDSA_ex(&pub, attr, TPM_MLDSA_65,
|
|
TPM_ALG_SHA256, TPM_ALG_SHA512);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(pub.nameAlg, TPM_ALG_SHA512);
|
|
#endif
|
|
|
|
#ifdef WOLFTPM_MLKEM
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
rc = wolfTPM2_GetKeyTemplate_MLKEM(&pub, attr, TPM_MLKEM_768);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(pub.nameAlg, WOLFTPM2_WRAP_DIGEST);
|
|
|
|
rc = wolfTPM2_GetKeyTemplate_MLKEM_ex(&pub, attr, TPM_MLKEM_768,
|
|
TPM_ALG_SHA512);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(pub.nameAlg, TPM_ALG_SHA512);
|
|
|
|
#ifndef NO_SHA
|
|
/* a name algorithm weaker than the default is rejected */
|
|
if (TPM2_GetHashDigestSize(TPM_ALG_SHA1) <
|
|
TPM2_GetHashDigestSize(WOLFTPM2_WRAP_DIGEST)) {
|
|
rc = wolfTPM2_GetKeyTemplate_MLKEM_ex(&pub, attr, TPM_MLKEM_768,
|
|
TPM_ALG_SHA1);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
(void)attr;
|
|
(void)rc;
|
|
printf("Test TPM Wrapper:\tGetKeyTemplate _ex nameAlg:\tPassed\n");
|
|
#endif /* WOLFTPM_PQC */
|
|
}
|
|
|
|
/* TPM2_GetTpmCurve / TPM2_GetWolfCurve must map wolfCrypt's
|
|
* ECC_BRAINPOOLP256R1 to TPM_ECC_BP_P256_R1 (0x0030), not
|
|
* TPM_ECC_BN_P256 (0x0010, Barreto-Naehrig). Pre-fix the two were
|
|
* conflated, producing an on-the-wire curve ID that is a different
|
|
* mathematical curve. */
|
|
static void test_TPM2_BrainpoolCurveMapping(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC)
|
|
AssertIntEQ(TPM2_GetTpmCurve(ECC_BRAINPOOLP256R1), TPM_ECC_BP_P256_R1);
|
|
AssertIntEQ(TPM2_GetTpmCurve(ECC_BRAINPOOLP384R1), TPM_ECC_BP_P384_R1);
|
|
AssertIntEQ(TPM2_GetTpmCurve(ECC_BRAINPOOLP512R1), TPM_ECC_BP_P512_R1);
|
|
|
|
AssertIntEQ(TPM2_GetWolfCurve(TPM_ECC_BP_P256_R1), ECC_BRAINPOOLP256R1);
|
|
AssertIntEQ(TPM2_GetWolfCurve(TPM_ECC_BP_P384_R1), ECC_BRAINPOOLP384R1);
|
|
AssertIntEQ(TPM2_GetWolfCurve(TPM_ECC_BP_P512_R1), ECC_BRAINPOOLP512R1);
|
|
|
|
/* TPM_ECC_BN_P256 (Barreto-Naehrig pairing curve) has no wolfCrypt
|
|
* equivalent and must report ECC_CURVE_OID_E rather than aliasing
|
|
* to a Brainpool ID. */
|
|
AssertIntEQ(TPM2_GetWolfCurve(TPM_ECC_BN_P256), ECC_CURVE_OID_E);
|
|
|
|
/* Sanity: NIST mappings still round-trip. */
|
|
AssertIntEQ(TPM2_GetTpmCurve(ECC_SECP256R1), TPM_ECC_NIST_P256);
|
|
AssertIntEQ(TPM2_GetWolfCurve(TPM_ECC_NIST_P256), ECC_SECP256R1);
|
|
|
|
/* TPM2_GetCurveSize must report the correct byte size for the new
|
|
* Brainpool curve IDs (32 / 48 / 64). */
|
|
AssertIntEQ(TPM2_GetCurveSize(TPM_ECC_BP_P256_R1), 32);
|
|
AssertIntEQ(TPM2_GetCurveSize(TPM_ECC_BP_P384_R1), 48);
|
|
AssertIntEQ(TPM2_GetCurveSize(TPM_ECC_BP_P512_R1), 64);
|
|
|
|
/* TPM2_GetCurveHashAlg pairs the digest strength to the curve size,
|
|
* per TCG recommended combinations. */
|
|
AssertIntEQ(TPM2_GetCurveHashAlg(TPM_ECC_NIST_P256), TPM_ALG_SHA256);
|
|
AssertIntEQ(TPM2_GetCurveHashAlg(TPM_ECC_NIST_P384), TPM_ALG_SHA384);
|
|
AssertIntEQ(TPM2_GetCurveHashAlg(TPM_ECC_NIST_P521), TPM_ALG_SHA512);
|
|
AssertIntEQ(TPM2_GetCurveHashAlg(TPM_ECC_BP_P256_R1), TPM_ALG_SHA256);
|
|
AssertIntEQ(TPM2_GetCurveHashAlg(TPM_ECC_BP_P384_R1), TPM_ALG_SHA384);
|
|
AssertIntEQ(TPM2_GetCurveHashAlg(TPM_ECC_BP_P512_R1), TPM_ALG_SHA512); /* 64 -> SHA512 */
|
|
AssertIntEQ(TPM2_GetCurveHashAlg(TPM_ECC_BN_P638), TPM_ALG_SHA512);
|
|
|
|
printf("Test TPM Wrapper:\tBrainpool curve mapping:\tPassed\n");
|
|
#endif
|
|
}
|
|
|
|
/* The no-explicit-curve named templates (SRK, AIK) follow the build's
|
|
* WOLFTPM2_ECC_DEFAULT_CURVE: a no-op in the shipped P256 build, an upgrade
|
|
* when overridden to e.g. P384. The explicit-curve APIs (general ECC/_ex) and
|
|
* the TCG-fixed EK templates honor the exact curve requested (except under
|
|
* NO_ECC256, where P256 is unavailable to every caller - those checks are
|
|
* guarded for that case). */
|
|
static void test_TPM2_EccDefaultCurveTemplate(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC)
|
|
TPMT_PUBLIC t;
|
|
|
|
#ifndef NO_ECC256
|
|
/* Explicit-curve APIs must honor exactly what is passed: an explicit P256
|
|
* request stays P256 even in a WOLFTPM2_ECC_DEFAULT_CURVE override build.
|
|
* This is the regression guard for protocol-bound callers (e.g. ECDH),
|
|
* whose shared secret breaks if the curve is silently remapped. */
|
|
AssertIntEQ(wolfTPM2_GetKeyTemplate_ECC(&t, TPMA_OBJECT_sign,
|
|
TPM_ECC_NIST_P256, TPM_ALG_ECDSA), 0);
|
|
AssertIntEQ(t.parameters.eccDetail.curveID, TPM_ECC_NIST_P256);
|
|
AssertIntEQ(wolfTPM2_GetKeyTemplate_ECC_ex(&t, TPM_ALG_SHA256,
|
|
TPMA_OBJECT_sign, TPM_ECC_NIST_P256, TPM_ALG_ECDSA, TPM_ALG_SHA256), 0);
|
|
AssertIntEQ(t.parameters.eccDetail.curveID, TPM_ECC_NIST_P256);
|
|
#endif
|
|
|
|
/* The storage primary (wolfTPM2_CreateSRK ECC path) follows the default. */
|
|
AssertIntEQ(wolfTPM2_GetKeyTemplate_ECC_SRK(&t), 0);
|
|
AssertIntEQ(t.parameters.eccDetail.curveID, WOLFTPM2_ECC_DEFAULT_CURVE);
|
|
AssertIntEQ(t.nameAlg, TPM2_GetCurveHashAlg(WOLFTPM2_ECC_DEFAULT_CURVE));
|
|
|
|
/* The attestation signing key (AIK) follows the default. This block
|
|
* exercises the sigHash arg of the resolver, so lock in name and sig
|
|
* hash too. */
|
|
AssertIntEQ(wolfTPM2_GetKeyTemplate_ECC_AIK(&t), 0);
|
|
AssertIntEQ(t.parameters.eccDetail.curveID, WOLFTPM2_ECC_DEFAULT_CURVE);
|
|
AssertIntEQ(t.nameAlg, TPM2_GetCurveHashAlg(WOLFTPM2_ECC_DEFAULT_CURVE));
|
|
AssertIntEQ(t.parameters.eccDetail.scheme.details.ecdsa.hashAlg,
|
|
TPM2_GetCurveHashAlg(WOLFTPM2_ECC_DEFAULT_CURVE));
|
|
|
|
#ifndef NO_ECC256
|
|
/* EK P256 NV index is TCG-fixed and must NOT follow the default curve. */
|
|
AssertIntEQ(wolfTPM2_GetKeyTemplate_EKIndex(TPM2_NV_EK_ECC_P256, &t), 0);
|
|
AssertIntEQ(t.parameters.eccDetail.curveID, TPM_ECC_NIST_P256);
|
|
AssertIntEQ(t.nameAlg, TPM_ALG_SHA256);
|
|
AssertIntEQ(t.authPolicy.size, sizeof(TPM_20_EK_AUTH_POLICY));
|
|
#else
|
|
/* Under NO_ECC256 the EK P256 index is substituted to an enabled curve for
|
|
* every caller. Verify the name hash matches the substituted curve and a
|
|
* (non-empty) auth policy was selected for that same hash - i.e. nameAlg
|
|
* and authPolicy stay consistent (regression guard for the prior mismatch
|
|
* where nameAlg became SHA384/SHA512 but the SHA256 policy was copied). */
|
|
AssertIntEQ(wolfTPM2_GetKeyTemplate_EKIndex(TPM2_NV_EK_ECC_P256, &t), 0);
|
|
AssertIntEQ(t.parameters.eccDetail.curveID, WOLFTPM2_ECC_DEFAULT_CURVE);
|
|
AssertIntEQ(t.nameAlg, TPM2_GetCurveHashAlg(WOLFTPM2_ECC_DEFAULT_CURVE));
|
|
AssertIntGT(t.authPolicy.size, 0);
|
|
#endif
|
|
|
|
printf("Test TPM Wrapper:\tECC default-curve template:\tPassed\n");
|
|
#endif
|
|
}
|
|
|
|
static void test_TPM2_KeyedHashScheme_XorSerialize(void)
|
|
{
|
|
TPM2_Packet packet;
|
|
byte buf[64];
|
|
TPMT_KEYEDHASH_SCHEME schemeIn, schemeOut;
|
|
|
|
/* XOR scheme roundtrip: scheme(2) + hashAlg(2) + kdf(2) = 6 bytes */
|
|
XMEMSET(&schemeIn, 0, sizeof(schemeIn));
|
|
schemeIn.scheme = TPM_ALG_XOR;
|
|
schemeIn.details.xorr.hashAlg = TPM_ALG_SHA256;
|
|
schemeIn.details.xorr.kdf = TPM_ALG_KDF1_SP800_108;
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendKeyedHashScheme(&packet, &schemeIn);
|
|
AssertIntEQ(packet.pos, 6);
|
|
|
|
packet.pos = 0;
|
|
XMEMSET(&schemeOut, 0, sizeof(schemeOut));
|
|
TPM2_Packet_ParseKeyedHashScheme(&packet, &schemeOut);
|
|
|
|
AssertIntEQ(schemeOut.scheme, TPM_ALG_XOR);
|
|
AssertIntEQ(schemeOut.details.xorr.hashAlg, TPM_ALG_SHA256);
|
|
AssertIntEQ(schemeOut.details.xorr.kdf, TPM_ALG_KDF1_SP800_108);
|
|
|
|
/* HMAC scheme still works: scheme(2) + hashAlg(2) = 4 bytes */
|
|
XMEMSET(&schemeIn, 0, sizeof(schemeIn));
|
|
schemeIn.scheme = TPM_ALG_HMAC;
|
|
schemeIn.details.hmac.hashAlg = TPM_ALG_SHA384;
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendKeyedHashScheme(&packet, &schemeIn);
|
|
AssertIntEQ(packet.pos, 4);
|
|
|
|
packet.pos = 0;
|
|
XMEMSET(&schemeOut, 0, sizeof(schemeOut));
|
|
TPM2_Packet_ParseKeyedHashScheme(&packet, &schemeOut);
|
|
AssertIntEQ(schemeOut.scheme, TPM_ALG_HMAC);
|
|
AssertIntEQ(schemeOut.details.hmac.hashAlg, TPM_ALG_SHA384);
|
|
|
|
/* NULL scheme: scheme(2) only */
|
|
XMEMSET(&schemeIn, 0, sizeof(schemeIn));
|
|
schemeIn.scheme = TPM_ALG_NULL;
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendKeyedHashScheme(&packet, &schemeIn);
|
|
AssertIntEQ(packet.pos, 2);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "KeyedHashScheme XOR serialize:");
|
|
}
|
|
|
|
static void test_TPM2_Signature_EcSchnorrSm2Serialize(void)
|
|
{
|
|
TPM2_Packet packet;
|
|
byte buf[256];
|
|
TPMT_SIGNATURE sigIn, sigOut;
|
|
const byte rBuf[8] = {0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88};
|
|
const byte sBuf[8] = {0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff,0x00};
|
|
|
|
/* ECSCHNORR: sigAlg(2) + hashAlg(2) + rSz(2) + r(8) + sSz(2) + s(8) = 24 */
|
|
XMEMSET(&sigIn, 0, sizeof(sigIn));
|
|
sigIn.sigAlg = TPM_ALG_ECSCHNORR;
|
|
sigIn.signature.ecdsa.hash = TPM_ALG_SHA256;
|
|
sigIn.signature.ecdsa.signatureR.size = sizeof(rBuf);
|
|
XMEMCPY(sigIn.signature.ecdsa.signatureR.buffer, rBuf, sizeof(rBuf));
|
|
sigIn.signature.ecdsa.signatureS.size = sizeof(sBuf);
|
|
XMEMCPY(sigIn.signature.ecdsa.signatureS.buffer, sBuf, sizeof(sBuf));
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendSignature(&packet, &sigIn);
|
|
AssertIntEQ(packet.pos, 24);
|
|
|
|
packet.pos = 0;
|
|
XMEMSET(&sigOut, 0, sizeof(sigOut));
|
|
TPM2_Packet_ParseSignature(&packet, &sigOut);
|
|
AssertIntEQ(sigOut.sigAlg, TPM_ALG_ECSCHNORR);
|
|
AssertIntEQ(sigOut.signature.ecdsa.hash, TPM_ALG_SHA256);
|
|
AssertIntEQ(sigOut.signature.ecdsa.signatureR.size, sizeof(rBuf));
|
|
AssertIntEQ(XMEMCMP(sigOut.signature.ecdsa.signatureR.buffer,
|
|
rBuf, sizeof(rBuf)), 0);
|
|
AssertIntEQ(sigOut.signature.ecdsa.signatureS.size, sizeof(sBuf));
|
|
AssertIntEQ(XMEMCMP(sigOut.signature.ecdsa.signatureS.buffer,
|
|
sBuf, sizeof(sBuf)), 0);
|
|
|
|
/* SM2: same wire format */
|
|
sigIn.sigAlg = TPM_ALG_SM2;
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendSignature(&packet, &sigIn);
|
|
AssertIntEQ(packet.pos, 24);
|
|
|
|
packet.pos = 0;
|
|
XMEMSET(&sigOut, 0, sizeof(sigOut));
|
|
TPM2_Packet_ParseSignature(&packet, &sigOut);
|
|
AssertIntEQ(sigOut.sigAlg, TPM_ALG_SM2);
|
|
AssertIntEQ(sigOut.signature.ecdsa.signatureR.size, sizeof(rBuf));
|
|
AssertIntEQ(sigOut.signature.ecdsa.signatureS.size, sizeof(sBuf));
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n",
|
|
"Signature ECSCHNORR/SM2 serialize:");
|
|
}
|
|
|
|
static void test_TPM2_Signature_RsaHmacSerialize(void)
|
|
{
|
|
TPM2_Packet packet;
|
|
byte buf[256];
|
|
TPMT_SIGNATURE sigIn, sigOut;
|
|
const byte rsaSig[16] = {
|
|
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
|
|
0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF
|
|
};
|
|
byte hmacDigest[TPM_MAX_DIGEST_SIZE];
|
|
int digestSz;
|
|
int i;
|
|
|
|
/* RSASSA: sigAlg(2) + hash(2) + sigSz(2) + sig(16) = 22 bytes */
|
|
XMEMSET(&sigIn, 0, sizeof(sigIn));
|
|
sigIn.sigAlg = TPM_ALG_RSASSA;
|
|
sigIn.signature.rsassa.hash = TPM_ALG_SHA256;
|
|
sigIn.signature.rsassa.sig.size = sizeof(rsaSig);
|
|
XMEMCPY(sigIn.signature.rsassa.sig.buffer, rsaSig, sizeof(rsaSig));
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendSignature(&packet, &sigIn);
|
|
AssertIntEQ(packet.pos, 22);
|
|
|
|
packet.pos = 0;
|
|
XMEMSET(&sigOut, 0, sizeof(sigOut));
|
|
TPM2_Packet_ParseSignature(&packet, &sigOut);
|
|
AssertIntEQ(sigOut.sigAlg, TPM_ALG_RSASSA);
|
|
AssertIntEQ(sigOut.signature.rsassa.hash, TPM_ALG_SHA256);
|
|
AssertIntEQ(sigOut.signature.rsassa.sig.size, sizeof(rsaSig));
|
|
AssertIntEQ(XMEMCMP(sigOut.signature.rsassa.sig.buffer, rsaSig,
|
|
sizeof(rsaSig)), 0);
|
|
|
|
/* RSAPSS: identical wire format */
|
|
sigIn.sigAlg = TPM_ALG_RSAPSS;
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendSignature(&packet, &sigIn);
|
|
AssertIntEQ(packet.pos, 22);
|
|
|
|
packet.pos = 0;
|
|
XMEMSET(&sigOut, 0, sizeof(sigOut));
|
|
TPM2_Packet_ParseSignature(&packet, &sigOut);
|
|
AssertIntEQ(sigOut.sigAlg, TPM_ALG_RSAPSS);
|
|
AssertIntEQ(sigOut.signature.rsapss.sig.size, sizeof(rsaSig));
|
|
AssertIntEQ(XMEMCMP(sigOut.signature.rsapss.sig.buffer, rsaSig,
|
|
sizeof(rsaSig)), 0);
|
|
|
|
/* HMAC: sigAlg(2) + hashAlg(2) + digest(digestSz), no length prefix -
|
|
* on-wire length derives solely from TPM2_GetHashDigestSize(hashAlg) */
|
|
digestSz = TPM2_GetHashDigestSize(TPM_ALG_SHA256);
|
|
for (i = 0; i < digestSz; i++) {
|
|
hmacDigest[i] = (byte)(0x40 + i);
|
|
}
|
|
XMEMSET(&sigIn, 0, sizeof(sigIn));
|
|
sigIn.sigAlg = TPM_ALG_HMAC;
|
|
sigIn.signature.hmac.hashAlg = TPM_ALG_SHA256;
|
|
XMEMCPY(sigIn.signature.hmac.digest.H, hmacDigest, digestSz);
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendSignature(&packet, &sigIn);
|
|
AssertIntEQ(packet.pos, 4 + digestSz);
|
|
|
|
packet.pos = 0;
|
|
XMEMSET(&sigOut, 0, sizeof(sigOut));
|
|
TPM2_Packet_ParseSignature(&packet, &sigOut);
|
|
AssertIntEQ(sigOut.sigAlg, TPM_ALG_HMAC);
|
|
AssertIntEQ(sigOut.signature.hmac.hashAlg, TPM_ALG_SHA256);
|
|
AssertIntEQ(XMEMCMP(sigOut.signature.hmac.digest.H, hmacDigest,
|
|
digestSz), 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n",
|
|
"Signature RSASSA/RSAPSS/HMAC serialize:");
|
|
}
|
|
|
|
static void test_TPM2_Public_RsaEcc_Roundtrip(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT)
|
|
int rc, sz;
|
|
byte buf[sizeof(TPM2B_PUBLIC)];
|
|
TPM2B_PUBLIC pubIn, pubOut;
|
|
const byte uniqueBytes[8] = {
|
|
0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22
|
|
};
|
|
|
|
/* RSA with AES-128-CFB symmetric wrapper (exercises the AES mode field)
|
|
* and an RSASSA-SHA256 scheme */
|
|
XMEMSET(&pubIn, 0, sizeof(pubIn));
|
|
pubIn.publicArea.type = TPM_ALG_RSA;
|
|
pubIn.publicArea.nameAlg = TPM_ALG_SHA256;
|
|
pubIn.publicArea.objectAttributes = TPMA_OBJECT_sign;
|
|
pubIn.publicArea.parameters.rsaDetail.symmetric.algorithm = TPM_ALG_AES;
|
|
pubIn.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = 128;
|
|
pubIn.publicArea.parameters.rsaDetail.symmetric.mode.aes = TPM_ALG_CFB;
|
|
pubIn.publicArea.parameters.rsaDetail.scheme.scheme = TPM_ALG_RSASSA;
|
|
pubIn.publicArea.parameters.rsaDetail.scheme.details.rsassa.hashAlg =
|
|
TPM_ALG_SHA256;
|
|
pubIn.publicArea.parameters.rsaDetail.keyBits = 2048;
|
|
pubIn.publicArea.parameters.rsaDetail.exponent = 0x10001;
|
|
pubIn.publicArea.unique.rsa.size = sizeof(uniqueBytes);
|
|
XMEMCPY(pubIn.publicArea.unique.rsa.buffer, uniqueBytes,
|
|
sizeof(uniqueBytes));
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
sz = 0;
|
|
rc = TPM2_AppendPublic(buf, (word32)sizeof(buf), &sz, &pubIn);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntGT(sz, 0);
|
|
|
|
XMEMSET(&pubOut, 0, sizeof(pubOut));
|
|
rc = TPM2_ParsePublic(&pubOut, buf, (word32)sz, &sz);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(pubOut.publicArea.type, TPM_ALG_RSA);
|
|
AssertIntEQ(pubOut.publicArea.nameAlg, TPM_ALG_SHA256);
|
|
AssertIntEQ(pubOut.publicArea.parameters.rsaDetail.symmetric.algorithm,
|
|
TPM_ALG_AES);
|
|
AssertIntEQ(pubOut.publicArea.parameters.rsaDetail.symmetric.keyBits.aes,
|
|
128);
|
|
AssertIntEQ(pubOut.publicArea.parameters.rsaDetail.symmetric.mode.aes,
|
|
TPM_ALG_CFB);
|
|
AssertIntEQ(pubOut.publicArea.parameters.rsaDetail.scheme.scheme,
|
|
TPM_ALG_RSASSA);
|
|
AssertIntEQ(
|
|
pubOut.publicArea.parameters.rsaDetail.scheme.details.rsassa.hashAlg,
|
|
TPM_ALG_SHA256);
|
|
AssertIntEQ(pubOut.publicArea.parameters.rsaDetail.keyBits, 2048);
|
|
AssertIntEQ((int)pubOut.publicArea.parameters.rsaDetail.exponent, 0x10001);
|
|
AssertIntEQ(pubOut.publicArea.unique.rsa.size, sizeof(uniqueBytes));
|
|
AssertIntEQ(XMEMCMP(pubOut.publicArea.unique.rsa.buffer, uniqueBytes,
|
|
sizeof(uniqueBytes)), 0);
|
|
|
|
/* ECC P-256 with ECDSA-SHA256 scheme and NULL symmetric/kdf */
|
|
XMEMSET(&pubIn, 0, sizeof(pubIn));
|
|
pubIn.publicArea.type = TPM_ALG_ECC;
|
|
pubIn.publicArea.nameAlg = TPM_ALG_SHA256;
|
|
pubIn.publicArea.objectAttributes = TPMA_OBJECT_sign;
|
|
pubIn.publicArea.parameters.eccDetail.symmetric.algorithm = TPM_ALG_NULL;
|
|
pubIn.publicArea.parameters.eccDetail.scheme.scheme = TPM_ALG_ECDSA;
|
|
pubIn.publicArea.parameters.eccDetail.scheme.details.ecdsa.hashAlg =
|
|
TPM_ALG_SHA256;
|
|
pubIn.publicArea.parameters.eccDetail.curveID = TPM_ECC_NIST_P256;
|
|
pubIn.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
|
|
pubIn.publicArea.unique.ecc.x.size = sizeof(uniqueBytes);
|
|
XMEMCPY(pubIn.publicArea.unique.ecc.x.buffer, uniqueBytes,
|
|
sizeof(uniqueBytes));
|
|
pubIn.publicArea.unique.ecc.y.size = sizeof(uniqueBytes);
|
|
XMEMCPY(pubIn.publicArea.unique.ecc.y.buffer, uniqueBytes,
|
|
sizeof(uniqueBytes));
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
sz = 0;
|
|
rc = TPM2_AppendPublic(buf, (word32)sizeof(buf), &sz, &pubIn);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
XMEMSET(&pubOut, 0, sizeof(pubOut));
|
|
rc = TPM2_ParsePublic(&pubOut, buf, (word32)sz, &sz);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(pubOut.publicArea.type, TPM_ALG_ECC);
|
|
AssertIntEQ(pubOut.publicArea.parameters.eccDetail.symmetric.algorithm,
|
|
TPM_ALG_NULL);
|
|
AssertIntEQ(pubOut.publicArea.parameters.eccDetail.scheme.scheme,
|
|
TPM_ALG_ECDSA);
|
|
AssertIntEQ(
|
|
pubOut.publicArea.parameters.eccDetail.scheme.details.ecdsa.hashAlg,
|
|
TPM_ALG_SHA256);
|
|
AssertIntEQ(pubOut.publicArea.parameters.eccDetail.curveID,
|
|
TPM_ECC_NIST_P256);
|
|
AssertIntEQ(pubOut.publicArea.parameters.eccDetail.kdf.scheme,
|
|
TPM_ALG_NULL);
|
|
AssertIntEQ(pubOut.publicArea.unique.ecc.x.size, sizeof(uniqueBytes));
|
|
AssertIntEQ(XMEMCMP(pubOut.publicArea.unique.ecc.x.buffer, uniqueBytes,
|
|
sizeof(uniqueBytes)), 0);
|
|
AssertIntEQ(pubOut.publicArea.unique.ecc.y.size, sizeof(uniqueBytes));
|
|
AssertIntEQ(XMEMCMP(pubOut.publicArea.unique.ecc.y.buffer, uniqueBytes,
|
|
sizeof(uniqueBytes)), 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "Public RSA/ECC roundtrip:");
|
|
#endif
|
|
}
|
|
|
|
static void test_TPM2_Public_KeyedHashSym_Roundtrip(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT)
|
|
int rc, sz;
|
|
byte buf[sizeof(TPM2B_PUBLIC)];
|
|
TPM2B_PUBLIC pubIn, pubOut;
|
|
const byte uniqueBytes[8] = {
|
|
0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22
|
|
};
|
|
|
|
/* KEYEDHASH (the seal/unseal object type) with an HMAC-SHA256 scheme */
|
|
XMEMSET(&pubIn, 0, sizeof(pubIn));
|
|
pubIn.publicArea.type = TPM_ALG_KEYEDHASH;
|
|
pubIn.publicArea.nameAlg = TPM_ALG_SHA256;
|
|
pubIn.publicArea.objectAttributes = TPMA_OBJECT_sign;
|
|
pubIn.publicArea.parameters.keyedHashDetail.scheme.scheme = TPM_ALG_HMAC;
|
|
pubIn.publicArea.parameters.keyedHashDetail.scheme.details.hmac.hashAlg =
|
|
TPM_ALG_SHA256;
|
|
pubIn.publicArea.unique.keyedHash.size = sizeof(uniqueBytes);
|
|
XMEMCPY(pubIn.publicArea.unique.keyedHash.buffer, uniqueBytes,
|
|
sizeof(uniqueBytes));
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
sz = 0;
|
|
rc = TPM2_AppendPublic(buf, (word32)sizeof(buf), &sz, &pubIn);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntGT(sz, 0);
|
|
|
|
XMEMSET(&pubOut, 0, sizeof(pubOut));
|
|
rc = TPM2_ParsePublic(&pubOut, buf, (word32)sz, &sz);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(pubOut.publicArea.type, TPM_ALG_KEYEDHASH);
|
|
AssertIntEQ(pubOut.publicArea.nameAlg, TPM_ALG_SHA256);
|
|
AssertIntEQ(pubOut.publicArea.parameters.keyedHashDetail.scheme.scheme,
|
|
TPM_ALG_HMAC);
|
|
AssertIntEQ(pubOut.publicArea.parameters.keyedHashDetail.scheme.details.hmac
|
|
.hashAlg, TPM_ALG_SHA256);
|
|
AssertIntEQ(pubOut.publicArea.unique.keyedHash.size, sizeof(uniqueBytes));
|
|
AssertIntEQ(XMEMCMP(pubOut.publicArea.unique.keyedHash.buffer, uniqueBytes,
|
|
sizeof(uniqueBytes)), 0);
|
|
|
|
/* SYMCIPHER with an AES-128-CFB key definition */
|
|
XMEMSET(&pubIn, 0, sizeof(pubIn));
|
|
pubIn.publicArea.type = TPM_ALG_SYMCIPHER;
|
|
pubIn.publicArea.nameAlg = TPM_ALG_SHA256;
|
|
pubIn.publicArea.objectAttributes =
|
|
(TPMA_OBJECT_sign | TPMA_OBJECT_decrypt);
|
|
pubIn.publicArea.parameters.symDetail.sym.algorithm = TPM_ALG_AES;
|
|
pubIn.publicArea.parameters.symDetail.sym.keyBits.aes = 128;
|
|
pubIn.publicArea.parameters.symDetail.sym.mode.aes = TPM_ALG_CFB;
|
|
pubIn.publicArea.unique.sym.size = sizeof(uniqueBytes);
|
|
XMEMCPY(pubIn.publicArea.unique.sym.buffer, uniqueBytes,
|
|
sizeof(uniqueBytes));
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
sz = 0;
|
|
rc = TPM2_AppendPublic(buf, (word32)sizeof(buf), &sz, &pubIn);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
XMEMSET(&pubOut, 0, sizeof(pubOut));
|
|
rc = TPM2_ParsePublic(&pubOut, buf, (word32)sz, &sz);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(pubOut.publicArea.type, TPM_ALG_SYMCIPHER);
|
|
AssertIntEQ(pubOut.publicArea.parameters.symDetail.sym.algorithm,
|
|
TPM_ALG_AES);
|
|
AssertIntEQ(pubOut.publicArea.parameters.symDetail.sym.keyBits.aes, 128);
|
|
AssertIntEQ(pubOut.publicArea.parameters.symDetail.sym.mode.aes,
|
|
TPM_ALG_CFB);
|
|
AssertIntEQ(pubOut.publicArea.unique.sym.size, sizeof(uniqueBytes));
|
|
AssertIntEQ(XMEMCMP(pubOut.publicArea.unique.sym.buffer, uniqueBytes,
|
|
sizeof(uniqueBytes)), 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n",
|
|
"Public KEYEDHASH/SYMCIPHER roundtrip:");
|
|
#endif
|
|
}
|
|
|
|
#ifdef WOLFTPM_PQC
|
|
/* Round-trip the v1.85 PQC arms of TPMT_SIGNATURE through the packet
|
|
* marshaler. Pure ML-DSA (Table 217 mldsa arm) is bare TPM2B + bytes —
|
|
* no hash field. Hash-ML-DSA prefixes a hashAlg before the TPM2B. The
|
|
* tests pin the on-wire byte counts to catch any future drift. */
|
|
static void test_TPM2_Signature_PQC_Serialize(void)
|
|
{
|
|
TPM2_Packet packet;
|
|
byte buf[256];
|
|
TPMT_SIGNATURE sigIn, sigOut;
|
|
const byte sigBytes[16] = {
|
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
|
|
};
|
|
|
|
/* Pure ML-DSA: sigAlg(2) + sigSz(2) + sig(16) = 20 bytes. */
|
|
XMEMSET(&sigIn, 0, sizeof(sigIn));
|
|
sigIn.sigAlg = TPM_ALG_MLDSA;
|
|
sigIn.signature.mldsa.size = sizeof(sigBytes);
|
|
XMEMCPY(sigIn.signature.mldsa.buffer, sigBytes, sizeof(sigBytes));
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendSignature(&packet, &sigIn);
|
|
AssertIntEQ(packet.pos, 2 + 2 + (int)sizeof(sigBytes));
|
|
|
|
packet.pos = 0;
|
|
XMEMSET(&sigOut, 0, sizeof(sigOut));
|
|
TPM2_Packet_ParseSignature(&packet, &sigOut);
|
|
AssertIntEQ(sigOut.sigAlg, TPM_ALG_MLDSA);
|
|
AssertIntEQ(sigOut.signature.mldsa.size, sizeof(sigBytes));
|
|
AssertIntEQ(XMEMCMP(sigOut.signature.mldsa.buffer,
|
|
sigBytes, sizeof(sigBytes)), 0);
|
|
|
|
/* Hash-ML-DSA: sigAlg(2) + hash(2) + sigSz(2) + sig(16) = 22 bytes. */
|
|
XMEMSET(&sigIn, 0, sizeof(sigIn));
|
|
sigIn.sigAlg = TPM_ALG_HASH_MLDSA;
|
|
sigIn.signature.hash_mldsa.hash = TPM_ALG_SHA256;
|
|
sigIn.signature.hash_mldsa.signature.size = sizeof(sigBytes);
|
|
XMEMCPY(sigIn.signature.hash_mldsa.signature.buffer,
|
|
sigBytes, sizeof(sigBytes));
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendSignature(&packet, &sigIn);
|
|
AssertIntEQ(packet.pos, 2 + 2 + 2 + (int)sizeof(sigBytes));
|
|
|
|
packet.pos = 0;
|
|
XMEMSET(&sigOut, 0, sizeof(sigOut));
|
|
TPM2_Packet_ParseSignature(&packet, &sigOut);
|
|
AssertIntEQ(sigOut.sigAlg, TPM_ALG_HASH_MLDSA);
|
|
AssertIntEQ(sigOut.signature.hash_mldsa.hash, TPM_ALG_SHA256);
|
|
AssertIntEQ(sigOut.signature.hash_mldsa.signature.size, sizeof(sigBytes));
|
|
AssertIntEQ(XMEMCMP(sigOut.signature.hash_mldsa.signature.buffer,
|
|
sigBytes, sizeof(sigBytes)), 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "Signature PQC serialize:");
|
|
}
|
|
|
|
/* Round-trip the v1.85 PQC arms of TPM2B_PUBLIC through the
|
|
* TPM2_AppendPublic / TPM2_ParsePublic public marshalers. ML-DSA +
|
|
* Hash-ML-DSA share the unique.mldsa arm (Part 2 Table 225 note);
|
|
* ML-KEM has its own unique.mlkem arm. Verifies every round-tripped
|
|
* field for the three key types. */
|
|
static void test_TPM2_Public_PQC_Roundtrip(void)
|
|
{
|
|
int rc, sz;
|
|
/* TPM2_AppendPublic requires the scratch buffer to hold a full
|
|
* TPM2B_PUBLIC; the v1.85 struct grows to fit the largest PQC public
|
|
* key (MLDSA-87 = 2592 bytes). */
|
|
byte buf[sizeof(TPM2B_PUBLIC)];
|
|
TPM2B_PUBLIC pubIn, pubOut;
|
|
const byte uniqueBytes[8] = {
|
|
0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22
|
|
};
|
|
|
|
/* ML-DSA-65 */
|
|
XMEMSET(&pubIn, 0, sizeof(pubIn));
|
|
pubIn.publicArea.type = TPM_ALG_MLDSA;
|
|
pubIn.publicArea.nameAlg = TPM_ALG_SHA256;
|
|
pubIn.publicArea.objectAttributes = TPMA_OBJECT_sign;
|
|
pubIn.publicArea.parameters.mldsaDetail.parameterSet = TPM_MLDSA_65;
|
|
pubIn.publicArea.parameters.mldsaDetail.allowExternalMu = NO;
|
|
pubIn.publicArea.unique.mldsa.size = sizeof(uniqueBytes);
|
|
XMEMCPY(pubIn.publicArea.unique.mldsa.buffer,
|
|
uniqueBytes, sizeof(uniqueBytes));
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
sz = 0;
|
|
rc = TPM2_AppendPublic(buf, (word32)sizeof(buf), &sz, &pubIn);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntGT(sz, 0);
|
|
|
|
XMEMSET(&pubOut, 0, sizeof(pubOut));
|
|
rc = TPM2_ParsePublic(&pubOut, buf, (word32)sz, &sz);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(pubOut.publicArea.type, TPM_ALG_MLDSA);
|
|
AssertIntEQ(pubOut.publicArea.nameAlg, TPM_ALG_SHA256);
|
|
AssertIntEQ(pubOut.publicArea.parameters.mldsaDetail.parameterSet,
|
|
TPM_MLDSA_65);
|
|
AssertIntEQ(pubOut.publicArea.parameters.mldsaDetail.allowExternalMu, NO);
|
|
AssertIntEQ(pubOut.publicArea.unique.mldsa.size, sizeof(uniqueBytes));
|
|
AssertIntEQ(XMEMCMP(pubOut.publicArea.unique.mldsa.buffer,
|
|
uniqueBytes, sizeof(uniqueBytes)), 0);
|
|
|
|
/* Hash-ML-DSA-65 with SHA-256 — shared unique.mldsa arm. */
|
|
XMEMSET(&pubIn, 0, sizeof(pubIn));
|
|
pubIn.publicArea.type = TPM_ALG_HASH_MLDSA;
|
|
pubIn.publicArea.nameAlg = TPM_ALG_SHA256;
|
|
pubIn.publicArea.objectAttributes = TPMA_OBJECT_sign;
|
|
pubIn.publicArea.parameters.hash_mldsaDetail.parameterSet = TPM_MLDSA_65;
|
|
pubIn.publicArea.parameters.hash_mldsaDetail.hashAlg = TPM_ALG_SHA256;
|
|
pubIn.publicArea.unique.mldsa.size = sizeof(uniqueBytes);
|
|
XMEMCPY(pubIn.publicArea.unique.mldsa.buffer,
|
|
uniqueBytes, sizeof(uniqueBytes));
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
sz = 0;
|
|
rc = TPM2_AppendPublic(buf, (word32)sizeof(buf), &sz, &pubIn);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
XMEMSET(&pubOut, 0, sizeof(pubOut));
|
|
rc = TPM2_ParsePublic(&pubOut, buf, (word32)sz, &sz);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(pubOut.publicArea.type, TPM_ALG_HASH_MLDSA);
|
|
AssertIntEQ(pubOut.publicArea.parameters.hash_mldsaDetail.parameterSet,
|
|
TPM_MLDSA_65);
|
|
AssertIntEQ(pubOut.publicArea.parameters.hash_mldsaDetail.hashAlg,
|
|
TPM_ALG_SHA256);
|
|
AssertIntEQ(pubOut.publicArea.unique.mldsa.size, sizeof(uniqueBytes));
|
|
AssertIntEQ(XMEMCMP(pubOut.publicArea.unique.mldsa.buffer,
|
|
uniqueBytes, sizeof(uniqueBytes)), 0);
|
|
|
|
/* ML-KEM-768 — unique.mlkem arm. */
|
|
XMEMSET(&pubIn, 0, sizeof(pubIn));
|
|
pubIn.publicArea.type = TPM_ALG_MLKEM;
|
|
pubIn.publicArea.nameAlg = TPM_ALG_SHA256;
|
|
pubIn.publicArea.objectAttributes = TPMA_OBJECT_decrypt;
|
|
pubIn.publicArea.parameters.mlkemDetail.parameterSet = TPM_MLKEM_768;
|
|
pubIn.publicArea.parameters.mlkemDetail.symmetric.algorithm = TPM_ALG_NULL;
|
|
pubIn.publicArea.unique.mlkem.size = sizeof(uniqueBytes);
|
|
XMEMCPY(pubIn.publicArea.unique.mlkem.buffer,
|
|
uniqueBytes, sizeof(uniqueBytes));
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
sz = 0;
|
|
rc = TPM2_AppendPublic(buf, (word32)sizeof(buf), &sz, &pubIn);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
XMEMSET(&pubOut, 0, sizeof(pubOut));
|
|
rc = TPM2_ParsePublic(&pubOut, buf, (word32)sz, &sz);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(pubOut.publicArea.type, TPM_ALG_MLKEM);
|
|
AssertIntEQ(pubOut.publicArea.parameters.mlkemDetail.parameterSet,
|
|
TPM_MLKEM_768);
|
|
AssertIntEQ(pubOut.publicArea.unique.mlkem.size, sizeof(uniqueBytes));
|
|
AssertIntEQ(XMEMCMP(pubOut.publicArea.unique.mlkem.buffer,
|
|
uniqueBytes, sizeof(uniqueBytes)), 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "Public PQC roundtrip:");
|
|
}
|
|
#endif /* WOLFTPM_PQC */
|
|
|
|
static void test_TPM2_Sensitive_Roundtrip(void)
|
|
{
|
|
TPM2_Packet packet;
|
|
byte buf[512];
|
|
TPM2B_SENSITIVE sensIn, sensOut;
|
|
const byte authBuf[4] = {0x01, 0x02, 0x03, 0x04};
|
|
const byte seedBuf[8] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11};
|
|
const byte rsaPriv[16] = {
|
|
0xde, 0xad, 0xbe, 0xef, 0x11, 0x22, 0x33, 0x44,
|
|
0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc
|
|
};
|
|
|
|
/* RSA sensitive roundtrip */
|
|
XMEMSET(&sensIn, 0, sizeof(sensIn));
|
|
sensIn.sensitiveArea.sensitiveType = TPM_ALG_RSA;
|
|
sensIn.sensitiveArea.authValue.size = sizeof(authBuf);
|
|
XMEMCPY(sensIn.sensitiveArea.authValue.buffer, authBuf, sizeof(authBuf));
|
|
sensIn.sensitiveArea.seedValue.size = sizeof(seedBuf);
|
|
XMEMCPY(sensIn.sensitiveArea.seedValue.buffer, seedBuf, sizeof(seedBuf));
|
|
sensIn.sensitiveArea.sensitive.rsa.size = sizeof(rsaPriv);
|
|
XMEMCPY(sensIn.sensitiveArea.sensitive.rsa.buffer, rsaPriv,
|
|
sizeof(rsaPriv));
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendSensitive(&packet, &sensIn);
|
|
|
|
packet.pos = 0;
|
|
XMEMSET(&sensOut, 0, sizeof(sensOut));
|
|
TPM2_Packet_ParseSensitive(&packet, &sensOut);
|
|
|
|
AssertIntEQ(sensOut.sensitiveArea.sensitiveType, TPM_ALG_RSA);
|
|
AssertIntEQ(sensOut.sensitiveArea.authValue.size, sizeof(authBuf));
|
|
AssertIntEQ(XMEMCMP(sensOut.sensitiveArea.authValue.buffer,
|
|
authBuf, sizeof(authBuf)), 0);
|
|
AssertIntEQ(sensOut.sensitiveArea.seedValue.size, sizeof(seedBuf));
|
|
AssertIntEQ(XMEMCMP(sensOut.sensitiveArea.seedValue.buffer,
|
|
seedBuf, sizeof(seedBuf)), 0);
|
|
AssertIntEQ(sensOut.sensitiveArea.sensitive.rsa.size, sizeof(rsaPriv));
|
|
AssertIntEQ(XMEMCMP(sensOut.sensitiveArea.sensitive.rsa.buffer,
|
|
rsaPriv, sizeof(rsaPriv)), 0);
|
|
|
|
/* ECC sensitive roundtrip */
|
|
XMEMSET(&sensIn, 0, sizeof(sensIn));
|
|
sensIn.sensitiveArea.sensitiveType = TPM_ALG_ECC;
|
|
sensIn.sensitiveArea.sensitive.ecc.size = sizeof(rsaPriv);
|
|
XMEMCPY(sensIn.sensitiveArea.sensitive.ecc.buffer, rsaPriv,
|
|
sizeof(rsaPriv));
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendSensitive(&packet, &sensIn);
|
|
|
|
packet.pos = 0;
|
|
XMEMSET(&sensOut, 0, sizeof(sensOut));
|
|
TPM2_Packet_ParseSensitive(&packet, &sensOut);
|
|
|
|
AssertIntEQ(sensOut.sensitiveArea.sensitiveType, TPM_ALG_ECC);
|
|
AssertIntEQ(sensOut.sensitiveArea.sensitive.ecc.size, sizeof(rsaPriv));
|
|
AssertIntEQ(XMEMCMP(sensOut.sensitiveArea.sensitive.ecc.buffer,
|
|
rsaPriv, sizeof(rsaPriv)), 0);
|
|
|
|
/* KEYEDHASH sensitive roundtrip */
|
|
XMEMSET(&sensIn, 0, sizeof(sensIn));
|
|
sensIn.sensitiveArea.sensitiveType = TPM_ALG_KEYEDHASH;
|
|
sensIn.sensitiveArea.sensitive.bits.size = sizeof(rsaPriv);
|
|
XMEMCPY(sensIn.sensitiveArea.sensitive.bits.buffer, rsaPriv,
|
|
sizeof(rsaPriv));
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendSensitive(&packet, &sensIn);
|
|
|
|
packet.pos = 0;
|
|
XMEMSET(&sensOut, 0, sizeof(sensOut));
|
|
TPM2_Packet_ParseSensitive(&packet, &sensOut);
|
|
|
|
AssertIntEQ(sensOut.sensitiveArea.sensitiveType, TPM_ALG_KEYEDHASH);
|
|
AssertIntEQ(sensOut.sensitiveArea.sensitive.bits.size, sizeof(rsaPriv));
|
|
AssertIntEQ(XMEMCMP(sensOut.sensitiveArea.sensitive.bits.buffer,
|
|
rsaPriv, sizeof(rsaPriv)), 0);
|
|
|
|
/* SYMCIPHER sensitive roundtrip */
|
|
XMEMSET(&sensIn, 0, sizeof(sensIn));
|
|
sensIn.sensitiveArea.sensitiveType = TPM_ALG_SYMCIPHER;
|
|
sensIn.sensitiveArea.sensitive.sym.size = sizeof(rsaPriv);
|
|
XMEMCPY(sensIn.sensitiveArea.sensitive.sym.buffer, rsaPriv,
|
|
sizeof(rsaPriv));
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendSensitive(&packet, &sensIn);
|
|
|
|
packet.pos = 0;
|
|
XMEMSET(&sensOut, 0, sizeof(sensOut));
|
|
TPM2_Packet_ParseSensitive(&packet, &sensOut);
|
|
|
|
AssertIntEQ(sensOut.sensitiveArea.sensitiveType, TPM_ALG_SYMCIPHER);
|
|
AssertIntEQ(sensOut.sensitiveArea.sensitive.sym.size, sizeof(rsaPriv));
|
|
AssertIntEQ(XMEMCMP(sensOut.sensitiveArea.sensitive.sym.buffer,
|
|
rsaPriv, sizeof(rsaPriv)), 0);
|
|
|
|
#ifdef WOLFTPM_PQC
|
|
/* ML-DSA sensitive roundtrip — regression for missing PQC arm in
|
|
* TPM2_Packet_ParseSensitive (would silently drop the private bytes
|
|
* before the parse-side fix). */
|
|
XMEMSET(&sensIn, 0, sizeof(sensIn));
|
|
sensIn.sensitiveArea.sensitiveType = TPM_ALG_MLDSA;
|
|
sensIn.sensitiveArea.sensitive.mldsa.size = sizeof(rsaPriv);
|
|
XMEMCPY(sensIn.sensitiveArea.sensitive.mldsa.buffer, rsaPriv,
|
|
sizeof(rsaPriv));
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendSensitive(&packet, &sensIn);
|
|
|
|
packet.pos = 0;
|
|
XMEMSET(&sensOut, 0, sizeof(sensOut));
|
|
TPM2_Packet_ParseSensitive(&packet, &sensOut);
|
|
|
|
AssertIntEQ(sensOut.sensitiveArea.sensitiveType, TPM_ALG_MLDSA);
|
|
AssertIntEQ(sensOut.sensitiveArea.sensitive.mldsa.size, sizeof(rsaPriv));
|
|
AssertIntEQ(XMEMCMP(sensOut.sensitiveArea.sensitive.mldsa.buffer,
|
|
rsaPriv, sizeof(rsaPriv)), 0);
|
|
|
|
/* HASH_MLDSA shares the .mldsa arm on the wire (TPM2B_PRIVATE_VENDOR_SPECIFIC
|
|
* bounded by MAX_MLDSA_KEY_BYTES) — sensitiveType differs, layout matches. */
|
|
XMEMSET(&sensIn, 0, sizeof(sensIn));
|
|
sensIn.sensitiveArea.sensitiveType = TPM_ALG_HASH_MLDSA;
|
|
sensIn.sensitiveArea.sensitive.mldsa.size = sizeof(rsaPriv);
|
|
XMEMCPY(sensIn.sensitiveArea.sensitive.mldsa.buffer, rsaPriv,
|
|
sizeof(rsaPriv));
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendSensitive(&packet, &sensIn);
|
|
|
|
packet.pos = 0;
|
|
XMEMSET(&sensOut, 0, sizeof(sensOut));
|
|
TPM2_Packet_ParseSensitive(&packet, &sensOut);
|
|
|
|
AssertIntEQ(sensOut.sensitiveArea.sensitiveType, TPM_ALG_HASH_MLDSA);
|
|
AssertIntEQ(sensOut.sensitiveArea.sensitive.mldsa.size, sizeof(rsaPriv));
|
|
AssertIntEQ(XMEMCMP(sensOut.sensitiveArea.sensitive.mldsa.buffer,
|
|
rsaPriv, sizeof(rsaPriv)), 0);
|
|
|
|
/* ML-KEM sensitive roundtrip. */
|
|
XMEMSET(&sensIn, 0, sizeof(sensIn));
|
|
sensIn.sensitiveArea.sensitiveType = TPM_ALG_MLKEM;
|
|
sensIn.sensitiveArea.sensitive.mlkem.size = sizeof(rsaPriv);
|
|
XMEMCPY(sensIn.sensitiveArea.sensitive.mlkem.buffer, rsaPriv,
|
|
sizeof(rsaPriv));
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendSensitive(&packet, &sensIn);
|
|
|
|
packet.pos = 0;
|
|
XMEMSET(&sensOut, 0, sizeof(sensOut));
|
|
TPM2_Packet_ParseSensitive(&packet, &sensOut);
|
|
|
|
AssertIntEQ(sensOut.sensitiveArea.sensitiveType, TPM_ALG_MLKEM);
|
|
AssertIntEQ(sensOut.sensitiveArea.sensitive.mlkem.size, sizeof(rsaPriv));
|
|
AssertIntEQ(XMEMCMP(sensOut.sensitiveArea.sensitive.mlkem.buffer,
|
|
rsaPriv, sizeof(rsaPriv)), 0);
|
|
#endif /* WOLFTPM_PQC */
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "Sensitive roundtrip:");
|
|
}
|
|
|
|
#ifdef WOLFTPM_SPDM
|
|
/* Pin the rxBuf bound in the SPDM I/O callbacks: a response larger than the
|
|
* caller buffer must be rejected even when it fits the local I/O buffer. */
|
|
static void test_wolfTPM2_SPDM_ValidateRspSz(void)
|
|
{
|
|
word32 ioBufSz = MAX_RESPONSE_SIZE;
|
|
word32 rxSz = 256;
|
|
|
|
AssertIntEQ(wolfTPM2_SPDM_ValidateRspSz(rxSz, rxSz, ioBufSz), 0);
|
|
AssertIntEQ(wolfTPM2_SPDM_ValidateRspSz(rxSz + 1, rxSz, ioBufSz), -1);
|
|
AssertIntEQ(wolfTPM2_SPDM_ValidateRspSz(ioBufSz + 1, ioBufSz, ioBufSz), -1);
|
|
|
|
printf("Test TPM2: %-40s Passed\n", "SPDM ValidateRspSz:");
|
|
}
|
|
#endif /* WOLFTPM_SPDM */
|
|
|
|
/* Pin the TIS response-size bounds so a mutation dropping the buffer-bound or
|
|
* the MAX_RESPONSE_SIZE term is caught. */
|
|
static void test_TPM2_TIS_ValidateRspSz(void)
|
|
{
|
|
int packetSize = 1024;
|
|
|
|
AssertIntEQ(TPM2_TIS_ValidateRspSz(TPM2_HEADER_SIZE, packetSize),
|
|
TPM_RC_SUCCESS);
|
|
AssertIntEQ(TPM2_TIS_ValidateRspSz(packetSize, packetSize),
|
|
TPM_RC_SUCCESS);
|
|
|
|
AssertIntEQ(TPM2_TIS_ValidateRspSz(packetSize + 1, packetSize),
|
|
TPM_RC_FAILURE);
|
|
AssertIntEQ(TPM2_TIS_ValidateRspSz(MAX_RESPONSE_SIZE, MAX_RESPONSE_SIZE),
|
|
TPM_RC_SUCCESS);
|
|
AssertIntEQ(TPM2_TIS_ValidateRspSz(MAX_RESPONSE_SIZE + 1,
|
|
MAX_RESPONSE_SIZE), TPM_RC_FAILURE);
|
|
AssertIntEQ(TPM2_TIS_ValidateRspSz(TPM2_HEADER_SIZE - 1, packetSize),
|
|
TPM_RC_FAILURE);
|
|
AssertIntEQ(TPM2_TIS_ValidateRspSz(-1, packetSize),
|
|
TPM_RC_FAILURE);
|
|
|
|
printf("Test TPM2: %-40s Passed\n", "TIS ValidateRspSz:");
|
|
}
|
|
|
|
/* A zero-size TPM2B_PUBLIC must clear publicArea so stale fields from a reused
|
|
* struct cannot survive a parse. */
|
|
static void test_TPM2_ParsePublic_EmptyClears(void)
|
|
{
|
|
TPM2_Packet packet;
|
|
byte buf[8];
|
|
TPM2B_PUBLIC pub;
|
|
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
pub.publicArea.type = TPM_ALG_RSA;
|
|
pub.publicArea.nameAlg = TPM_ALG_SHA256;
|
|
pub.publicArea.objectAttributes = 0xFFFFFFFFUL;
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_ParsePublic(&packet, &pub);
|
|
|
|
AssertIntEQ(pub.size, 0);
|
|
AssertIntEQ(pub.publicArea.type, 0);
|
|
AssertIntEQ(pub.publicArea.nameAlg, 0);
|
|
AssertIntEQ(pub.publicArea.objectAttributes, 0);
|
|
|
|
printf("Test TPM2: %-40s Passed\n", "ParsePublic empty clears:");
|
|
}
|
|
|
|
/* An oversized inner size on a classic arm must be clamped to the arm buffer
|
|
* size, matching the PQC arms, so AppendBytes does not over-read the source.
|
|
* Each arm has its own buffer member so all four are exercised. */
|
|
static void test_TPM2_AppendSensitive_Clamp(void)
|
|
{
|
|
TPM2_Packet packet;
|
|
byte buf[1024];
|
|
TPM2B_SENSITIVE sens;
|
|
word16 rsaCap, eccCap, bitsCap, symCap;
|
|
|
|
rsaCap = (word16)sizeof(sens.sensitiveArea.sensitive.rsa.buffer);
|
|
eccCap = (word16)sizeof(sens.sensitiveArea.sensitive.ecc.buffer);
|
|
bitsCap = (word16)sizeof(sens.sensitiveArea.sensitive.bits.buffer);
|
|
symCap = (word16)sizeof(sens.sensitiveArea.sensitive.sym.buffer);
|
|
|
|
XMEMSET(&sens, 0, sizeof(sens));
|
|
sens.sensitiveArea.sensitiveType = TPM_ALG_RSA;
|
|
sens.sensitiveArea.sensitive.rsa.size = rsaCap + 100;
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
TPM2_Packet_AppendSensitive(&packet, &sens);
|
|
AssertIntEQ(sens.sensitiveArea.sensitive.rsa.size, rsaCap);
|
|
|
|
XMEMSET(&sens, 0, sizeof(sens));
|
|
sens.sensitiveArea.sensitiveType = TPM_ALG_ECC;
|
|
sens.sensitiveArea.sensitive.ecc.size = eccCap + 100;
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
TPM2_Packet_AppendSensitive(&packet, &sens);
|
|
AssertIntEQ(sens.sensitiveArea.sensitive.ecc.size, eccCap);
|
|
|
|
XMEMSET(&sens, 0, sizeof(sens));
|
|
sens.sensitiveArea.sensitiveType = TPM_ALG_KEYEDHASH;
|
|
sens.sensitiveArea.sensitive.bits.size = bitsCap + 100;
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
TPM2_Packet_AppendSensitive(&packet, &sens);
|
|
AssertIntEQ(sens.sensitiveArea.sensitive.bits.size, bitsCap);
|
|
|
|
XMEMSET(&sens, 0, sizeof(sens));
|
|
sens.sensitiveArea.sensitiveType = TPM_ALG_SYMCIPHER;
|
|
sens.sensitiveArea.sensitive.sym.size = symCap + 100;
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
TPM2_Packet_AppendSensitive(&packet, &sens);
|
|
AssertIntEQ(sens.sensitiveArea.sensitive.sym.size, symCap);
|
|
|
|
printf("Test TPM2: %-40s Passed\n", "AppendSensitive clamp:");
|
|
}
|
|
|
|
static void test_TPM2_AppendPublic_Clamp(void)
|
|
{
|
|
TPM2_Packet packet;
|
|
byte buf[sizeof(TPM2B_PUBLIC)];
|
|
TPM2B_PUBLIC pub;
|
|
word16 policyCap, rsaCap, eccCap, khCap, symCap;
|
|
#ifdef WOLFTPM_MLDSA
|
|
word16 mldsaCap;
|
|
#endif
|
|
#ifdef WOLFTPM_MLKEM
|
|
word16 mlkemCap;
|
|
#endif
|
|
|
|
policyCap = (word16)sizeof(pub.publicArea.authPolicy.buffer);
|
|
rsaCap = (word16)sizeof(pub.publicArea.unique.rsa.buffer);
|
|
eccCap = (word16)sizeof(pub.publicArea.unique.ecc.x.buffer);
|
|
khCap = (word16)sizeof(pub.publicArea.unique.keyedHash.buffer);
|
|
symCap = (word16)sizeof(pub.publicArea.unique.sym.buffer);
|
|
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
pub.publicArea.type = TPM_ALG_RSA;
|
|
pub.publicArea.nameAlg = TPM_ALG_SHA256;
|
|
pub.publicArea.authPolicy.size = policyCap + 100;
|
|
pub.publicArea.unique.rsa.size = rsaCap + 100;
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
TPM2_Packet_AppendPublic(&packet, &pub);
|
|
AssertIntEQ(pub.publicArea.authPolicy.size, policyCap);
|
|
AssertIntEQ(pub.publicArea.unique.rsa.size, rsaCap);
|
|
|
|
/* ECC point x/y sizes must clamp on append too */
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
pub.publicArea.type = TPM_ALG_ECC;
|
|
pub.publicArea.nameAlg = TPM_ALG_SHA256;
|
|
pub.publicArea.unique.ecc.x.size = eccCap + 100;
|
|
pub.publicArea.unique.ecc.y.size = eccCap + 100;
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
TPM2_Packet_AppendPublic(&packet, &pub);
|
|
AssertIntEQ(pub.publicArea.unique.ecc.x.size, eccCap);
|
|
AssertIntEQ(pub.publicArea.unique.ecc.y.size, eccCap);
|
|
|
|
/* keyedHash unique size must clamp */
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
pub.publicArea.type = TPM_ALG_KEYEDHASH;
|
|
pub.publicArea.nameAlg = TPM_ALG_SHA256;
|
|
pub.publicArea.unique.keyedHash.size = khCap + 100;
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
TPM2_Packet_AppendPublic(&packet, &pub);
|
|
AssertIntEQ(pub.publicArea.unique.keyedHash.size, khCap);
|
|
|
|
/* symcipher unique size must clamp */
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
pub.publicArea.type = TPM_ALG_SYMCIPHER;
|
|
pub.publicArea.nameAlg = TPM_ALG_SHA256;
|
|
pub.publicArea.unique.sym.size = symCap + 100;
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
TPM2_Packet_AppendPublic(&packet, &pub);
|
|
AssertIntEQ(pub.publicArea.unique.sym.size, symCap);
|
|
|
|
#ifdef WOLFTPM_MLDSA
|
|
mldsaCap = (word16)sizeof(pub.publicArea.unique.mldsa.buffer);
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
pub.publicArea.type = TPM_ALG_MLDSA;
|
|
pub.publicArea.nameAlg = TPM_ALG_SHA256;
|
|
pub.publicArea.unique.mldsa.size = mldsaCap + 100;
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
TPM2_Packet_AppendPublic(&packet, &pub);
|
|
AssertIntEQ(pub.publicArea.unique.mldsa.size, mldsaCap);
|
|
#endif
|
|
#ifdef WOLFTPM_MLKEM
|
|
mlkemCap = (word16)sizeof(pub.publicArea.unique.mlkem.buffer);
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
pub.publicArea.type = TPM_ALG_MLKEM;
|
|
pub.publicArea.nameAlg = TPM_ALG_SHA256;
|
|
pub.publicArea.unique.mlkem.size = mlkemCap + 100;
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
TPM2_Packet_AppendPublic(&packet, &pub);
|
|
AssertIntEQ(pub.publicArea.unique.mlkem.size, mlkemCap);
|
|
#endif
|
|
|
|
printf("Test TPM2: %-40s Passed\n", "AppendPublic clamp:");
|
|
}
|
|
|
|
/* Roundtrip a maximum-size inner payload (size == buffer capacity) so the
|
|
* parse-side ParseU16Buf clamp branch is exercised with valid data. */
|
|
static void test_TPM2_Sensitive_MaxRoundtrip(void)
|
|
{
|
|
TPM2_Packet packet;
|
|
byte buf[1024];
|
|
TPM2B_SENSITIVE sensIn, sensOut;
|
|
word16 cap, i;
|
|
|
|
XMEMSET(&sensIn, 0, sizeof(sensIn));
|
|
sensIn.sensitiveArea.sensitiveType = TPM_ALG_RSA;
|
|
cap = (word16)sizeof(sensIn.sensitiveArea.sensitive.rsa.buffer);
|
|
sensIn.sensitiveArea.sensitive.rsa.size = cap;
|
|
for (i = 0; i < cap; i++) {
|
|
sensIn.sensitiveArea.sensitive.rsa.buffer[i] = (byte)(i & 0xFF);
|
|
}
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
XMEMSET(&packet, 0, sizeof(packet));
|
|
packet.buf = buf;
|
|
packet.size = sizeof(buf);
|
|
|
|
TPM2_Packet_AppendSensitive(&packet, &sensIn);
|
|
|
|
packet.pos = 0;
|
|
XMEMSET(&sensOut, 0, sizeof(sensOut));
|
|
TPM2_Packet_ParseSensitive(&packet, &sensOut);
|
|
|
|
AssertIntEQ(sensOut.sensitiveArea.sensitiveType, TPM_ALG_RSA);
|
|
AssertIntEQ(sensOut.sensitiveArea.sensitive.rsa.size, cap);
|
|
AssertIntEQ(XMEMCMP(sensOut.sensitiveArea.sensitive.rsa.buffer,
|
|
sensIn.sensitiveArea.sensitive.rsa.buffer, cap), 0);
|
|
|
|
printf("Test TPM2: %-40s Passed\n", "Sensitive max roundtrip:");
|
|
}
|
|
|
|
static void test_KeySealTemplate(void)
|
|
{
|
|
int rc;
|
|
TPMT_PUBLIC tmpl;
|
|
|
|
rc = wolfTPM2_GetKeyTemplate_KeySeal(&tmpl, TPM_ALG_SHA256);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* Template must include userWithAuth so password-based unseal works */
|
|
AssertIntNE(tmpl.objectAttributes & TPMA_OBJECT_userWithAuth, 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "KeySealTemplate:");
|
|
}
|
|
|
|
/* Test boundary validation for seal size and keyed hash key size.
|
|
* Uses zero-initialized dev intentionally — only testing argument validation,
|
|
* not TPM operations. */
|
|
static void test_SealAndKeyedHash_Boundaries(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEYBLOB keyBlob;
|
|
WOLFTPM2_KEY key;
|
|
WOLFTPM2_HANDLE parent;
|
|
TPMT_PUBLIC tmpl;
|
|
byte data[MAX_SYM_DATA + 1];
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&keyBlob, 0, sizeof(keyBlob));
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
XMEMSET(&parent, 0, sizeof(parent));
|
|
XMEMSET(&tmpl, 0, sizeof(tmpl));
|
|
XMEMSET(data, 0xAA, sizeof(data));
|
|
|
|
/* NULL arg checks */
|
|
rc = wolfTPM2_CreateKeySeal_ex(NULL, &keyBlob, &parent, &tmpl,
|
|
NULL, 0, TPM_ALG_NULL, NULL, 0, data, 1);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* sealSize = MAX_SYM_DATA+1 (129) must be rejected */
|
|
rc = wolfTPM2_CreateKeySeal_ex(&dev, &keyBlob, &parent, &tmpl,
|
|
NULL, 0, TPM_ALG_NULL, NULL, 0, data, MAX_SYM_DATA + 1);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* sealSize = -1 must be rejected */
|
|
rc = wolfTPM2_CreateKeySeal_ex(&dev, &keyBlob, &parent, &tmpl,
|
|
NULL, 0, TPM_ALG_NULL, NULL, 0, data, -1);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* sealSize > 0 with NULL sealData must be rejected */
|
|
rc = wolfTPM2_CreateKeySeal_ex(&dev, &keyBlob, &parent, &tmpl,
|
|
NULL, 0, TPM_ALG_NULL, NULL, 0, NULL, 1);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* keySz = MAX_SYM_DATA+1 (129) must be rejected */
|
|
rc = wolfTPM2_LoadKeyedHashKey(&dev, &key, &parent,
|
|
TPM_ALG_SHA256, data, MAX_SYM_DATA + 1, NULL, 0);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
|
|
/* keySz = 0 must be rejected */
|
|
rc = wolfTPM2_LoadKeyedHashKey(&dev, &key, &parent,
|
|
TPM_ALG_SHA256, data, 0, NULL, 0);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
|
|
/* NULL keyBuf must be rejected */
|
|
rc = wolfTPM2_LoadKeyedHashKey(&dev, &key, &parent,
|
|
TPM_ALG_SHA256, NULL, MAX_SYM_DATA, NULL, 0);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "SealKeyedHash Boundary:");
|
|
}
|
|
|
|
static void test_GetAlgId(void)
|
|
{
|
|
AssertIntEQ(TPM2_GetAlgId("SHA256"), TPM_ALG_SHA256);
|
|
AssertIntEQ(TPM2_GetAlgId("SHA3_256"), TPM_ALG_SHA3_256);
|
|
AssertIntEQ(TPM2_GetAlgId("SHA3_384"), TPM_ALG_SHA3_384);
|
|
AssertIntEQ(TPM2_GetAlgId("SHA3_512"), TPM_ALG_SHA3_512);
|
|
#ifdef WOLFTPM_PQC
|
|
AssertIntEQ(TPM2_GetAlgId("ML-KEM"), TPM_ALG_MLKEM);
|
|
AssertIntEQ(TPM2_GetAlgId("ML-DSA"), TPM_ALG_MLDSA);
|
|
AssertIntEQ(TPM2_GetAlgId("HashML-DSA"), TPM_ALG_HASH_MLDSA);
|
|
#endif
|
|
AssertIntEQ(TPM2_GetAlgId("not_a_real_alg"), TPM_ALG_ERROR);
|
|
}
|
|
|
|
static void test_wolfTPM2_CSR(void)
|
|
{
|
|
#if defined(WOLFTPM2_CERT_GEN) && !defined(WOLFTPM2_NO_HEAP) && \
|
|
defined(WOLFTPM_CRYPTOCB)
|
|
int rc;
|
|
WOLFTPM2_CSR* csr = wolfTPM2_NewCSR();
|
|
AssertNotNull(csr);
|
|
|
|
/* invalid cases */
|
|
rc = wolfTPM2_CSR_SetSubject(NULL, NULL, NULL);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_CSR_SetSubject(NULL, csr, NULL);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* valid, but empty DH strings */
|
|
rc = wolfTPM2_CSR_SetSubject(NULL, csr, ""); /* test no slash */
|
|
AssertIntEQ(rc, 0);
|
|
rc = wolfTPM2_CSR_SetSubject(NULL, csr, "/C=/CN="); /* test blank value */
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* valid string */
|
|
rc = wolfTPM2_CSR_SetSubject(NULL, csr,
|
|
"/C=US/ST=Oregon/L=Portland/SN=Test/O=wolfSSL"
|
|
"/OU=RSA/CN=www.wolfssl.com/emailAddress=info@wolfssl.com");
|
|
AssertIntEQ(rc, 0);
|
|
|
|
wolfTPM2_FreeCSR(csr);
|
|
|
|
printf("Test TPM Wrapper: %-40s %s\n", "CSR Subject:",
|
|
rc == 0 ? "Passed" : "Failed");
|
|
#endif
|
|
}
|
|
|
|
/* Exercise hash cache growth with small updates and verify the digest. */
|
|
static void test_wolfTPM2_CryptoDevCb_HashCacheStream(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WRAPPER) && defined(WOLFTPM_CRYPTOCB) && \
|
|
!defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFTPM_USE_SYMMETRIC) && \
|
|
defined(WOLFSSL_HASH_FLAGS) && !defined(NO_SHA256)
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
TpmCryptoDevCtx tpmCtx;
|
|
int tpmDevId = INVALID_DEVID;
|
|
wc_Sha256 sha;
|
|
byte digest[TPM_SHA256_DIGEST_SIZE];
|
|
byte digestSw[TPM_SHA256_DIGEST_SIZE];
|
|
byte data[4096];
|
|
word32 i, pos, chunk;
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
XMEMSET(&tpmCtx, 0, sizeof(tpmCtx));
|
|
for (i = 0; i < (word32)sizeof(data); i++) {
|
|
data[i] = (byte)(i * 7 + 1);
|
|
}
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
if (rc != 0) {
|
|
printf("Test TPM Wrapper: %-40s Skipped\n", "CryptoDevCb hash cache:");
|
|
return;
|
|
}
|
|
|
|
tpmCtx.dev = &dev;
|
|
tpmCtx.useSymmetricOnTPM = 1;
|
|
rc = wolfTPM2_SetCryptoDevCb(&dev, wolfTPM2_CryptoDevCb, &tpmCtx,
|
|
&tpmDevId);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Force repeated growth, then a greater-than-2x allocation. */
|
|
rc = wc_InitSha256_ex(&sha, NULL, tpmDevId);
|
|
AssertIntEQ(rc, 0);
|
|
rc = wc_Sha256SetFlags(&sha, WC_HASH_FLAG_WILLCOPY);
|
|
AssertIntEQ(rc, 0);
|
|
pos = 0;
|
|
chunk = 1;
|
|
while (pos < (word32)sizeof(data) - 2048) {
|
|
if (chunk > (word32)sizeof(data) - 2048 - pos)
|
|
chunk = (word32)sizeof(data) - 2048 - pos;
|
|
rc = wc_Sha256Update(&sha, &data[pos], chunk);
|
|
AssertIntEQ(rc, 0);
|
|
pos += chunk;
|
|
chunk = (chunk % 96) + 1;
|
|
}
|
|
rc = wc_Sha256Update(&sha, &data[pos], 2048);
|
|
AssertIntEQ(rc, 0);
|
|
rc = wc_Sha256Final(&sha, digest);
|
|
AssertIntEQ(rc, 0);
|
|
wc_Sha256Free(&sha);
|
|
|
|
rc = wc_InitSha256_ex(&sha, NULL, INVALID_DEVID);
|
|
AssertIntEQ(rc, 0);
|
|
rc = wc_Sha256Update(&sha, data, (word32)sizeof(data));
|
|
AssertIntEQ(rc, 0);
|
|
rc = wc_Sha256Final(&sha, digestSw);
|
|
AssertIntEQ(rc, 0);
|
|
wc_Sha256Free(&sha);
|
|
|
|
AssertIntEQ(XMEMCMP(digest, digestSw, sizeof(digest)), 0);
|
|
|
|
wolfTPM2_ClearCryptoDevCb(&dev, tpmDevId);
|
|
wolfTPM2_Cleanup(&dev);
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "CryptoDevCb hash cache:");
|
|
#else
|
|
printf("Test TPM Wrapper: %-40s Skipped\n", "CryptoDevCb hash cache:");
|
|
#endif
|
|
}
|
|
|
|
static void test_wolfTPM2_CryptoDevCb_EccVerifyOversizedRS(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WRAPPER) && defined(WOLFTPM_CRYPTOCB) && \
|
|
!defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) && \
|
|
defined(HAVE_ECC_VERIFY) && !defined(WC_NO_RNG) && (MAX_ECC_BYTES > 32)
|
|
int rc;
|
|
int i;
|
|
int c, rLen, sLen;
|
|
int verifyRes = 0;
|
|
WOLFTPM2_DEV dev;
|
|
TpmCryptoDevCtx tpmCtx;
|
|
wc_CryptoInfo info;
|
|
ecc_key key;
|
|
byte digest[32];
|
|
byte sig[128];
|
|
word32 sigSz;
|
|
|
|
XMEMSET(digest, 0x33, sizeof(digest));
|
|
XMEMSET(&tpmCtx, 0, sizeof(tpmCtx));
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
tpmCtx.dev = &dev;
|
|
|
|
rc = wc_ecc_init(&key);
|
|
AssertIntEQ(rc, 0);
|
|
rc = wc_ecc_make_key_ex(wolfTPM2_GetRng(&dev), 32, &key, ECC_SECP256R1);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* c==0 drives the oversized-R guard, c==1 the oversized-S guard; both
|
|
* exceed the P-256 key size and must fall back before the TPM key load */
|
|
for (c = 0; c < 2; c++) {
|
|
rLen = (c == 0) ? 40 : 32;
|
|
sLen = (c == 0) ? 32 : 40;
|
|
|
|
sigSz = 0;
|
|
sig[sigSz++] = 0x30;
|
|
sig[sigSz++] = (byte)(2 + rLen + 2 + sLen);
|
|
sig[sigSz++] = 0x02;
|
|
sig[sigSz++] = (byte)rLen;
|
|
for (i = 0; i < rLen; i++)
|
|
sig[sigSz++] = 0x11;
|
|
sig[sigSz++] = 0x02;
|
|
sig[sigSz++] = (byte)sLen;
|
|
for (i = 0; i < sLen; i++)
|
|
sig[sigSz++] = 0x22;
|
|
|
|
XMEMSET(&info, 0, sizeof(info));
|
|
info.algo_type = WC_ALGO_TYPE_PK;
|
|
info.pk.type = WC_PK_TYPE_ECDSA_VERIFY;
|
|
info.pk.eccverify.sig = sig;
|
|
info.pk.eccverify.siglen = sigSz;
|
|
info.pk.eccverify.hash = digest;
|
|
info.pk.eccverify.hashlen = (word32)sizeof(digest);
|
|
info.pk.eccverify.res = &verifyRes;
|
|
info.pk.eccverify.key = &key;
|
|
|
|
rc = wolfTPM2_CryptoDevCb(INVALID_DEVID, &info, &tpmCtx);
|
|
AssertIntEQ(rc, CRYPTOCB_UNAVAILABLE);
|
|
}
|
|
|
|
wc_ecc_free(&key);
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "CryptoDevCb ECC oversized R/S:");
|
|
#endif
|
|
}
|
|
|
|
static void test_wolfTPM2_CryptoDevCb_MlDsaSign(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WRAPPER) && defined(WOLFTPM_CRYPTOCB) && \
|
|
!defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFTPM_MLDSA_SIGN)
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY dummyKey;
|
|
WOLFTPM2_KEY tpmKey;
|
|
TPMT_PUBLIC pub;
|
|
TpmCryptoDevCtx tpmCtx;
|
|
wc_CryptoInfo info;
|
|
byte msg[32];
|
|
byte sig[64];
|
|
byte signContext[4];
|
|
word32 sigLen = (word32)sizeof(sig);
|
|
byte* bigSig = NULL;
|
|
word32 bigSigLen;
|
|
#ifdef WOLFTPM_MLDSA_VERIFY
|
|
byte otherContext[4];
|
|
TPM_HANDLE vSeq;
|
|
TPMT_TK_VERIFIED vtk;
|
|
#endif
|
|
|
|
XMEMSET(&tpmCtx, 0, sizeof(tpmCtx));
|
|
XMEMSET(&dummyKey, 0, sizeof(dummyKey));
|
|
XMEMSET(&tpmKey, 0, sizeof(tpmKey));
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
XMEMSET(msg, 0x5A, sizeof(msg));
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
tpmCtx.dev = &dev;
|
|
|
|
XMEMSET(&info, 0, sizeof(info));
|
|
info.algo_type = WC_ALGO_TYPE_PK;
|
|
info.pk.type = WC_PK_TYPE_PQC_SIG_SIGN;
|
|
info.pk.pqc_sign.type = WC_PQC_SIG_TYPE_MLDSA;
|
|
info.pk.pqc_sign.in = msg;
|
|
info.pk.pqc_sign.inlen = (word32)sizeof(msg);
|
|
info.pk.pqc_sign.out = sig;
|
|
info.pk.pqc_sign.outlen = &sigLen;
|
|
|
|
/* no key: fall back */
|
|
tpmCtx.mldsaKey = NULL;
|
|
rc = wolfTPM2_CryptoDevCb(INVALID_DEVID, &info, &tpmCtx);
|
|
AssertIntEQ(rc, CRYPTOCB_UNAVAILABLE);
|
|
|
|
/* wrong PQC type: fall back */
|
|
info.pk.pqc_sign.type = WC_PQC_SIG_TYPE_MLDSA + 1;
|
|
tpmCtx.mldsaKey = &dummyKey;
|
|
rc = wolfTPM2_CryptoDevCb(INVALID_DEVID, &info, &tpmCtx);
|
|
AssertIntEQ(rc, CRYPTOCB_UNAVAILABLE);
|
|
|
|
/* With a TPM key set the private material is on-chip, so an unsupported
|
|
* request must fail rather than yield to a software signer that has no
|
|
* private key to use. */
|
|
|
|
/* pre-hash: not pure ML-DSA */
|
|
info.pk.pqc_sign.type = WC_PQC_SIG_TYPE_MLDSA;
|
|
info.pk.pqc_sign.preHashType = WC_HASH_TYPE_SHA256;
|
|
rc = wolfTPM2_CryptoDevCb(INVALID_DEVID, &info, &tpmCtx);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
info.pk.pqc_sign.preHashType = WC_HASH_TYPE_NONE;
|
|
|
|
/* too big for one-shot */
|
|
info.pk.pqc_sign.inlen = MAX_DIGEST_BUFFER + 1;
|
|
rc = wolfTPM2_CryptoDevCb(INVALID_DEVID, &info, &tpmCtx);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
info.pk.pqc_sign.inlen = (word32)sizeof(msg);
|
|
|
|
/* NULL outlen: reject before dereference */
|
|
info.pk.pqc_sign.outlen = NULL;
|
|
rc = wolfTPM2_CryptoDevCb(INVALID_DEVID, &info, &tpmCtx);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
info.pk.pqc_sign.outlen = &sigLen;
|
|
|
|
/* happy path (needs v1.85 ML-DSA) */
|
|
rc = wolfTPM2_GetKeyTemplate_MLDSA(&pub,
|
|
TPMA_OBJECT_sign | TPMA_OBJECT_fixedTPM | TPMA_OBJECT_fixedParent |
|
|
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
|
|
TPMA_OBJECT_noDA, TPM_MLDSA_65, 0);
|
|
if (rc == 0) {
|
|
rc = wolfTPM2_CreatePrimaryKey(&dev, &tpmKey, TPM_RH_OWNER, &pub,
|
|
NULL, 0);
|
|
}
|
|
if (rc == TPM_RC_VALUE || rc == TPM_RC_SCHEME ||
|
|
rc == TPM_RC_COMMAND_CODE || rc == (int)(RC_VER1 + 0x043)) {
|
|
/* TPM lacks ML-DSA: skip */
|
|
printf("Test TPM Wrapper: %-40s Skipped (not supported)\n",
|
|
"CryptoDevCb ML-DSA sign:");
|
|
}
|
|
else {
|
|
AssertIntEQ(rc, 0);
|
|
bigSig = (byte*)XMALLOC(WC_MLDSA_65_SIG_SIZE, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
AssertNotNull(bigSig);
|
|
tpmCtx.mldsaKey = &tpmKey;
|
|
info.pk.pqc_sign.type = WC_PQC_SIG_TYPE_MLDSA;
|
|
info.pk.pqc_sign.out = bigSig;
|
|
|
|
/* full buffer: signs, sets len */
|
|
bigSigLen = (word32)WC_MLDSA_65_SIG_SIZE;
|
|
info.pk.pqc_sign.outlen = &bigSigLen;
|
|
rc = wolfTPM2_CryptoDevCb(INVALID_DEVID, &info, &tpmCtx);
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntEQ((int)bigSigLen, WC_MLDSA_65_SIG_SIZE);
|
|
|
|
/* context passthrough: sig must verify under the same context */
|
|
XMEMSET(signContext, 0x42, sizeof(signContext));
|
|
bigSigLen = (word32)WC_MLDSA_65_SIG_SIZE;
|
|
info.pk.pqc_sign.outlen = &bigSigLen;
|
|
info.pk.pqc_sign.context = signContext;
|
|
info.pk.pqc_sign.contextLen = (byte)sizeof(signContext);
|
|
rc = wolfTPM2_CryptoDevCb(INVALID_DEVID, &info, &tpmCtx);
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntEQ((int)bigSigLen, WC_MLDSA_65_SIG_SIZE);
|
|
info.pk.pqc_sign.context = NULL;
|
|
info.pk.pqc_sign.contextLen = 0;
|
|
|
|
#ifdef WOLFTPM_MLDSA_VERIFY
|
|
rc = wolfTPM2_VerifySequenceStart(&dev, &tpmKey, signContext,
|
|
(int)sizeof(signContext), &vSeq);
|
|
AssertIntEQ(rc, 0);
|
|
rc = wolfTPM2_VerifySequenceUpdate(&dev, vSeq, msg, (int)sizeof(msg));
|
|
AssertIntEQ(rc, 0);
|
|
XMEMSET(&vtk, 0, sizeof(vtk));
|
|
rc = wolfTPM2_VerifySequenceComplete(&dev, vSeq, &tpmKey,
|
|
NULL, 0, bigSig, (int)bigSigLen, &vtk);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* a different context must not verify the same signature */
|
|
XMEMSET(otherContext, 0x24, sizeof(otherContext));
|
|
rc = wolfTPM2_VerifySequenceStart(&dev, &tpmKey, otherContext,
|
|
(int)sizeof(otherContext), &vSeq);
|
|
AssertIntEQ(rc, 0);
|
|
rc = wolfTPM2_VerifySequenceUpdate(&dev, vSeq, msg, (int)sizeof(msg));
|
|
AssertIntEQ(rc, 0);
|
|
XMEMSET(&vtk, 0, sizeof(vtk));
|
|
rc = wolfTPM2_VerifySequenceComplete(&dev, vSeq, &tpmKey,
|
|
NULL, 0, bigSig, (int)bigSigLen, &vtk);
|
|
AssertIntNE(rc, 0);
|
|
#endif /* WOLFTPM_MLDSA_VERIFY */
|
|
|
|
/* small buffer: preserves the size-error contract, not WC_HW_E */
|
|
bigSigLen = 16;
|
|
info.pk.pqc_sign.outlen = &bigSigLen;
|
|
rc = wolfTPM2_CryptoDevCb(INVALID_DEVID, &info, &tpmCtx);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
|
|
XFREE(bigSig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
wolfTPM2_UnloadHandle(&dev, &tpmKey.handle);
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "CryptoDevCb ML-DSA sign:");
|
|
}
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
#endif
|
|
}
|
|
|
|
static void test_TPM2_ASN_DecodeX509Cert_Errors(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WRAPPER) && !defined(WOLFTPM2_NO_ASN)
|
|
int rc;
|
|
DecodedX509 x509;
|
|
byte garbage[16];
|
|
byte trunc[4];
|
|
|
|
XMEMSET(&x509, 0, sizeof(x509));
|
|
XMEMSET(garbage, 0xFF, sizeof(garbage));
|
|
|
|
/* NULL arguments must be rejected, not dereferenced */
|
|
rc = TPM2_ASN_DecodeX509Cert(NULL, 0, &x509);
|
|
AssertIntNE(rc, 0);
|
|
rc = TPM2_ASN_DecodeX509Cert(garbage, (int)sizeof(garbage), NULL);
|
|
AssertIntNE(rc, 0);
|
|
|
|
/* malformed input must not report success */
|
|
rc = TPM2_ASN_DecodeX509Cert(garbage, (int)sizeof(garbage), &x509);
|
|
AssertIntNE(rc, 0);
|
|
|
|
/* outer SEQUENCE whose length runs past the buffer (TPM_RC_INSUFFICIENT) */
|
|
trunc[0] = 0x30; trunc[1] = 0x20; trunc[2] = 0x00; trunc[3] = 0x00;
|
|
rc = TPM2_ASN_DecodeX509Cert(trunc, (int)sizeof(trunc), &x509);
|
|
AssertIntNE(rc, 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "ASN DecodeX509Cert errors:");
|
|
#endif
|
|
}
|
|
|
|
static void test_TPM2_ASN_RsaUnpadPkcsv15(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WRAPPER) && !defined(WOLFTPM2_NO_ASN)
|
|
byte blk[64];
|
|
byte* p;
|
|
int sz;
|
|
int i;
|
|
|
|
/* Well formed: 00 01 FF*8 00 then 2 data bytes */
|
|
XMEMSET(blk, 0xFF, sizeof(blk));
|
|
blk[0] = 0x00; blk[1] = 0x01; blk[10] = 0x00;
|
|
blk[11] = 0xAA; blk[12] = 0xBB;
|
|
p = blk; sz = 13;
|
|
AssertIntEQ(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0);
|
|
AssertIntEQ(sz, 2);
|
|
AssertIntEQ(p[0], 0xAA);
|
|
AssertIntEQ(p[1], 0xBB);
|
|
|
|
/* Exactly 8 pad bytes is the minimum accepted */
|
|
XMEMSET(blk, 0xFF, sizeof(blk));
|
|
blk[0] = 0x00; blk[1] = 0x01; blk[10] = 0x00; blk[11] = 0x5A;
|
|
p = blk; sz = 12;
|
|
AssertIntEQ(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0);
|
|
AssertIntEQ(sz, 1);
|
|
|
|
/* Seven pad bytes must be rejected */
|
|
XMEMSET(blk, 0xFF, sizeof(blk));
|
|
blk[0] = 0x00; blk[1] = 0x01; blk[9] = 0x00; blk[10] = 0x5A;
|
|
p = blk; sz = 11;
|
|
AssertIntNE(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0);
|
|
|
|
/* Wrong leading byte */
|
|
XMEMSET(blk, 0xFF, sizeof(blk));
|
|
blk[0] = 0x01; blk[1] = 0x01; blk[10] = 0x00;
|
|
p = blk; sz = 12;
|
|
AssertIntNE(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0);
|
|
|
|
/* Block type 2 must be rejected (this routine is type 1 only) */
|
|
XMEMSET(blk, 0xFF, sizeof(blk));
|
|
blk[0] = 0x00; blk[1] = 0x02; blk[10] = 0x00;
|
|
p = blk; sz = 12;
|
|
AssertIntNE(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0);
|
|
|
|
/* No separator at all (all 0xFF tail) */
|
|
XMEMSET(blk, 0xFF, sizeof(blk));
|
|
blk[0] = 0x00; blk[1] = 0x01;
|
|
p = blk; sz = 16;
|
|
AssertIntNE(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0);
|
|
|
|
/* Non-zero, non-FF byte where the separator belongs */
|
|
XMEMSET(blk, 0xFF, sizeof(blk));
|
|
blk[0] = 0x00; blk[1] = 0x01; blk[10] = 0x7E;
|
|
p = blk; sz = 16;
|
|
AssertIntNE(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0);
|
|
|
|
/* Too short to hold a block */
|
|
for (i = 0; i < 3; i++) {
|
|
XMEMSET(blk, 0x00, sizeof(blk));
|
|
p = blk; sz = i;
|
|
AssertIntNE(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0);
|
|
}
|
|
|
|
/* Separator as the final byte yields an empty payload */
|
|
XMEMSET(blk, 0xFF, sizeof(blk));
|
|
blk[0] = 0x00; blk[1] = 0x01; blk[11] = 0x00;
|
|
p = blk; sz = 12;
|
|
AssertIntEQ(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0);
|
|
AssertIntEQ(sz, 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "ASN RsaUnpadPkcsv15:");
|
|
#endif
|
|
}
|
|
|
|
#if !defined(WOLFTPM2_NO_WRAPPER) && !defined(WOLFTPM2_NO_ASN)
|
|
#include <examples/endorsement/trusted_certs_der.h>
|
|
#endif
|
|
static void test_TPM2_ASN_DecodeX509Cert_Valid(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WRAPPER) && !defined(WOLFTPM2_NO_ASN)
|
|
int rc;
|
|
DecodedX509 x509;
|
|
|
|
/* a well-formed DER certificate must decode and populate the fields */
|
|
XMEMSET(&x509, 0, sizeof(x509));
|
|
rc = TPM2_ASN_DecodeX509Cert((uint8_t*)kSTSAFEIntCa20,
|
|
(int)sizeof(kSTSAFEIntCa20), &x509);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntGT(x509.certSz, 0);
|
|
AssertNotNull(x509.publicKey);
|
|
AssertIntGT(x509.pubKeySz, 0);
|
|
AssertNotNull(x509.signature);
|
|
AssertIntGT(x509.sigSz, 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "ASN DecodeX509Cert valid:");
|
|
#endif
|
|
}
|
|
|
|
static void test_TPM2_ASN_DecodeTag_Errors(void)
|
|
{
|
|
#if !defined(WOLFTPM2_NO_WRAPPER) && !defined(WOLFTPM2_NO_ASN)
|
|
int rc, idx, tagLen;
|
|
byte buf[4];
|
|
|
|
buf[0] = 0x30; buf[1] = 0x02; buf[2] = 0x00; buf[3] = 0x00;
|
|
|
|
idx = 0;
|
|
rc = TPM2_ASN_DecodeTag(buf, (int)sizeof(buf), &idx, &tagLen, 0x30);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* wrong expected tag must be reported, not accepted as success */
|
|
idx = 0;
|
|
rc = TPM2_ASN_DecodeTag(buf, (int)sizeof(buf), &idx, &tagLen, 0x02);
|
|
AssertIntNE(rc, 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "ASN DecodeTag tag mismatch:");
|
|
#endif
|
|
}
|
|
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) && \
|
|
!defined(WOLFTPM2_NO_ASN)
|
|
#define FLAGS_USE_WOLFCRYPT (1 << 0)
|
|
#define FLAGS_USE_CRYPTO_CB (1 << 1)
|
|
#define FLAGS_USE_PK_CB (1 << 2) /* requires TLS layer to test */
|
|
static void test_wolfTPM2_EccSignVerifyDig(WOLFTPM2_DEV* dev,
|
|
WOLFTPM2_KEY* storageKey, const byte* digest, int digestSz,
|
|
TPM_ECC_CURVE curve, TPMI_ALG_HASH hashAlg, int flags)
|
|
{
|
|
int rc;
|
|
int verifyRes = 0;
|
|
WOLFTPM2_KEYBLOB eccKey;
|
|
TPMT_PUBLIC publicTemplate;
|
|
byte sigRs[MAX_ECC_BYTES*2];
|
|
word32 sigRsSz = (word32)sizeof(sigRs);
|
|
byte sig[ECC_MAX_SIG_SIZE];
|
|
word32 sigSz;
|
|
byte *r, *s;
|
|
word32 rLen, sLen;
|
|
ecc_key wolfKey;
|
|
int curveSize = TPM2_GetCurveSize(curve);
|
|
int tpmDevId = INVALID_DEVID;
|
|
char nameBuf[48];
|
|
#ifdef WOLF_CRYPTO_CB
|
|
TpmCryptoDevCtx tpmCtx;
|
|
byte badDigest[TPM_MAX_DIGEST_SIZE];
|
|
|
|
XMEMSET(&tpmCtx, 0, sizeof(tpmCtx));
|
|
tpmCtx.dev = dev;
|
|
tpmCtx.ecdsaKey = &eccKey;
|
|
tpmCtx.storageKey = storageKey;
|
|
|
|
if (flags & FLAGS_USE_CRYPTO_CB) {
|
|
rc = wolfTPM2_SetCryptoDevCb(dev, wolfTPM2_CryptoDevCb, &tpmCtx,
|
|
&tpmDevId);
|
|
AssertIntEQ(rc, 0);
|
|
}
|
|
#endif
|
|
|
|
/* Skip if this TPM doesn't implement the requested hash alg. Some TPMs
|
|
* (e.g. Nuvoton NPCT75x) only support a subset of hashes; the TPM rejects
|
|
* Create with TPM_RC_SIZE param 1, not TPM_RC_HASH, so the existing
|
|
* post-hoc skip-check can't catch it. Query capabilities up front. */
|
|
if (!test_tpm_alg_supported(hashAlg)) {
|
|
printf("Hash alg 0x%x not supported by TPM... Skipping\n", hashAlg);
|
|
goto exit;
|
|
}
|
|
|
|
/* -- Use TPM key to sign and verify with wolfCrypt -- */
|
|
/* Create ECC key for signing */
|
|
rc = wolfTPM2_GetKeyTemplate_ECC_ex(&publicTemplate, hashAlg,
|
|
(TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
|
|
TPMA_OBJECT_sign | TPMA_OBJECT_noDA),
|
|
curve, TPM_ALG_ECDSA, hashAlg);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Use create key and load key directly instead to make
|
|
* sure the private portion is populated */
|
|
rc = wolfTPM2_CreateKey(dev, &eccKey, &storageKey->handle,
|
|
&publicTemplate, (byte*)gKeyAuth, sizeof(gKeyAuth)-1);
|
|
if (rc == TPM_RC_SUCCESS) {
|
|
rc = wolfTPM2_LoadKey(dev, &eccKey, &storageKey->handle);
|
|
}
|
|
if ((rc & TPM_RC_HASH) == TPM_RC_HASH) {
|
|
printf("Hash type not supported... Skipping\n");
|
|
goto exit;
|
|
}
|
|
if ((rc & TPM_RC_CURVE) == TPM_RC_CURVE) {
|
|
printf("Curve not supported... Skipping\n");
|
|
goto exit;
|
|
}
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Sign with TPM */
|
|
rc = wolfTPM2_SignHashScheme(dev, (WOLFTPM2_KEY*)&eccKey, digest, digestSz,
|
|
sigRs, (int*)&sigRsSz, TPM_ALG_ECDSA, hashAlg);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Make sure leading zero's not required are trimmed */
|
|
rLen = sLen = sigRsSz / 2;
|
|
r = &sigRs[0];
|
|
s = &sigRs[rLen];
|
|
|
|
/* Encode ECDSA Header */
|
|
sigSz = (word32)sizeof(sig);
|
|
rc = wc_ecc_rs_raw_to_sig(r, rLen, s, sLen, sig, &sigSz);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Initialize wolfCrypt ECC key */
|
|
rc = wc_ecc_init_ex(&wolfKey, NULL, tpmDevId);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Convert TPM key to wolfCrypt key for verification */
|
|
rc = wolfTPM2_EccKey_TpmToWolf(dev, (WOLFTPM2_KEY*)&eccKey, &wolfKey);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Verify TPM signature with wolfCrypt */
|
|
rc = wc_ecc_verify_hash(sig, sigSz, digest, digestSz, &verifyRes, &wolfKey);
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntEQ(verifyRes, 1); /* 1 indicates successful verification */
|
|
|
|
#ifdef WOLF_CRYPTO_CB
|
|
/* Drive the invalid-signature branch of the crypto callback: a tampered
|
|
* digest must return verifyRes == 0 with rc == 0 */
|
|
if (flags & FLAGS_USE_CRYPTO_CB) {
|
|
XMEMCPY(badDigest, digest, digestSz);
|
|
badDigest[0] ^= 0xFF;
|
|
verifyRes = 1;
|
|
rc = wc_ecc_verify_hash(sig, sigSz, badDigest, digestSz, &verifyRes,
|
|
&wolfKey);
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntEQ(verifyRes, 0);
|
|
}
|
|
#endif
|
|
|
|
/* Cleanup first wolfCrypt key */
|
|
wc_ecc_free(&wolfKey);
|
|
wolfTPM2_UnloadHandle(dev, &eccKey.handle);
|
|
#ifdef WOLF_CRYPTO_CB
|
|
tpmCtx.ecdsaKey = NULL; /* create new one */
|
|
#endif
|
|
|
|
/* -- Use wolfCrypt key to sign and verify with TPM -- */
|
|
/* Initialize new wolfCrypt ECC key */
|
|
rc = wc_ecc_init_ex(&wolfKey, NULL, tpmDevId);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Generate new ECC key with wolfCrypt */
|
|
rc = wc_ecc_make_key(wolfTPM2_GetRng(dev), curveSize, &wolfKey);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Sign with wolfCrypt */
|
|
sigSz = (word32)sizeof(sig);
|
|
rc = wc_ecc_sign_hash(digest, digestSz, sig, &sigSz, wolfTPM2_GetRng(dev),
|
|
&wolfKey);
|
|
AssertIntEQ(rc, 0);
|
|
wolfTPM2_UnloadHandle(dev, &eccKey.handle);
|
|
|
|
/* Decode ECDSA Header */
|
|
r = sigRs;
|
|
s = &sigRs[MAX_ECC_BYTES];
|
|
rLen = sLen = MAX_ECC_BYTES;
|
|
rc = wc_ecc_sig_to_rs(sig, sigSz, r, &rLen, s, &sLen);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Convert wolfCrypt key to TPM key for verification */
|
|
rc = wolfTPM2_EccKey_WolfToTpm(dev, &wolfKey, (WOLFTPM2_KEY*)&eccKey);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* combine R and S at key size (zero pad leading) */
|
|
XMEMMOVE(&sigRs[curveSize-rLen], r, rLen);
|
|
XMEMSET(&sigRs[0], 0, curveSize-rLen);
|
|
XMEMMOVE(&sigRs[curveSize + (curveSize-sLen)], s, sLen);
|
|
XMEMSET(&sigRs[curveSize], 0, curveSize-sLen);
|
|
|
|
/* Verify wolfCrypt signature with TPM */
|
|
rc = wolfTPM2_VerifyHashScheme(dev, (WOLFTPM2_KEY*)&eccKey, sigRs,
|
|
curveSize*2, digest, digestSz, TPM_ALG_ECDSA, hashAlg);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Cleanup */
|
|
wc_ecc_free(&wolfKey);
|
|
wolfTPM2_UnloadHandle(dev, &eccKey.handle);
|
|
|
|
XSNPRINTF(nameBuf, sizeof(nameBuf), "Sign/Verify Dig=%d Curve=%d %s%s:",
|
|
digestSz, TPM2_GetCurveSize(curve), TPM2_GetAlgName(hashAlg),
|
|
(flags & FLAGS_USE_CRYPTO_CB) ? " CCB" : "");
|
|
printf("Test TPM Wrapper: %-40s %s\n", nameBuf,
|
|
rc == 0 ? "Passed" : "Failed");
|
|
|
|
exit:
|
|
#ifdef WOLF_CRYPTO_CB
|
|
/* Unregister on every path (incl. skips) so a leaked registration does
|
|
* not make the next SetCryptoDevCb return ALREADY_E on wolfSSL 5.9.2+. */
|
|
if (flags & FLAGS_USE_CRYPTO_CB) {
|
|
wolfTPM2_ClearCryptoDevCb(dev, tpmDevId);
|
|
}
|
|
#endif
|
|
(void)tpmDevId;
|
|
}
|
|
|
|
static void test_wolfTPM2_EccSignVerify_All(WOLFTPM2_DEV* dev,
|
|
WOLFTPM2_KEY* storageKey, int flags)
|
|
{
|
|
int i;
|
|
byte digest[TPM_MAX_DIGEST_SIZE];
|
|
|
|
for (i = 0; i < (int)sizeof(digest); i++) {
|
|
digest[i] = (byte)i;
|
|
}
|
|
|
|
test_wolfTPM2_EccSignVerifyDig(dev, storageKey, digest, 20,
|
|
TPM_ECC_NIST_P256, TPM_ALG_SHA256, flags);
|
|
test_wolfTPM2_EccSignVerifyDig(dev, storageKey, digest, 32,
|
|
TPM_ECC_NIST_P256, TPM_ALG_SHA256, flags);
|
|
test_wolfTPM2_EccSignVerifyDig(dev, storageKey, digest, 48,
|
|
TPM_ECC_NIST_P256, TPM_ALG_SHA256, flags);
|
|
test_wolfTPM2_EccSignVerifyDig(dev, storageKey, digest, 64,
|
|
TPM_ECC_NIST_P256, TPM_ALG_SHA256, flags);
|
|
|
|
#if (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 384
|
|
test_wolfTPM2_EccSignVerifyDig(dev, storageKey, digest, 20,
|
|
TPM_ECC_NIST_P384, TPM_ALG_SHA384, flags);
|
|
test_wolfTPM2_EccSignVerifyDig(dev, storageKey, digest, 32,
|
|
TPM_ECC_NIST_P384, TPM_ALG_SHA384, flags);
|
|
test_wolfTPM2_EccSignVerifyDig(dev, storageKey, digest, 48,
|
|
TPM_ECC_NIST_P384, TPM_ALG_SHA384, flags);
|
|
test_wolfTPM2_EccSignVerifyDig(dev, storageKey, digest, 64,
|
|
TPM_ECC_NIST_P384, TPM_ALG_SHA384, flags);
|
|
#endif
|
|
|
|
#if (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 521
|
|
test_wolfTPM2_EccSignVerifyDig(dev, storageKey, digest, 20,
|
|
TPM_ECC_NIST_P521, TPM_ALG_SHA512, flags);
|
|
test_wolfTPM2_EccSignVerifyDig(dev, storageKey, digest, 32,
|
|
TPM_ECC_NIST_P521, TPM_ALG_SHA512, flags);
|
|
test_wolfTPM2_EccSignVerifyDig(dev, storageKey, digest, 48,
|
|
TPM_ECC_NIST_P521, TPM_ALG_SHA512, flags);
|
|
test_wolfTPM2_EccSignVerifyDig(dev, storageKey, digest, 64,
|
|
TPM_ECC_NIST_P521, TPM_ALG_SHA512, flags);
|
|
#endif
|
|
}
|
|
|
|
/* Test with smaller, same and larger digest sizes using different ECC curves.
|
|
* Interop sign and verify with wolfCrypt and TPM */
|
|
static void test_wolfTPM2_EccSignVerify(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY storageKey;
|
|
|
|
/* Initialize TPM */
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Create storage key */
|
|
rc = wolfTPM2_CreateSRK(&dev, &storageKey, TPM_ALG_ECC,
|
|
(byte*)gStorageKeyAuth, sizeof(gStorageKeyAuth)-1);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
test_wolfTPM2_EccSignVerify_All(&dev, &storageKey, 0);
|
|
#ifdef WOLF_CRYPTO_CB
|
|
test_wolfTPM2_EccSignVerify_All(&dev, &storageKey, FLAGS_USE_CRYPTO_CB);
|
|
#endif
|
|
|
|
wolfTPM2_UnloadHandle(&dev, &storageKey.handle);
|
|
wolfTPM2_Cleanup(&dev);
|
|
}
|
|
#endif
|
|
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFTPM2_PEM_DECODE) && \
|
|
!defined(NO_RSA)
|
|
static WOLFTPM2_KEY authKey; /* also used for test_wolfTPM2_PCRPolicy */
|
|
|
|
static void test_wolfTPM_ImportPublicKey(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
TPM_ALG_ID alg = TPM_ALG_RSA;
|
|
int encType = ENCODING_TYPE_PEM;
|
|
TPMA_OBJECT attributes = (
|
|
TPMA_OBJECT_sign |
|
|
TPMA_OBJECT_noDA |
|
|
TPMA_OBJECT_userWithAuth
|
|
);
|
|
/* public key from ibmtss/utils/policies/rsapubkey.pem */
|
|
const char* pemPublicKey =
|
|
"-----BEGIN PUBLIC KEY-----\n"
|
|
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAukO2Z2rjxNm7EWi82TpW\n"
|
|
"hXmJo5fPf2enN4KzF35qVM4KjYdpVODWQ377Lq3edqriP1Ji2dUvqoUHNrkfwSOH\n"
|
|
"EHHKWXO++if4o+kI5YdC1MzwXMVHI2Yrn7fAteGArM7Ox9GRcdzmicw38HMWWGtM\n"
|
|
"OBUkaLZnO7rJW1VPQQw1IG9d+hFepXfrNl75zz2S2mceWecFRGBFE8DPW+zMQIMm\n"
|
|
"qFtt9g9+LIw0b1fn13DsMW7JX3J126ZwgTH6BEmSIY04xz2Tz0Z0+GNb+mwDypP9\n"
|
|
"1o0l0ITkETMsfabpGgEfC2x+67lQJR986MyLZ+WDK+3LeT2b4mA2bxpRa6yDrEv/\n"
|
|
"gQIDAQAB\n"
|
|
"-----END PUBLIC KEY-----";
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
rc = wolfTPM2_ImportPublicKeyBuffer(&dev,
|
|
alg,
|
|
&authKey,
|
|
encType,
|
|
pemPublicKey, (word32)XSTRLEN(pemPublicKey),
|
|
attributes
|
|
);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
}
|
|
|
|
/* Returns 1 if the connected TPM has at least one PCR allocated in the @alg
|
|
* bank. Lets bank-specific tests adapt to whatever the hardware exposes
|
|
* instead of assuming a fixed bank (e.g. a TPM provisioned with SHA-384 PCRs
|
|
* only). */
|
|
static int test_pcr_bank_allocated(TPM_ALG_ID alg)
|
|
{
|
|
GetCapability_In in;
|
|
GetCapability_Out out;
|
|
TPML_PCR_SELECTION* sel;
|
|
word32 i;
|
|
int j;
|
|
|
|
XMEMSET(&in, 0, sizeof(in));
|
|
XMEMSET(&out, 0, sizeof(out));
|
|
in.capability = TPM_CAP_PCRS;
|
|
in.property = 0;
|
|
in.propertyCount = 1;
|
|
if (TPM2_GetCapability(&in, &out) != TPM_RC_SUCCESS)
|
|
return 0;
|
|
|
|
sel = &out.capabilityData.data.assignedPCR;
|
|
for (i = 0; i < sel->count; i++) {
|
|
if (sel->pcrSelections[i].hash == alg) {
|
|
for (j = 0; j < sel->pcrSelections[i].sizeofSelect; j++) {
|
|
if (sel->pcrSelections[i].pcrSelect[j] != 0)
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Test vector from ibmtss policy authorize test for SHA2-256 */
|
|
static void test_wolfTPM2_PCRPolicy(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
TPM_ALG_ID pcrAlg = TPM_ALG_SHA256;
|
|
const char* aaa = "aaa";
|
|
byte pcrArray[1] = {TPM2_DEMO_PCR_INDEX};
|
|
word32 pcrArraySz = 1;
|
|
byte pcrIndex = pcrArray[0];
|
|
byte digest[WC_SHA256_DIGEST_SIZE];
|
|
word32 digestSz;
|
|
byte pcrHash[WC_SHA256_DIGEST_SIZE];
|
|
word32 pcrHashSz;
|
|
const byte expectedPolicyAuth[] = {
|
|
0xEB, 0xA3, 0xF9, 0x8C, 0x5E, 0xAF, 0x1E, 0xA8,
|
|
0xF9, 0x4F, 0x51, 0x9B, 0x4D, 0x2A, 0x31, 0x83,
|
|
0xEE, 0x79, 0x87, 0x66, 0x72, 0x39, 0x8E, 0x23,
|
|
0x15, 0xD9, 0x33, 0xC2, 0x88, 0xA8, 0xE5, 0x03
|
|
};
|
|
const byte expectedPCRAuth[] = {
|
|
0x76, 0x44, 0xF6, 0x11, 0xEA, 0x10, 0xD7, 0x60,
|
|
0xDA, 0xB9, 0x36, 0xC3, 0x95, 0x1E, 0x1D, 0x85,
|
|
0xEC, 0xDB, 0x84, 0xCE, 0x9A, 0x79, 0x03, 0xDD,
|
|
0xE1, 0xC7, 0xE0, 0xA2, 0xD9, 0x09, 0xA0, 0x13
|
|
};
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* fixup public key to match TCG test vector */
|
|
authKey.pub.publicArea.parameters.rsaDetail.exponent = 0;
|
|
|
|
/* Generate authorization policy for public key */
|
|
/* Use public key from "test_wolfTPM_ImportPublicKey" */
|
|
XMEMSET(digest, 0, sizeof(digest)); /* empty old hash */
|
|
digestSz = WC_SHA256_DIGEST_SIZE;
|
|
rc = wolfTPM2_PolicyAuthorizeMake(pcrAlg, &authKey.pub,
|
|
digest, &digestSz, NULL, 0);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
AssertIntEQ(XMEMCMP(digest, expectedPolicyAuth, sizeof(expectedPolicyAuth)),
|
|
0);
|
|
|
|
/* The remaining checks use fixed SHA-256 PCR vectors. A TPM without a
|
|
* SHA-256 PCR bank allocated cannot match them, so skip that portion
|
|
* rather than fail (the policy-authorize math above still runs). */
|
|
if (!test_pcr_bank_allocated(pcrAlg)) {
|
|
printf("Test TPM Wrapper: %-40s Skipped (no SHA-256 PCR bank)\n",
|
|
"PCRPolicy bank:");
|
|
wolfTPM2_Cleanup(&dev);
|
|
return;
|
|
}
|
|
|
|
rc = wolfTPM2_ResetPCR(&dev, pcrIndex);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
rc = wolfTPM2_ExtendPCR(&dev, pcrIndex, pcrAlg,
|
|
(byte*)aaa, (int)XSTRLEN(aaa));
|
|
AssertIntEQ(rc, 0);
|
|
|
|
rc = wolfTPM2_PCRGetDigest(&dev, pcrAlg, pcrArray, pcrArraySz,
|
|
pcrHash, &pcrHashSz);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
XMEMSET(digest, 0, sizeof(digest)); /* empty old hash */
|
|
digestSz = WC_SHA256_DIGEST_SIZE;
|
|
rc = wolfTPM2_PolicyPCRMake(pcrAlg, pcrArray, pcrArraySz,
|
|
pcrHash, pcrHashSz, digest, &digestSz);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
AssertIntEQ(XMEMCMP(digest, expectedPCRAuth, sizeof(expectedPCRAuth)), 0);
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
}
|
|
#endif /* !WOLFTPM2_NO_WOLFCRYPT && WOLFTPM2_PEM_DECODE */
|
|
|
|
#if defined(HAVE_THREAD_LS) && defined(HAVE_PTHREAD)
|
|
#include <pthread.h>
|
|
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
int secondRunner = 0;
|
|
|
|
static void* test_wolfTPM2_thread_local_storage_work_thread(void* args)
|
|
{
|
|
TPM2_CTX tpm2Ctx;
|
|
|
|
TPM2_Init(&tpm2Ctx, NULL, NULL);
|
|
|
|
/* lock so that the other thread must wait while we set the ctx */
|
|
pthread_mutex_lock(&mutex);
|
|
|
|
/* ctx should be what was set in init, not set by other thread */
|
|
if (secondRunner == 1) {
|
|
if (TPM2_GetActiveCtx() != &tpm2Ctx)
|
|
printf("Test TPM Wrapper: %-40s Failed\n", "Thread Local Storage:");
|
|
else
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "Thread Local Storage:");
|
|
}
|
|
|
|
/* set the active ctx, should not impact the other thread */
|
|
TPM2_SetActiveCtx(&tpm2Ctx);
|
|
|
|
secondRunner = 1;
|
|
|
|
/* let the other thread run */
|
|
pthread_mutex_unlock(&mutex);
|
|
|
|
/* On autodetect builds TPM2_Init acquires a /dev/tpmX descriptor, so this
|
|
* must be released or every thread leaks one. */
|
|
TPM2_Cleanup(&tpm2Ctx);
|
|
|
|
(void)args;
|
|
return NULL;
|
|
}
|
|
#endif /* HAVE_THREAD_LS && HAVE_PTHREAD */
|
|
|
|
/* On transports where the OS owns TPM startup state, Reset/Shutdown decline the
|
|
* command with NOT_COMPILED_IN, but a request for neither is still a success. */
|
|
static void test_wolfTPM2_Reset_contract(void)
|
|
{
|
|
#if defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_WINAPI)
|
|
WOLFTPM2_DEV dev;
|
|
int rc;
|
|
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
|
|
/* nothing requested - nothing declined */
|
|
rc = wolfTPM2_Reset(&dev, 0, 0);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* a shutdown was requested and is being refused */
|
|
rc = wolfTPM2_Reset(&dev, 1, 0);
|
|
AssertIntEQ(rc, NOT_COMPILED_IN);
|
|
|
|
rc = wolfTPM2_Shutdown(&dev, 0);
|
|
AssertIntEQ(rc, NOT_COMPILED_IN);
|
|
|
|
AssertIntEQ(wolfTPM2_Reset(NULL, 0, 0), BAD_FUNC_ARG);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "Reset contract:");
|
|
#elif defined(WOLFTPM_LINUX_DEV_AUTODETECT)
|
|
WOLFTPM2_DEV dev;
|
|
int rc;
|
|
|
|
/* A non-negative fd is all this branch inspects: it clears both requests
|
|
* before the shutdown/startup blocks, so no command is sent and the fd is
|
|
* never touched. That makes the contract testable with no TPM present. */
|
|
XMEMSET(&dev, 0, sizeof(dev));
|
|
dev.ctx.fd = 1;
|
|
|
|
rc = wolfTPM2_Reset(&dev, 0, 0);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
rc = wolfTPM2_Reset(&dev, 1, 0);
|
|
AssertIntEQ(rc, NOT_COMPILED_IN);
|
|
|
|
rc = wolfTPM2_Shutdown(&dev, 0);
|
|
AssertIntEQ(rc, NOT_COMPILED_IN);
|
|
|
|
AssertIntEQ(wolfTPM2_Reset(NULL, 0, 0), BAD_FUNC_ARG);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "Reset contract:");
|
|
#endif
|
|
}
|
|
|
|
static void test_wolfTPM2_thread_local_storage(void)
|
|
{
|
|
#if defined(HAVE_THREAD_LS) && defined(HAVE_PTHREAD)
|
|
pthread_t thread_1;
|
|
pthread_t thread_2;
|
|
|
|
pthread_create(&thread_1, NULL,
|
|
test_wolfTPM2_thread_local_storage_work_thread, NULL);
|
|
pthread_create(&thread_2, NULL,
|
|
test_wolfTPM2_thread_local_storage_work_thread, NULL);
|
|
|
|
pthread_join(thread_1, NULL);
|
|
pthread_join(thread_2, NULL);
|
|
#endif /* HAVE_THREAD_LS && HAVE_PTHREAD */
|
|
}
|
|
|
|
#ifdef WOLFTPM_SPDM
|
|
/* Test SPDM wrapper API functions */
|
|
static void test_wolfTPM2_SPDM_Functions(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
#if defined(WOLFTPM_SPDM) && defined(WOLFTPM_SPDM_TCG)
|
|
byte rspPubKey[WOLFSPDM_ECC_POINT_SIZE];
|
|
#endif
|
|
#ifdef WOLFSPDM_NUVOTON
|
|
WOLFSPDM_NUVOTON_STATUS nuvStatus;
|
|
#endif
|
|
#if defined(WOLFSPDM_NUVOTON) || defined(WOLFSPDM_NATIONS)
|
|
byte pubKey[256];
|
|
word32 pubKeySz;
|
|
#endif
|
|
#ifdef WOLFSPDM_NATIONS
|
|
WOLFSPDM_NATIONS_STATUS nStatus;
|
|
TPM2_AUTH_SESSION nationsOrigSess;
|
|
#endif
|
|
|
|
AssertStrEQ(wolfSPDM_GetErrorString(WOLFSPDM_E_BAD_STATE),
|
|
"Invalid state");
|
|
AssertStrNE(TPM2_GetRCString(WOLFSPDM_E_BAD_STATE),
|
|
"SPDM invalid state");
|
|
|
|
/* Initialize device */
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* Test 1: Parameter validation - NULL args */
|
|
rc = wolfTPM2_SpdmInit(NULL);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_SpdmConnect(NULL);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
AssertIntEQ(wolfTPM2_SpdmIsConnected(NULL), 0);
|
|
AssertIntEQ(wolfTPM2_SpdmGetSessionId(NULL), 0);
|
|
rc = wolfTPM2_SpdmDisconnect(NULL);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_SpdmCleanup(NULL);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
#if defined(WOLFTPM_SPDM) && defined(WOLFTPM_SPDM_TCG)
|
|
XMEMSET(rspPubKey, 0xA5, sizeof(rspPubKey));
|
|
rc = wolfTPM2_SpdmSetResponderPubKey(NULL, rspPubKey,
|
|
sizeof(rspPubKey));
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
#endif
|
|
|
|
/* Test 2: Context lifecycle - init, check state, cleanup */
|
|
rc = wolfTPM2_SpdmInit(&dev);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
#if defined(WOLFTPM_SPDM) && defined(WOLFTPM_SPDM_TCG)
|
|
rc = wolfTPM2_SpdmSetResponderPubKey(&dev, NULL, 0);
|
|
AssertIntEQ(rc, WOLFSPDM_E_INVALID_ARG);
|
|
rc = wolfTPM2_SpdmSetResponderPubKey(&dev, rspPubKey,
|
|
sizeof(rspPubKey) - 1);
|
|
AssertIntEQ(rc, WOLFSPDM_E_INVALID_ARG);
|
|
if (!TestWolfTPM2_HasResponderPin()) {
|
|
rc = wolfTPM2_SpdmSetResponderPubKey(&dev, rspPubKey,
|
|
sizeof(rspPubKey));
|
|
AssertIntEQ(rc, WOLFSPDM_SUCCESS);
|
|
}
|
|
#endif
|
|
/* When SPDM-only mode is active, auto-SPDM connects during Init.
|
|
* Otherwise, just initialized but not yet connected. */
|
|
if (!dev.ctx.spdmOnlyDetected && !TestWolfTPM2_HasResponderPin()) {
|
|
AssertIntEQ(wolfTPM2_SpdmIsConnected(&dev), 0);
|
|
AssertIntEQ(wolfTPM2_SpdmGetSessionId(&dev), 0);
|
|
}
|
|
else if (TestWolfTPM2_HasResponderPin()) {
|
|
AssertIntEQ(wolfTPM2_SpdmIsConnected(&dev), 1);
|
|
AssertIntNE(wolfTPM2_SpdmGetSessionId(&dev), 0);
|
|
}
|
|
/* Cleanup */
|
|
if (TestWolfTPM2_HasResponderPin() &&
|
|
wolfTPM2_SpdmIsConnected(&dev)) {
|
|
rc = wolfTPM2_SpdmDisconnect(&dev);
|
|
AssertIntEQ(rc, WOLFSPDM_SUCCESS);
|
|
}
|
|
rc = wolfTPM2_SpdmCleanup(&dev);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
/* Idempotent cleanup */
|
|
rc = wolfTPM2_SpdmCleanup(&dev);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
#ifdef WOLFSPDM_NUVOTON
|
|
/* Test 3: Nuvoton-specific parameter validation */
|
|
rc = wolfTPM2_SpdmSetNuvotonMode(NULL);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_SpdmEnable(NULL);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
pubKeySz = sizeof(pubKey);
|
|
rc = wolfTPM2_SpdmGetStatus(NULL, &nuvStatus);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_SpdmGetStatus(&dev, NULL);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_SpdmGetPubKey(NULL, pubKey, &pubKeySz);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_SpdmSetOnlyMode(NULL, 0);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* Test 3b: SpdmEnable/Disable must preserve session[0] */
|
|
rc = wolfTPM2_SpdmInit(&dev);
|
|
if (rc == 0) {
|
|
TPM2_AUTH_SESSION origSess;
|
|
/* Set up a distinguishable session[0] state */
|
|
dev.session[0].sessionHandle = HMAC_SESSION_FIRST;
|
|
dev.session[0].sessionAttributes = 0x27;
|
|
dev.session[0].auth.size = 4;
|
|
XMEMCPY(dev.session[0].auth.buffer, "\x01\x02\x03\x04", 4);
|
|
XMEMCPY(&origSess, &dev.session[0], sizeof(origSess));
|
|
|
|
/* SpdmEnable may fail (no Nuvoton HW) but must restore session[0] */
|
|
(void)wolfTPM2_SpdmEnable(&dev);
|
|
AssertIntEQ(dev.session[0].sessionHandle, origSess.sessionHandle);
|
|
AssertIntEQ(dev.session[0].sessionAttributes, origSess.sessionAttributes);
|
|
AssertIntEQ(dev.session[0].auth.size, origSess.auth.size);
|
|
|
|
/* Restore and test SpdmDisable */
|
|
XMEMCPY(&dev.session[0], &origSess, sizeof(origSess));
|
|
(void)wolfTPM2_SpdmDisable(&dev);
|
|
AssertIntEQ(dev.session[0].sessionHandle, origSess.sessionHandle);
|
|
AssertIntEQ(dev.session[0].sessionAttributes, origSess.sessionAttributes);
|
|
AssertIntEQ(dev.session[0].auth.size, origSess.auth.size);
|
|
|
|
wolfTPM2_SpdmCleanup(&dev);
|
|
}
|
|
#endif /* WOLFSPDM_NUVOTON */
|
|
|
|
#ifdef WOLFSPDM_NATIONS
|
|
/* Test 4: Nations-specific parameter validation */
|
|
rc = wolfTPM2_SpdmSetNationsMode(NULL);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_SpdmNationsIdentityKeySet(NULL, 0);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
pubKeySz = sizeof(pubKey);
|
|
rc = wolfTPM2_SpdmGetPubKey(NULL, pubKey, &pubKeySz);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
/* Nations PSK wrapper parameter validation */
|
|
rc = wolfTPM2_SpdmConnectNationsPsk(NULL, NULL, 0, NULL, 0);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_SpdmNationsGetStatus(NULL, &nStatus);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_SpdmNationsSetOnlyMode(NULL, 0);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_SpdmNationsPskSet(NULL, NULL, 0);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
rc = wolfTPM2_SpdmNationsPskClear(NULL, NULL, 0);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* Test 4b: SpdmNationsIdentityKeySet must preserve session[0] */
|
|
dev.session[0].sessionHandle = HMAC_SESSION_FIRST;
|
|
dev.session[0].sessionAttributes = 0x27;
|
|
dev.session[0].auth.size = 4;
|
|
XMEMCPY(dev.session[0].auth.buffer, "\x01\x02\x03\x04", 4);
|
|
XMEMCPY(&nationsOrigSess, &dev.session[0], sizeof(nationsOrigSess));
|
|
|
|
/* May fail (no Nations HW) but must restore session[0] */
|
|
(void)wolfTPM2_SpdmNationsIdentityKeySet(&dev, 1);
|
|
AssertIntEQ(dev.session[0].sessionHandle, nationsOrigSess.sessionHandle);
|
|
AssertIntEQ(dev.session[0].sessionAttributes,
|
|
nationsOrigSess.sessionAttributes);
|
|
AssertIntEQ(dev.session[0].auth.size, nationsOrigSess.auth.size);
|
|
#endif /* WOLFSPDM_NATIONS */
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "SPDM Functions:");
|
|
}
|
|
#endif /* WOLFTPM_SPDM */
|
|
|
|
#ifdef WOLFTPM_SWTPM
|
|
/* Pin the swtpm response-size bounds so a mutation dropping either the
|
|
* lower (header) or upper (buffer) bound is caught. */
|
|
static void test_TPM2_SwtpmValidateRspSz(void)
|
|
{
|
|
int packetSize = 4096;
|
|
|
|
AssertIntEQ(TPM2_SwtpmValidateRspSz(packetSize, TPM2_HEADER_SIZE),
|
|
TPM_RC_SUCCESS);
|
|
AssertIntEQ(TPM2_SwtpmValidateRspSz(packetSize, (uint32_t)packetSize),
|
|
TPM_RC_SUCCESS);
|
|
|
|
AssertIntEQ(TPM2_SwtpmValidateRspSz(packetSize, TPM2_HEADER_SIZE - 1),
|
|
TPM_RC_FAILURE);
|
|
AssertIntEQ(TPM2_SwtpmValidateRspSz(packetSize, (uint32_t)packetSize + 1),
|
|
TPM_RC_FAILURE);
|
|
AssertIntEQ(TPM2_SwtpmValidateRspSz(packetSize, 0xFFFFFFFFUL),
|
|
TPM_RC_FAILURE);
|
|
|
|
printf("Test TPM2: %-40s Passed\n", "Swtpm ValidateRspSz:");
|
|
}
|
|
#endif /* WOLFTPM_SWTPM */
|
|
|
|
#if !defined(NO_FILESYSTEM) && !defined(NO_WRITE_TEMP_FILES) && \
|
|
!defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC)
|
|
/* Create a temp file for writing with owner-only permissions, so the test
|
|
* does not leave a world-writable file as plain fopen("wb") would. */
|
|
static XFILE openTestFileWrite(const char* fn)
|
|
{
|
|
#if defined(__linux__) || defined(__APPLE__) || defined(__unix__)
|
|
int fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
|
if (fd < 0) {
|
|
return XBADFILE;
|
|
}
|
|
return fdopen(fd, "wb");
|
|
#else
|
|
return XFOPEN(fn, "wb");
|
|
#endif
|
|
}
|
|
|
|
/* Craft a key-blob file with a small valid public area and an oversized
|
|
* private tail, then load it with readKeyBlob(). A canary placed directly
|
|
* after the keyblob catches any write past it. No TPM is required. */
|
|
static void test_readKeyBlob_PrivOverflow(void)
|
|
{
|
|
int rc;
|
|
int pubAreaSize = 0;
|
|
word32 i;
|
|
size_t privTailSz, remaining, chunk;
|
|
UINT16 privSizeMarker;
|
|
UINT16 bigMarker;
|
|
XFILE fp;
|
|
byte pubAreaBuffer[sizeof(TPM2B_PUBLIC)];
|
|
byte filler[64];
|
|
WOLFTPM2_KEYBLOB tmpl;
|
|
const char* filename = "keyblob_overflow_test.raw";
|
|
struct {
|
|
WOLFTPM2_KEYBLOB key;
|
|
byte canary[128];
|
|
} guarded;
|
|
|
|
XMEMSET(&tmpl, 0, sizeof(tmpl));
|
|
|
|
/* Build a real minimal public area so TPM2_ParsePublic() accepts it and
|
|
* readKeyBlob proceeds to the private read. */
|
|
rc = wolfTPM2_GetKeyTemplate_ECC(&tmpl.pub.publicArea,
|
|
TPMA_OBJECT_sign | TPMA_OBJECT_userWithAuth | TPMA_OBJECT_noDA,
|
|
TPM_ECC_NIST_P256, TPM_ALG_ECDSA);
|
|
AssertIntEQ(rc, 0);
|
|
rc = TPM2_AppendPublic(pubAreaBuffer, (word32)sizeof(pubAreaBuffer),
|
|
&pubAreaSize, &tmpl.pub);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Private tail longer than the destination so the pre-fix path, which
|
|
* reads the whole remaining file into &key->priv, overflows past the
|
|
* keyblob. Keep the size marker small and valid so the post-read sanity
|
|
* check passes. */
|
|
privTailSz = sizeof(guarded.key.priv) + sizeof(filler);
|
|
privSizeMarker = 32;
|
|
|
|
fp = openTestFileWrite(filename);
|
|
AssertNotNull(fp);
|
|
XFWRITE(&tmpl.pub.size, 1, sizeof(tmpl.pub.size), fp);
|
|
XFWRITE(pubAreaBuffer, 1, sizeof(UINT16) + tmpl.pub.size, fp);
|
|
XFWRITE(&privSizeMarker, 1, sizeof(privSizeMarker), fp);
|
|
XMEMSET(filler, 0xAB, sizeof(filler));
|
|
remaining = privTailSz - sizeof(privSizeMarker);
|
|
while (remaining > 0) {
|
|
chunk = (remaining < sizeof(filler)) ? remaining : sizeof(filler);
|
|
XFWRITE(filler, 1, chunk, fp);
|
|
remaining -= chunk;
|
|
}
|
|
XFCLOSE(fp);
|
|
|
|
XMEMSET(&guarded, 0, sizeof(guarded));
|
|
XMEMSET(guarded.canary, 0x5A, sizeof(guarded.canary));
|
|
|
|
rc = readKeyBlob(filename, &guarded.key);
|
|
(void)rc;
|
|
|
|
for (i = 0; i < sizeof(guarded.canary); i++) {
|
|
AssertIntEQ(guarded.canary[i], 0x5A);
|
|
}
|
|
|
|
remove(filename);
|
|
|
|
/* Rejection branch: priv.size marker larger than the destination buffer
|
|
* must be refused with BUFFER_E before any bytes are read. */
|
|
bigMarker = (UINT16)(sizeof(guarded.key.priv.buffer) + 1);
|
|
fp = openTestFileWrite(filename);
|
|
AssertNotNull(fp);
|
|
XFWRITE(&tmpl.pub.size, 1, sizeof(tmpl.pub.size), fp);
|
|
XFWRITE(pubAreaBuffer, 1, sizeof(UINT16) + tmpl.pub.size, fp);
|
|
XFWRITE(&bigMarker, 1, sizeof(bigMarker), fp);
|
|
XFCLOSE(fp);
|
|
XMEMSET(&guarded, 0, sizeof(guarded));
|
|
rc = readKeyBlob(filename, &guarded.key);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
remove(filename);
|
|
|
|
/* Rejection branch: pub.size marker larger than the public area buffer. */
|
|
bigMarker = (UINT16)sizeof(pubAreaBuffer);
|
|
fp = openTestFileWrite(filename);
|
|
AssertNotNull(fp);
|
|
XFWRITE(&bigMarker, 1, sizeof(bigMarker), fp);
|
|
XFCLOSE(fp);
|
|
XMEMSET(&guarded, 0, sizeof(guarded));
|
|
rc = readKeyBlob(filename, &guarded.key);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
remove(filename);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "readKeyBlob priv overflow:");
|
|
}
|
|
#endif
|
|
|
|
/* Test creating key and exporting keyblob as buffer,
|
|
* importing and loading key. */
|
|
static void test_wolfTPM2_KeyBlob(TPM_ALG_ID alg)
|
|
{
|
|
int rc;
|
|
TPM_HANDLE handle;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY srk;
|
|
WOLFTPM2_KEYBLOB key;
|
|
byte blob[MAX_CONTEXT_SIZE];
|
|
TPMT_PUBLIC publicTemplate;
|
|
word32 privBufferSz, pubBufferSz;
|
|
char nameBuf[32];
|
|
|
|
XMEMSET(&srk, 0, sizeof(srk));
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
XMEMSET(&publicTemplate, 0, sizeof(publicTemplate));
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
if (alg == TPM_ALG_ECC)
|
|
handle = TPM2_DEMO_STORAGE_KEY_HANDLE;
|
|
else /* RSA */
|
|
handle = TPM2_DEMO_STORAGE_EC_KEY_HANDLE;
|
|
|
|
/* Load or create the SRK */
|
|
rc = wolfTPM2_ReadPublicKey(&dev, &srk, handle);
|
|
if ((rc & RC_MAX_FMT1) == TPM_RC_HANDLE) {
|
|
rc = wolfTPM2_CreateSRK(&dev, &srk, alg,
|
|
(byte*)gStorageKeyAuth, sizeof(gStorageKeyAuth)-1);
|
|
AssertIntEQ(rc, 0);
|
|
}
|
|
else {
|
|
srk.handle.auth.size = sizeof(gStorageKeyAuth)-1;
|
|
XMEMCPY(srk.handle.auth.buffer, gStorageKeyAuth, srk.handle.auth.size);
|
|
}
|
|
|
|
if (alg == TPM_ALG_ECC) {
|
|
rc = wolfTPM2_GetKeyTemplate_ECC(&publicTemplate,
|
|
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
|
|
TPMA_OBJECT_decrypt | TPMA_OBJECT_sign | TPMA_OBJECT_noDA,
|
|
TPM_ECC_NIST_P256, TPM_ALG_NULL);
|
|
}
|
|
else { /* RSA */
|
|
rc = wolfTPM2_GetKeyTemplate_RSA(&publicTemplate,
|
|
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
|
|
TPMA_OBJECT_decrypt | TPMA_OBJECT_sign | TPMA_OBJECT_noDA);
|
|
}
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Create key under SRK and get encrypted private and public from TPM */
|
|
rc = wolfTPM2_CreateKey(&dev, &key, &srk.handle, &publicTemplate,
|
|
(byte*)gKeyAuth, sizeof(gKeyAuth)-1);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Test getting size only */
|
|
rc = wolfTPM2_GetKeyBlobAsSeparateBuffers(NULL, &pubBufferSz,
|
|
NULL, &privBufferSz, &key);
|
|
AssertIntEQ(rc, LENGTH_ONLY_E);
|
|
|
|
AssertIntLT(pubBufferSz + privBufferSz, sizeof(blob));
|
|
|
|
/* Test exporting private and public parts separately */
|
|
rc = wolfTPM2_GetKeyBlobAsSeparateBuffers(blob, &pubBufferSz,
|
|
blob +pubBufferSz, &privBufferSz, &key);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Test getting size only */
|
|
rc = wolfTPM2_GetKeyBlobAsBuffer(NULL, sizeof(blob), &key);
|
|
AssertIntGT(rc, 0);
|
|
|
|
/* Export private and public key */
|
|
rc = wolfTPM2_GetKeyBlobAsBuffer(blob, sizeof(blob), &key);
|
|
AssertIntGT(rc, 0);
|
|
|
|
/* Reset the originally created key */
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
|
|
/* Load key blob (private/public) from buffer */
|
|
rc = wolfTPM2_SetKeyBlobFromBuffer(&key, blob, rc);
|
|
AssertIntEQ(rc, 0);
|
|
key.handle.auth.size = sizeof(gKeyAuth)-1;
|
|
XMEMCPY(key.handle.auth.buffer, gKeyAuth, key.handle.auth.size);
|
|
|
|
/* Load key to TPM and get temp handle */
|
|
rc = wolfTPM2_LoadKey(&dev, &key, &srk.handle);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
wolfTPM2_UnloadHandle(&dev, &key.handle);
|
|
wolfTPM2_UnloadHandle(&dev, &srk.handle);
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
snprintf(nameBuf, sizeof(nameBuf), "KeyBlob %s:", TPM2_GetAlgName(alg));
|
|
printf("Test TPM Wrapper: %-40s %s\n", nameBuf,
|
|
rc == 0 ? "Passed" : "Failed");
|
|
}
|
|
|
|
/* Test DecodeRsaDer/DecodeEccDer default attributes for private key imports */
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_ASN)
|
|
static void test_wolfTPM2_DecodeDer_DefaultAttribs(void)
|
|
{
|
|
#ifdef HAVE_ECC
|
|
int rc;
|
|
TPM2B_PUBLIC pub;
|
|
TPM2B_SENSITIVE sens;
|
|
TPMA_OBJECT attrs;
|
|
/* ECC P-256 private key DER (from certs/example-ecc256-key.der) */
|
|
static const byte eccKeyDer[] = {
|
|
0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x45, 0xb6, 0x69, 0x02,
|
|
0x73, 0x9c, 0x6c, 0x85, 0xa1, 0x38, 0x5b, 0x72, 0xe8, 0xe8, 0xc7,
|
|
0xac, 0xc4, 0x03, 0x8d, 0x53, 0x35, 0x04, 0xfa, 0x6c, 0x28, 0xdc,
|
|
0x34, 0x8d, 0xe1, 0xa8, 0x09, 0x8c, 0xa0, 0x0a, 0x06, 0x08, 0x2a,
|
|
0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42,
|
|
0x00, 0x04, 0xbb, 0x33, 0xac, 0x4c, 0x27, 0x50, 0x4a, 0xc6, 0x4a,
|
|
0xa5, 0x04, 0xc3, 0x3c, 0xde, 0x9f, 0x36, 0xdb, 0x72, 0x2d, 0xce,
|
|
0x94, 0xea, 0x2b, 0xfa, 0xcb, 0x20, 0x09, 0x39, 0x2c, 0x16, 0xe8,
|
|
0x61, 0x02, 0xe9, 0xaf, 0x4d, 0xd3, 0x02, 0x93, 0x9a, 0x31, 0x5b,
|
|
0x97, 0x92, 0x21, 0x7f, 0xf0, 0xcf, 0x18, 0xda, 0x91, 0x11, 0x02,
|
|
0x34, 0x86, 0xe8, 0x20, 0x58, 0x33, 0x0b, 0x80, 0x34, 0x89, 0xd8
|
|
};
|
|
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
XMEMSET(&sens, 0, sizeof(sens));
|
|
|
|
/* Call with attributes=0 and sens!=NULL (private key import) */
|
|
rc = wolfTPM2_DecodeEccDer(eccKeyDer, (word32)sizeof(eccKeyDer),
|
|
&pub, &sens, 0);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
attrs = pub.publicArea.objectAttributes;
|
|
|
|
/* For imported private keys, restricted must NOT be set when both
|
|
* sign and decrypt are set (TPM 2.0 Part 2 Table 31) */
|
|
AssertIntEQ(attrs & TPMA_OBJECT_restricted, 0);
|
|
|
|
/* sensitiveDataOrigin must NOT be set for imported keys */
|
|
AssertIntEQ(attrs & TPMA_OBJECT_sensitiveDataOrigin, 0);
|
|
|
|
/* sign and decrypt should both be set for general-purpose imported keys */
|
|
AssertTrue(attrs & TPMA_OBJECT_sign);
|
|
AssertTrue(attrs & TPMA_OBJECT_decrypt);
|
|
|
|
/* userWithAuth should be set */
|
|
AssertTrue(attrs & TPMA_OBJECT_userWithAuth);
|
|
|
|
/* When both sign and decrypt are set, scheme must be NULL */
|
|
AssertIntEQ(pub.publicArea.parameters.eccDetail.scheme.scheme,
|
|
TPM_ALG_NULL);
|
|
#endif
|
|
/* Note: DecodeRsaDer uses the same default attribute and scheme logic
|
|
* as DecodeEccDer — validated by the ECC test above. RSA DER key is
|
|
* too large (1217 bytes) to embed inline for a unit test. */
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "DecodeDer DefaultAttribs:");
|
|
}
|
|
|
|
/* Verify the AES wrap-key strength selected by wolfTPM2_DecodeRsaDer /
|
|
* wolfTPM2_DecodeEccDer scales with the imported parent's strength: 2048-bit
|
|
* RSA / P-256 -> AES-128, 3072-bit RSA / P-384 -> AES-256. */
|
|
static void test_wolfTPM2_DecodeDer_WrapKeyScaling(void)
|
|
{
|
|
#if defined(HAVE_ECC) && defined(WOLFSSL_KEY_GEN) && \
|
|
!defined(WC_NO_RNG)
|
|
int rc;
|
|
WC_RNG rng;
|
|
ecc_key eccKey;
|
|
byte derBuf[1024];
|
|
int derSz;
|
|
TPM2B_PUBLIC pub;
|
|
const TPMA_OBJECT restrictedDecrypt =
|
|
TPMA_OBJECT_restricted | TPMA_OBJECT_decrypt;
|
|
#if !defined(NO_RSA)
|
|
RsaKey rsaKey;
|
|
byte rsaDer[2048];
|
|
int rsaDerSz;
|
|
#endif
|
|
|
|
AssertIntEQ(wc_InitRng(&rng), 0);
|
|
|
|
/* P-256 -> AES-128 */
|
|
AssertIntEQ(wc_ecc_init(&eccKey), 0);
|
|
AssertIntEQ(wc_ecc_make_key_ex(&rng, 32, &eccKey, ECC_SECP256R1), 0);
|
|
derSz = wc_EccKeyToDer(&eccKey, derBuf, (word32)sizeof(derBuf));
|
|
AssertIntGT(derSz, 0);
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
rc = wolfTPM2_DecodeEccDer(derBuf, (word32)derSz, &pub, NULL,
|
|
restrictedDecrypt);
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntEQ(pub.publicArea.parameters.eccDetail.symmetric.algorithm,
|
|
TPM_ALG_AES);
|
|
AssertIntEQ(pub.publicArea.parameters.eccDetail.symmetric.keyBits.aes,
|
|
128);
|
|
wc_ecc_free(&eccKey);
|
|
|
|
/* P-384 -> AES-256 */
|
|
AssertIntEQ(wc_ecc_init(&eccKey), 0);
|
|
AssertIntEQ(wc_ecc_make_key_ex(&rng, 48, &eccKey, ECC_SECP384R1), 0);
|
|
derSz = wc_EccKeyToDer(&eccKey, derBuf, (word32)sizeof(derBuf));
|
|
AssertIntGT(derSz, 0);
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
rc = wolfTPM2_DecodeEccDer(derBuf, (word32)derSz, &pub, NULL,
|
|
restrictedDecrypt);
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntEQ(pub.publicArea.parameters.eccDetail.symmetric.algorithm,
|
|
TPM_ALG_AES);
|
|
AssertIntEQ(pub.publicArea.parameters.eccDetail.symmetric.keyBits.aes,
|
|
256);
|
|
wc_ecc_free(&eccKey);
|
|
|
|
#if !defined(NO_RSA)
|
|
/* 2048-bit RSA -> AES-128 */
|
|
AssertIntEQ(wc_InitRsaKey(&rsaKey, NULL), 0);
|
|
AssertIntEQ(wc_MakeRsaKey(&rsaKey, 2048, WC_RSA_EXPONENT, &rng), 0);
|
|
rsaDerSz = wc_RsaKeyToDer(&rsaKey, rsaDer, (word32)sizeof(rsaDer));
|
|
AssertIntGT(rsaDerSz, 0);
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
rc = wolfTPM2_DecodeRsaDer(rsaDer, (word32)rsaDerSz, &pub, NULL,
|
|
restrictedDecrypt);
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntEQ(pub.publicArea.parameters.rsaDetail.symmetric.algorithm,
|
|
TPM_ALG_AES);
|
|
AssertIntEQ(pub.publicArea.parameters.rsaDetail.symmetric.keyBits.aes,
|
|
128);
|
|
wc_FreeRsaKey(&rsaKey);
|
|
|
|
/* 3072-bit RSA -> AES-256 */
|
|
AssertIntEQ(wc_InitRsaKey(&rsaKey, NULL), 0);
|
|
AssertIntEQ(wc_MakeRsaKey(&rsaKey, 3072, WC_RSA_EXPONENT, &rng), 0);
|
|
rsaDerSz = wc_RsaKeyToDer(&rsaKey, rsaDer, (word32)sizeof(rsaDer));
|
|
AssertIntGT(rsaDerSz, 0);
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
rc = wolfTPM2_DecodeRsaDer(rsaDer, (word32)rsaDerSz, &pub, NULL,
|
|
restrictedDecrypt);
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntEQ(pub.publicArea.parameters.rsaDetail.symmetric.algorithm,
|
|
TPM_ALG_AES);
|
|
AssertIntEQ(pub.publicArea.parameters.rsaDetail.symmetric.keyBits.aes,
|
|
256);
|
|
wc_FreeRsaKey(&rsaKey);
|
|
#endif /* !NO_RSA */
|
|
|
|
wc_FreeRng(&rng);
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "DecodeDer WrapKeyScaling:");
|
|
#endif /* HAVE_ECC && WOLFSSL_KEY_GEN && !WC_NO_RNG */
|
|
}
|
|
#endif /* !WOLFTPM2_NO_WOLFCRYPT && !NO_ASN */
|
|
|
|
/* Test NULL parentKey handling in LoadRsaPrivateKey_ex and LoadEccPrivateKey */
|
|
static void test_wolfTPM2_LoadPrivateKey_NullParent(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY key;
|
|
#ifndef NO_RSA
|
|
/* Dummy RSA key material for testing NULL parentKey handling */
|
|
byte rsaPub[1] = {0};
|
|
byte rsaPriv[1] = {0};
|
|
#endif
|
|
#ifdef HAVE_ECC
|
|
/* Dummy ECC key material for testing NULL parentKey handling */
|
|
byte eccPubX[32] = {0};
|
|
byte eccPubY[32] = {0};
|
|
byte eccPriv[32] = {0};
|
|
#endif
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
|
|
/* Test NULL parentKey does not crash (should not dereference NULL) */
|
|
#ifndef NO_RSA
|
|
rc = wolfTPM2_LoadRsaPrivateKey_ex(&dev, NULL, &key, rsaPub, sizeof(rsaPub),
|
|
RSA_DEFAULT_PUBLIC_EXPONENT, rsaPriv, sizeof(rsaPriv),
|
|
TPM_ALG_NULL, TPM_ALG_NULL);
|
|
/* rc may fail due to no real TPM, but must not crash */
|
|
AssertIntNE(rc, BAD_FUNC_ARG);
|
|
#endif
|
|
#ifdef HAVE_ECC
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
rc = wolfTPM2_LoadEccPrivateKey(&dev, NULL, &key, TPM_ECC_NIST_P256,
|
|
eccPubX, sizeof(eccPubX), eccPubY, sizeof(eccPubY),
|
|
eccPriv, sizeof(eccPriv));
|
|
/* rc may fail due to no real TPM, but must not crash */
|
|
AssertIntNE(rc, BAD_FUNC_ARG);
|
|
#endif
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "LoadPrivateKey NullParent:");
|
|
}
|
|
|
|
static void test_wolfTPM2_EncryptDecryptBlock(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY key;
|
|
byte in[MAX_AES_BLOCK_SIZE_BYTES];
|
|
byte out[MAX_AES_BLOCK_SIZE_BYTES];
|
|
byte iv[MAX_AES_BLOCK_SIZE_BYTES];
|
|
byte bigIv[MAX_SYM_BLOCK_SIZE + 1];
|
|
|
|
XMEMSET(in, 0, sizeof(in));
|
|
XMEMSET(out, 0, sizeof(out));
|
|
XMEMSET(iv, 0, sizeof(iv));
|
|
XMEMSET(bigIv, 0, sizeof(bigIv));
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
|
|
/* CBC mode: NULL IV should return BAD_FUNC_ARG */
|
|
key.pub.publicArea.parameters.symDetail.sym.mode.aes = TPM_ALG_CBC;
|
|
rc = wolfTPM2_EncryptDecryptBlock(&dev, &key, in, out,
|
|
sizeof(in), NULL, 0, WOLFTPM2_ENCRYPT);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* CBC mode: ivSz == 0 with non-NULL iv should return BAD_FUNC_ARG */
|
|
rc = wolfTPM2_EncryptDecryptBlock(&dev, &key, in, out,
|
|
sizeof(in), iv, 0, WOLFTPM2_ENCRYPT);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* CFB mode: NULL IV should return BAD_FUNC_ARG */
|
|
key.pub.publicArea.parameters.symDetail.sym.mode.aes = TPM_ALG_CFB;
|
|
rc = wolfTPM2_EncryptDecryptBlock(&dev, &key, in, out,
|
|
sizeof(in), NULL, 0, WOLFTPM2_ENCRYPT);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* CTR mode: NULL IV should return BAD_FUNC_ARG */
|
|
key.pub.publicArea.parameters.symDetail.sym.mode.aes = TPM_ALG_CTR;
|
|
rc = wolfTPM2_EncryptDecryptBlock(&dev, &key, in, out,
|
|
sizeof(in), NULL, 0, WOLFTPM2_ENCRYPT);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* OFB mode: NULL IV should return BAD_FUNC_ARG */
|
|
key.pub.publicArea.parameters.symDetail.sym.mode.aes = TPM_ALG_OFB;
|
|
rc = wolfTPM2_EncryptDecryptBlock(&dev, &key, in, out,
|
|
sizeof(in), NULL, 0, WOLFTPM2_ENCRYPT);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* Oversized IV should return BUFFER_E */
|
|
key.pub.publicArea.parameters.symDetail.sym.mode.aes = TPM_ALG_CBC;
|
|
rc = wolfTPM2_EncryptDecryptBlock(&dev, &key, in, out,
|
|
sizeof(in), bigIv, sizeof(bigIv), WOLFTPM2_ENCRYPT);
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
|
|
/* ECB mode: NULL IV should NOT return BAD_FUNC_ARG */
|
|
key.pub.publicArea.parameters.symDetail.sym.mode.aes = TPM_ALG_ECB;
|
|
rc = wolfTPM2_EncryptDecryptBlock(&dev, &key, in, out,
|
|
sizeof(in), NULL, 0, WOLFTPM2_ENCRYPT);
|
|
AssertIntNE(rc, BAD_FUNC_ARG);
|
|
|
|
/* NULL mode: NULL IV should NOT return BAD_FUNC_ARG */
|
|
key.pub.publicArea.parameters.symDetail.sym.mode.aes = TPM_ALG_NULL;
|
|
rc = wolfTPM2_EncryptDecryptBlock(&dev, &key, in, out,
|
|
sizeof(in), NULL, 0, WOLFTPM2_ENCRYPT);
|
|
AssertIntNE(rc, BAD_FUNC_ARG);
|
|
|
|
/* CBC mode: non-block-aligned inOutSz must return BAD_FUNC_ARG. */
|
|
key.pub.publicArea.parameters.symDetail.sym.mode.aes = TPM_ALG_CBC;
|
|
rc = wolfTPM2_EncryptDecryptBlock(&dev, &key, in, out,
|
|
MAX_AES_BLOCK_SIZE_BYTES - 1, iv, sizeof(iv), WOLFTPM2_ENCRYPT);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* ECB mode: non-block-aligned inOutSz must return BAD_FUNC_ARG. */
|
|
key.pub.publicArea.parameters.symDetail.sym.mode.aes = TPM_ALG_ECB;
|
|
rc = wolfTPM2_EncryptDecryptBlock(&dev, &key, in, out,
|
|
MAX_AES_BLOCK_SIZE_BYTES + 1, NULL, 0, WOLFTPM2_ENCRYPT);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* CFB mode: non-block-aligned inOutSz is a stream length and must
|
|
* bypass the block-alignment gate (so the return must not be the
|
|
* BAD_FUNC_ARG produced by the alignment check). */
|
|
key.pub.publicArea.parameters.symDetail.sym.mode.aes = TPM_ALG_CFB;
|
|
rc = wolfTPM2_EncryptDecryptBlock(&dev, &key, in, out,
|
|
MAX_AES_BLOCK_SIZE_BYTES - 1, iv, sizeof(iv), WOLFTPM2_ENCRYPT);
|
|
AssertIntNE(rc, BAD_FUNC_ARG);
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper:\tEncryptDecryptBlock IV validate:\tPassed\n");
|
|
}
|
|
|
|
#ifdef HAVE_ECC
|
|
static void test_wolfTPM2_ImportEccPrivateKeySeed_ErrorPaths(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY parentKey;
|
|
WOLFTPM2_KEYBLOB keyBlob;
|
|
byte eccPubX[32];
|
|
byte eccPubY[32];
|
|
byte eccPriv[32];
|
|
/* Wrong-size seed to trigger seed size mismatch error path.
|
|
* WOLFTPM2_WRAP_DIGEST is SHA256 (digestSz=32), so seedSz=1 mismatches. */
|
|
byte seed[1] = {0x42};
|
|
TPMA_OBJECT attrs = (TPMA_OBJECT_sign | TPMA_OBJECT_userWithAuth |
|
|
TPMA_OBJECT_noDA);
|
|
TPM2B_SENSITIVE sens;
|
|
byte big[MAX_ECC_KEY_BYTES];
|
|
word32 capX, capY, capP;
|
|
|
|
XMEMSET(eccPubX, 0x01, sizeof(eccPubX));
|
|
XMEMSET(eccPubY, 0x02, sizeof(eccPubY));
|
|
XMEMSET(eccPriv, 0x03, sizeof(eccPriv));
|
|
XMEMSET(big, 0x05, sizeof(big));
|
|
capX = (word32)sizeof(keyBlob.pub.publicArea.unique.ecc.x.buffer);
|
|
capY = (word32)sizeof(keyBlob.pub.publicArea.unique.ecc.y.buffer);
|
|
capP = (word32)sizeof(sens.sensitiveArea.sensitive.ecc.buffer);
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
XMEMSET(&parentKey, 0, sizeof(parentKey));
|
|
XMEMSET(&keyBlob, 0, sizeof(keyBlob));
|
|
|
|
/* Seed size mismatch must return BAD_FUNC_ARG (and zero sens) */
|
|
rc = wolfTPM2_ImportEccPrivateKeySeed(&dev, &parentKey, &keyBlob,
|
|
TPM_ECC_NIST_P256, eccPubX, sizeof(eccPubX), eccPubY, sizeof(eccPubY),
|
|
eccPriv, sizeof(eccPriv), attrs, seed, sizeof(seed));
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* Each component one byte over its TPM2B capacity must return BUFFER_E */
|
|
rc = wolfTPM2_ImportEccPrivateKeySeed(&dev, &parentKey, &keyBlob,
|
|
TPM_ECC_NIST_P256, big, capX + 1, big, capY, big, capP,
|
|
attrs, seed, sizeof(seed));
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
rc = wolfTPM2_ImportEccPrivateKeySeed(&dev, &parentKey, &keyBlob,
|
|
TPM_ECC_NIST_P256, big, capX, big, capY + 1, big, capP,
|
|
attrs, seed, sizeof(seed));
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
rc = wolfTPM2_ImportEccPrivateKeySeed(&dev, &parentKey, &keyBlob,
|
|
TPM_ECC_NIST_P256, big, capX, big, capY, big, capP + 1,
|
|
attrs, seed, sizeof(seed));
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
|
|
/* Exact-fit components must clear the bounds check (fail later at the
|
|
* seed size check, not BUFFER_E) - pins '>' against '>=' and deletion */
|
|
rc = wolfTPM2_ImportEccPrivateKeySeed(&dev, &parentKey, &keyBlob,
|
|
TPM_ECC_NIST_P256, big, capX, big, capY, big, capP,
|
|
attrs, seed, sizeof(seed));
|
|
AssertIntNE(rc, BUFFER_E);
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper:\tImportEccSeed error paths:\tPassed\n");
|
|
}
|
|
#endif /* HAVE_ECC */
|
|
|
|
#ifndef NO_RSA
|
|
static void test_wolfTPM2_ImportRsaPrivateKeySeed_ErrorPaths(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY parentKey;
|
|
WOLFTPM2_KEYBLOB keyBlob;
|
|
TPM2B_SENSITIVE sens;
|
|
byte big[MAX_RSA_KEY_BYTES];
|
|
byte seed[1] = {0x42};
|
|
word32 capPub, capPriv;
|
|
TPMA_OBJECT attrs = (TPMA_OBJECT_sign | TPMA_OBJECT_userWithAuth |
|
|
TPMA_OBJECT_noDA);
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
XMEMSET(&parentKey, 0, sizeof(parentKey));
|
|
XMEMSET(&keyBlob, 0, sizeof(keyBlob));
|
|
XMEMSET(big, 0x05, sizeof(big));
|
|
capPub = (word32)sizeof(keyBlob.pub.publicArea.unique.rsa.buffer);
|
|
capPriv = (word32)sizeof(sens.sensitiveArea.sensitive.rsa.buffer);
|
|
|
|
/* Public/private modulus one byte over capacity must return BUFFER_E */
|
|
rc = wolfTPM2_ImportRsaPrivateKeySeed(&dev, &parentKey, &keyBlob,
|
|
big, capPub + 1, 0x10001, big, capPriv,
|
|
TPM_ALG_NULL, WOLFTPM2_WRAP_DIGEST, attrs, seed, sizeof(seed));
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
rc = wolfTPM2_ImportRsaPrivateKeySeed(&dev, &parentKey, &keyBlob,
|
|
big, capPub, 0x10001, big, capPriv + 1,
|
|
TPM_ALG_NULL, WOLFTPM2_WRAP_DIGEST, attrs, seed, sizeof(seed));
|
|
AssertIntEQ(rc, BUFFER_E);
|
|
|
|
/* Exact-fit components clear the bounds check (fail later at the seed
|
|
* size check, not BUFFER_E) - pins '>' against '>=' and deletion */
|
|
rc = wolfTPM2_ImportRsaPrivateKeySeed(&dev, &parentKey, &keyBlob,
|
|
big, capPub, 0x10001, big, capPriv,
|
|
TPM_ALG_NULL, WOLFTPM2_WRAP_DIGEST, attrs, seed, sizeof(seed));
|
|
AssertIntNE(rc, BUFFER_E);
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper:\tImportRsaSeed error paths:\tPassed\n");
|
|
}
|
|
#endif /* !NO_RSA */
|
|
|
|
static void test_wolfTPM2_NVStoreKey_BoundaryChecks(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY key;
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
|
|
/* Owner hierarchy: handle below PERSISTENT_FIRST must fail */
|
|
rc = wolfTPM2_NVStoreKey(&dev, TPM_RH_OWNER, &key,
|
|
PERSISTENT_FIRST - 1);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* Owner hierarchy: handle above PERSISTENT_LAST must fail */
|
|
rc = wolfTPM2_NVStoreKey(&dev, TPM_RH_OWNER, &key,
|
|
PERSISTENT_LAST + 1);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* Owner hierarchy: PERSISTENT_FIRST must NOT fail with BAD_FUNC_ARG */
|
|
key.handle.hndl = 0; /* ensure not already persistent */
|
|
rc = wolfTPM2_NVStoreKey(&dev, TPM_RH_OWNER, &key,
|
|
PERSISTENT_FIRST);
|
|
AssertIntNE(rc, BAD_FUNC_ARG);
|
|
|
|
/* Owner hierarchy: PERSISTENT_LAST must NOT fail with BAD_FUNC_ARG */
|
|
rc = wolfTPM2_NVStoreKey(&dev, TPM_RH_OWNER, &key,
|
|
PERSISTENT_LAST);
|
|
AssertIntNE(rc, BAD_FUNC_ARG);
|
|
|
|
/* Platform hierarchy: handle below PLATFORM_PERSISTENT must fail */
|
|
rc = wolfTPM2_NVStoreKey(&dev, TPM_RH_PLATFORM, &key,
|
|
PLATFORM_PERSISTENT - 1);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
/* Platform hierarchy: PLATFORM_PERSISTENT must NOT fail with BAD_FUNC_ARG */
|
|
rc = wolfTPM2_NVStoreKey(&dev, TPM_RH_PLATFORM, &key,
|
|
PLATFORM_PERSISTENT);
|
|
AssertIntNE(rc, BAD_FUNC_ARG);
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper:\tNVStoreKey boundary checks:\tPassed\n");
|
|
}
|
|
|
|
static void test_wolfTPM2_NVDeleteKey_BoundaryChecks(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY key;
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
|
|
/* Handle below PERSISTENT_FIRST: not persistent, early-return success */
|
|
key.handle.hndl = PERSISTENT_FIRST - 1;
|
|
rc = wolfTPM2_NVDeleteKey(&dev, TPM_RH_OWNER, &key);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* Handle above PERSISTENT_LAST: not persistent, early-return success */
|
|
key.handle.hndl = PERSISTENT_LAST + 1;
|
|
rc = wolfTPM2_NVDeleteKey(&dev, TPM_RH_OWNER, &key);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* Handle equal to PERSISTENT_FIRST: IS persistent, must NOT early-return */
|
|
key.handle.hndl = PERSISTENT_FIRST;
|
|
rc = wolfTPM2_NVDeleteKey(&dev, TPM_RH_OWNER, &key);
|
|
AssertIntNE(rc, TPM_RC_SUCCESS); /* will fail at TPM, but not early-return */
|
|
|
|
/* Handle equal to PERSISTENT_LAST: IS persistent, must NOT early-return */
|
|
key.handle.hndl = PERSISTENT_LAST;
|
|
rc = wolfTPM2_NVDeleteKey(&dev, TPM_RH_OWNER, &key);
|
|
AssertIntNE(rc, TPM_RC_SUCCESS); /* will fail at TPM, but not early-return */
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper:\tNVDeleteKey boundary checks:\tPassed\n");
|
|
}
|
|
|
|
static void test_wolfTPM2_UnloadHandle_PersistentGuard(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_HANDLE handle;
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
XMEMSET(&handle, 0, sizeof(handle));
|
|
|
|
/* Persistent handles must be skipped (return SUCCESS, no FlushContext) */
|
|
handle.hndl = PERSISTENT_FIRST;
|
|
rc = wolfTPM2_UnloadHandle(&dev, &handle);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
handle.hndl = PERSISTENT_LAST;
|
|
rc = wolfTPM2_UnloadHandle(&dev, &handle);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* Null handle must be skipped */
|
|
handle.hndl = TPM_RH_NULL;
|
|
rc = wolfTPM2_UnloadHandle(&dev, &handle);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* Zero handle must be skipped */
|
|
handle.hndl = 0;
|
|
rc = wolfTPM2_UnloadHandle(&dev, &handle);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* Handle just outside persistent range: NOT skipped (attempts flush) */
|
|
handle.hndl = PERSISTENT_FIRST - 1;
|
|
rc = wolfTPM2_UnloadHandle(&dev, &handle);
|
|
AssertIntNE(rc, TPM_RC_SUCCESS);
|
|
|
|
handle.hndl = PERSISTENT_LAST + 1;
|
|
rc = wolfTPM2_UnloadHandle(&dev, &handle);
|
|
AssertIntNE(rc, TPM_RC_SUCCESS);
|
|
|
|
wolfTPM2_Cleanup(&dev);
|
|
|
|
printf("Test TPM Wrapper:\tUnloadHandle persistent guard:\tPassed\n");
|
|
}
|
|
|
|
static void test_TPM2_GetHashDigestSize_AllAlgs(void)
|
|
{
|
|
/* Standard algorithms already supported */
|
|
AssertIntEQ(TPM2_GetHashDigestSize(TPM_ALG_SHA1), TPM_SHA_DIGEST_SIZE);
|
|
AssertIntEQ(TPM2_GetHashDigestSize(TPM_ALG_SHA256), TPM_SHA256_DIGEST_SIZE);
|
|
AssertIntEQ(TPM2_GetHashDigestSize(TPM_ALG_SHA384), TPM_SHA384_DIGEST_SIZE);
|
|
AssertIntEQ(TPM2_GetHashDigestSize(TPM_ALG_SHA512), TPM_SHA512_DIGEST_SIZE);
|
|
|
|
/* SM3 and SHA3 must return correct non-zero digest sizes */
|
|
AssertIntEQ(TPM2_GetHashDigestSize(TPM_ALG_SM3_256), TPM_SHA256_DIGEST_SIZE);
|
|
AssertIntEQ(TPM2_GetHashDigestSize(TPM_ALG_SHA3_256), TPM_SHA256_DIGEST_SIZE);
|
|
AssertIntEQ(TPM2_GetHashDigestSize(TPM_ALG_SHA3_384), TPM_SHA384_DIGEST_SIZE);
|
|
AssertIntEQ(TPM2_GetHashDigestSize(TPM_ALG_SHA3_512), TPM_SHA512_DIGEST_SIZE);
|
|
|
|
/* Unknown algorithm must return 0 */
|
|
AssertIntEQ(TPM2_GetHashDigestSize(TPM_ALG_NULL), 0);
|
|
|
|
printf("Test TPM2:\t\tGetHashDigestSize all algs:\tPassed\n");
|
|
}
|
|
|
|
/* These PQC unit tests call both ML-DSA and ML-KEM wrappers, so they compile
|
|
* only when both families are present (a WOLFTPM_NO_MLDSA or WOLFTPM_NO_MLKEM
|
|
* build excludes the matching wrapper definitions). CI always builds full
|
|
* PQC, so coverage is unchanged there. */
|
|
#if defined(WOLFTPM_MLDSA) && defined(WOLFTPM_MLKEM)
|
|
/* Post-Quantum Cryptography (PQC) Unit Tests - TPM 2.0 v185 */
|
|
|
|
/* TODO: Remove TPM_RC_COMMAND_CODE skip logic once we have a TPM simulator
|
|
* or hardware that supports TPM 2.0 v1.85 PQC commands. Currently the IBM SW
|
|
* TPM does not support ML-DSA/ML-KEM, so tests skip with TPM_RC_COMMAND_CODE.
|
|
* When real support is available, update tests to require success. */
|
|
|
|
/* Test ML-DSA Sign Sequence (Start, Update, Complete) */
|
|
/* Test ML-DSA Sign Sequence; writes sig to caller buffer on success. */
|
|
static void test_wolfTPM2_MLDSA_SignSequence(WOLFTPM2_DEV* dev,
|
|
WOLFTPM2_KEY* mldsaKey, const byte* message, int messageSz,
|
|
byte* sig, int* sigSz)
|
|
{
|
|
int rc;
|
|
TPM_HANDLE sequenceHandle;
|
|
byte context[16];
|
|
int contextSz = 0;
|
|
|
|
XMEMSET(context, 0, sizeof(context));
|
|
|
|
rc = wolfTPM2_SignSequenceStart(dev, mldsaKey, context, contextSz,
|
|
&sequenceHandle);
|
|
if (rc == TPM_RC_VALUE || rc == TPM_RC_SCHEME ||
|
|
rc == TPM_RC_COMMAND_CODE || rc == (int)(RC_VER1 + 0x043)) {
|
|
printf("Test TPM Wrapper: %-40s Skipped (not supported)\n",
|
|
"ML-DSA Sign Sequence:");
|
|
*sigSz = 0;
|
|
return;
|
|
}
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Pure-MLDSA rejects SequenceUpdate (Sec.17.5 TPM_RC_ONE_SHOT_SIGNATURE)
|
|
* — the message must be supplied in one shot at Complete. */
|
|
rc = wolfTPM2_SignSequenceComplete(dev, sequenceHandle, mldsaKey,
|
|
message, messageSz, sig, sigSz);
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntGT(*sigSz, 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "ML-DSA Sign Sequence:");
|
|
}
|
|
|
|
/* Test ML-DSA Verify Sequence (Start, Update, Complete) */
|
|
static void test_wolfTPM2_MLDSA_VerifySequence(WOLFTPM2_DEV* dev,
|
|
WOLFTPM2_KEY* mldsaKey, const byte* message, int messageSz,
|
|
const byte* sig, int sigSz)
|
|
{
|
|
int rc;
|
|
TPM_HANDLE sequenceHandle;
|
|
|
|
TPMT_TK_VERIFIED validation;
|
|
|
|
XMEMSET(&validation, 0, sizeof(validation));
|
|
|
|
if (sigSz <= 0) {
|
|
printf("Test TPM Wrapper: %-40s Skipped (no signature)\n",
|
|
"ML-DSA Verify Sequence:");
|
|
return;
|
|
}
|
|
rc = wolfTPM2_VerifySequenceStart(dev, mldsaKey, NULL, 0, &sequenceHandle);
|
|
if (rc == TPM_RC_VALUE || rc == TPM_RC_SCHEME ||
|
|
rc == TPM_RC_COMMAND_CODE || rc == (int)(RC_VER1 + 0x043)) {
|
|
printf("Test TPM Wrapper: %-40s Skipped (not supported)\n",
|
|
"ML-DSA Verify Sequence:");
|
|
return;
|
|
}
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Verify sequences accept SequenceUpdate per Part 3 Sec.20.3 */
|
|
rc = wolfTPM2_VerifySequenceUpdate(dev, sequenceHandle, message, messageSz);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
rc = wolfTPM2_VerifySequenceComplete(dev, sequenceHandle, mldsaKey,
|
|
NULL, 0, sig, sigSz, &validation);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "ML-DSA Verify Sequence:");
|
|
}
|
|
|
|
/* Regression for the SignSequenceComplete slot-1 auth fix.
|
|
* Creates a separate ML-DSA-65 primary with a NON-EMPTY user auth and runs
|
|
* a sign sequence end-to-end. The wrapper now sets both auth slots
|
|
* (slot 0 = sequence handle, slot 1 = key handle); if a future change drops
|
|
* the slot-1 SetAuthHandle call, the TPM rejects Complete with TPM_RC_BAD_AUTH. */
|
|
static void test_wolfTPM2_MLDSA_SignSequence_NonEmptyAuth(WOLFTPM2_DEV* dev,
|
|
const TPMT_PUBLIC* mldsaPub)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_KEY key;
|
|
TPMT_PUBLIC pub;
|
|
static const byte gAuth[] = { 'p','q','c','_','a','u','t','h' };
|
|
byte sig[5000];
|
|
int sigSz = (int)sizeof(sig);
|
|
static const byte gMsg[] = "Auth-bearing ML-DSA test message";
|
|
int msgSz = (int)sizeof(gMsg) - 1;
|
|
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
XMEMCPY(&pub, mldsaPub, sizeof(pub));
|
|
|
|
rc = wolfTPM2_CreatePrimaryKey(dev, &key, TPM_RH_OWNER, &pub,
|
|
gAuth, (int)sizeof(gAuth));
|
|
if (rc == TPM_RC_VALUE || rc == TPM_RC_SCHEME ||
|
|
rc == TPM_RC_COMMAND_CODE || rc == (int)(RC_VER1 + 0x043)) {
|
|
printf("Test TPM Wrapper: %-40s Skipped (not supported)\n",
|
|
"ML-DSA Sign Seq w/ key auth:");
|
|
return;
|
|
}
|
|
AssertIntEQ(rc, 0);
|
|
|
|
test_wolfTPM2_MLDSA_SignSequence(dev, &key, gMsg, msgSz, sig, &sigSz);
|
|
|
|
wolfTPM2_UnloadHandle(dev, &key.handle);
|
|
printf("Test TPM Wrapper: %-40s Passed\n",
|
|
"ML-DSA Sign Seq w/ key auth:");
|
|
}
|
|
|
|
/* Regression for the VerifySequenceComplete data-chain fix.
|
|
*
|
|
* The wrapper used to silently drop the data/dataSz arguments; the fix
|
|
* folds them in via an internal SequenceUpdate before Complete. Uses a
|
|
* Hash-ML-DSA-65 key (NOT the existing Pure ML-DSA + allowExternalMu key)
|
|
* because Hash-ML-DSA derives the verified message from the SHA-256
|
|
* digest of every byte streamed through SequenceUpdate — so dropping the
|
|
* Complete data argument actually changes the digest the signature is
|
|
* verified against. (Pure ML-DSA + allowExternalMu accepts a 64-byte μ
|
|
* digest directly and would not detect the drop.)
|
|
*
|
|
* If the silent-drop regresses, the verify sees only the first half of
|
|
* the message, computes a different digest from what the signature is
|
|
* over, and TPM_RC_SIGNATURE comes back. */
|
|
static void test_wolfTPM2_MLDSA_VerifySequence_DataChain(WOLFTPM2_DEV* dev)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_KEY hashKey;
|
|
TPMT_PUBLIC pub;
|
|
TPM_HANDLE seqHandle;
|
|
TPMT_TK_VERIFIED validation;
|
|
byte sig[5000];
|
|
int sigSz = (int)sizeof(sig);
|
|
static const byte msg[] =
|
|
"Hash-ML-DSA data-chain regression message: covers HIGH-3";
|
|
int msgSz = (int)sizeof(msg) - 1;
|
|
int firstHalf;
|
|
|
|
XMEMSET(&hashKey, 0, sizeof(hashKey));
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
XMEMSET(&validation, 0, sizeof(validation));
|
|
|
|
rc = wolfTPM2_GetKeyTemplate_HASH_MLDSA(&pub,
|
|
TPMA_OBJECT_sign | TPMA_OBJECT_fixedTPM | TPMA_OBJECT_fixedParent |
|
|
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth,
|
|
TPM_MLDSA_65, TPM_ALG_SHA256);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
rc = wolfTPM2_CreatePrimaryKey(dev, &hashKey, TPM_RH_OWNER, &pub, NULL, 0);
|
|
if (rc == TPM_RC_VALUE || rc == TPM_RC_SCHEME ||
|
|
rc == TPM_RC_COMMAND_CODE || rc == (int)(RC_VER1 + 0x043)) {
|
|
printf("Test TPM Wrapper: %-40s Skipped (not supported)\n",
|
|
"ML-DSA Verify Seq data-chain:");
|
|
return;
|
|
}
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Sign the full message in one shot via SignSequence (Hash-ML-DSA
|
|
* accepts SequenceUpdate; doing it all via Complete's data arg here
|
|
* is fine and simpler). */
|
|
test_wolfTPM2_MLDSA_SignSequence(dev, &hashKey, msg, msgSz, sig, &sigSz);
|
|
if (sigSz <= 0) {
|
|
wolfTPM2_UnloadHandle(dev, &hashKey.handle);
|
|
return;
|
|
}
|
|
|
|
/* Verify with the message split: first half via SequenceUpdate, second
|
|
* half via Complete's data arg. The fix's internal SequenceUpdate folds
|
|
* the second half before Complete; if the bug regresses, only the first
|
|
* half is in the sequence and the digest diverges from the signature's. */
|
|
firstHalf = msgSz / 2;
|
|
rc = wolfTPM2_VerifySequenceStart(dev, &hashKey, NULL, 0, &seqHandle);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
rc = wolfTPM2_VerifySequenceUpdate(dev, seqHandle, msg, firstHalf);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
rc = wolfTPM2_VerifySequenceComplete(dev, seqHandle, &hashKey,
|
|
msg + firstHalf, msgSz - firstHalf, sig, sigSz, &validation);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
wolfTPM2_UnloadHandle(dev, &hashKey.handle);
|
|
printf("Test TPM Wrapper: %-40s Passed\n",
|
|
"ML-DSA Verify Seq data-chain:");
|
|
}
|
|
|
|
/* Hash-ML-DSA streaming sign coverage: split the message across multiple
|
|
* wolfTPM2_SignSequenceUpdate calls then sign with an empty trailing
|
|
* buffer at Complete. Verifies the sig end-to-end. Also exercises the
|
|
* argument-validation paths (NULL dev / NULL data / dataSz<=0 /
|
|
* dataSz > buffer) — the wrapper is the documented streaming-update
|
|
* mechanism for Hash-ML-DSA so it needs direct test coverage. */
|
|
static void test_wolfTPM2_HashMLDSA_SignSequence_Streaming(WOLFTPM2_DEV* dev)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_KEY hashKey;
|
|
TPMT_PUBLIC pub;
|
|
TPM_HANDLE seqHandle;
|
|
TPMT_TK_VERIFIED validation;
|
|
byte sig[5000];
|
|
int sigSz = (int)sizeof(sig);
|
|
static const byte msg[] =
|
|
"Hash-ML-DSA streaming sign test — split across SequenceUpdate calls";
|
|
int msgSz = (int)sizeof(msg) - 1;
|
|
int firstHalf;
|
|
|
|
XMEMSET(&hashKey, 0, sizeof(hashKey));
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
XMEMSET(&validation, 0, sizeof(validation));
|
|
|
|
rc = wolfTPM2_GetKeyTemplate_HASH_MLDSA(&pub,
|
|
TPMA_OBJECT_sign | TPMA_OBJECT_fixedTPM | TPMA_OBJECT_fixedParent |
|
|
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth,
|
|
TPM_MLDSA_65, TPM_ALG_SHA256);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
rc = wolfTPM2_CreatePrimaryKey(dev, &hashKey, TPM_RH_OWNER, &pub, NULL, 0);
|
|
if (rc == TPM_RC_VALUE || rc == TPM_RC_SCHEME ||
|
|
rc == TPM_RC_COMMAND_CODE || rc == (int)(RC_VER1 + 0x043)) {
|
|
printf("Test TPM Wrapper: %-40s Skipped (not supported)\n",
|
|
"Hash-ML-DSA SignSeqUpdate streaming:");
|
|
return;
|
|
}
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Argument validation — none of these should reach the TPM. */
|
|
AssertIntEQ(wolfTPM2_SignSequenceUpdate(NULL, 0x80000000,
|
|
(const byte*)"x", 1), BAD_FUNC_ARG);
|
|
AssertIntEQ(wolfTPM2_SignSequenceUpdate(dev, 0x80000000, NULL, 1),
|
|
BAD_FUNC_ARG);
|
|
AssertIntEQ(wolfTPM2_SignSequenceUpdate(dev, 0x80000000,
|
|
(const byte*)"x", 0), BAD_FUNC_ARG);
|
|
/* dataSz larger than the SequenceUpdate buffer must reject locally. */
|
|
AssertIntEQ(wolfTPM2_SignSequenceUpdate(dev, 0x80000000,
|
|
(const byte*)"x", MAX_DIGEST_BUFFER + 1), BUFFER_E);
|
|
|
|
/* Streaming sign: SignSequenceStart → Update(part1) → Update(part2) →
|
|
* Complete(empty trailing buffer). */
|
|
rc = wolfTPM2_SignSequenceStart(dev, &hashKey, NULL, 0, &seqHandle);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
firstHalf = msgSz / 2;
|
|
rc = wolfTPM2_SignSequenceUpdate(dev, seqHandle, msg, firstHalf);
|
|
AssertIntEQ(rc, 0);
|
|
rc = wolfTPM2_SignSequenceUpdate(dev, seqHandle,
|
|
msg + firstHalf, msgSz - firstHalf);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
sigSz = (int)sizeof(sig);
|
|
rc = wolfTPM2_SignSequenceComplete(dev, seqHandle, &hashKey,
|
|
NULL, 0, sig, &sigSz);
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntGT(sigSz, 0);
|
|
|
|
/* Round-trip: verify the streamed signature matches the original
|
|
* message via VerifySequence (also streaming). */
|
|
rc = wolfTPM2_VerifySequenceStart(dev, &hashKey, NULL, 0, &seqHandle);
|
|
AssertIntEQ(rc, 0);
|
|
rc = wolfTPM2_VerifySequenceComplete(dev, seqHandle, &hashKey,
|
|
msg, msgSz, sig, sigSz, &validation);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
wolfTPM2_UnloadHandle(dev, &hashKey.handle);
|
|
printf("Test TPM Wrapper: %-40s Passed\n",
|
|
"Hash-ML-DSA SignSeqUpdate streaming:");
|
|
}
|
|
|
|
/* Direct coverage for wolfTPM2_SignDigest + wolfTPM2_VerifyDigestSignature
|
|
* wrappers. These are the documented one-shot digest APIs and were only
|
|
* exercised via the pqc_mssim_e2e example — wrapper-level marshaling bugs
|
|
* (TPMT_TK_HASHCHECK synthesis, sigAlg dispatch, ticket parse) were not
|
|
* caught by unit tests. Sign + Verify round-trip then assert the
|
|
* validation ticket reports DIGEST_VERIFIED. */
|
|
static void test_wolfTPM2_HashMLDSA_SignDigest_RoundTrip(WOLFTPM2_DEV* dev)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_KEY hashKey;
|
|
TPMT_PUBLIC pub;
|
|
TPMT_TK_VERIFIED validation;
|
|
byte sig[5000];
|
|
int sigSz = (int)sizeof(sig);
|
|
/* SHA-256 digest of an arbitrary 32-byte test vector. */
|
|
const byte digest[32] = {
|
|
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
|
|
};
|
|
|
|
XMEMSET(&hashKey, 0, sizeof(hashKey));
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
XMEMSET(&validation, 0, sizeof(validation));
|
|
|
|
rc = wolfTPM2_GetKeyTemplate_HASH_MLDSA(&pub,
|
|
TPMA_OBJECT_sign | TPMA_OBJECT_fixedTPM | TPMA_OBJECT_fixedParent |
|
|
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth,
|
|
TPM_MLDSA_65, TPM_ALG_SHA256);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
rc = wolfTPM2_CreatePrimaryKey(dev, &hashKey, TPM_RH_OWNER, &pub, NULL, 0);
|
|
if (rc == TPM_RC_VALUE || rc == TPM_RC_SCHEME ||
|
|
rc == TPM_RC_COMMAND_CODE || rc == (int)(RC_VER1 + 0x043)) {
|
|
printf("Test TPM Wrapper: %-40s Skipped (not supported)\n",
|
|
"Hash-ML-DSA SignDigest roundtrip:");
|
|
return;
|
|
}
|
|
AssertIntEQ(rc, 0);
|
|
|
|
rc = wolfTPM2_SignDigest(dev, &hashKey, digest, (int)sizeof(digest),
|
|
NULL, 0, sig, &sigSz);
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntGT(sigSz, 0);
|
|
|
|
rc = wolfTPM2_VerifyDigestSignature(dev, &hashKey,
|
|
digest, (int)sizeof(digest), sig, sigSz, NULL, 0, &validation);
|
|
AssertIntEQ(rc, 0);
|
|
/* Ticket from VerifyDigestSignature must be DIGEST_VERIFIED — a
|
|
* downstream PolicyTicket consumer relies on this tag. */
|
|
AssertIntEQ(validation.tag, TPM_ST_DIGEST_VERIFIED);
|
|
|
|
wolfTPM2_UnloadHandle(dev, &hashKey.handle);
|
|
printf("Test TPM Wrapper: %-40s Passed\n",
|
|
"Hash-ML-DSA SignDigest roundtrip:");
|
|
}
|
|
|
|
/* Regression for the TPM2_SignSequenceStart no-session path.
|
|
* Per Part 3 Sec.17.6.3 the command has Auth Index: None; the native API
|
|
* used to require ctx->session != NULL and hardcode TPM_ST_SESSIONS.
|
|
* This test forces the no-session branch and asserts success — if a
|
|
* future change re-adds the spurious session check or hardcodes the
|
|
* tag, the call returns BAD_FUNC_ARG. */
|
|
static void test_TPM2_SignSequenceStart_NoSession(WOLFTPM2_DEV* dev,
|
|
WOLFTPM2_KEY* mldsaKey)
|
|
{
|
|
TPM_RC rc;
|
|
TPM2_CTX* ctx = TPM2_GetActiveCtx();
|
|
TPM2_AUTH_SESSION* savedSession;
|
|
SignSequenceStart_In in;
|
|
SignSequenceStart_Out out;
|
|
|
|
if (ctx == NULL) {
|
|
printf("Test TPM Wrapper: %-40s Skipped (no ctx)\n",
|
|
"ML-DSA SignSeqStart no-session:");
|
|
return;
|
|
}
|
|
|
|
savedSession = ctx->session;
|
|
ctx->session = NULL;
|
|
|
|
XMEMSET(&in, 0, sizeof(in));
|
|
XMEMSET(&out, 0, sizeof(out));
|
|
in.keyHandle = mldsaKey->handle.hndl;
|
|
|
|
rc = TPM2_SignSequenceStart(&in, &out);
|
|
|
|
ctx->session = savedSession;
|
|
|
|
if (rc == TPM_RC_VALUE || rc == TPM_RC_SCHEME ||
|
|
rc == TPM_RC_COMMAND_CODE || rc == (TPM_RC)(RC_VER1 + 0x043)) {
|
|
printf("Test TPM Wrapper: %-40s Skipped (not supported)\n",
|
|
"ML-DSA SignSeqStart no-session:");
|
|
return;
|
|
}
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
/* Flush the sequence we just started */
|
|
if (out.sequenceHandle != 0) {
|
|
WOLFTPM2_HANDLE seqHandle;
|
|
XMEMSET(&seqHandle, 0, sizeof(seqHandle));
|
|
seqHandle.hndl = out.sequenceHandle;
|
|
wolfTPM2_UnloadHandle(dev, &seqHandle);
|
|
}
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n",
|
|
"ML-DSA SignSeqStart no-session:");
|
|
}
|
|
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && \
|
|
(defined(WOLFSSL_HAVE_MLKEM) || defined(WOLFSSL_KYBER512) || \
|
|
defined(WOLFSSL_KYBER768) || defined(WOLFSSL_KYBER1024))
|
|
/* Test ML-KEM Encapsulate; writes ct to caller buffer on success */
|
|
static void test_wolfTPM2_MLKEM_Encapsulate(WOLFTPM2_DEV* dev,
|
|
WOLFTPM2_KEY* mlkemKey, byte* ciphertext, int* ciphertextSz)
|
|
{
|
|
int rc;
|
|
byte sharedSecret[64];
|
|
int sharedSecretSz = (int)sizeof(sharedSecret);
|
|
|
|
XMEMSET(sharedSecret, 0, sizeof(sharedSecret));
|
|
|
|
rc = wolfTPM2_Encapsulate(dev, mlkemKey, ciphertext, ciphertextSz,
|
|
sharedSecret, &sharedSecretSz);
|
|
if (rc == TPM_RC_VALUE || rc == TPM_RC_SCHEME ||
|
|
rc == TPM_RC_COMMAND_CODE || rc == (int)(RC_VER1 + 0x043)) {
|
|
printf("Test TPM Wrapper: %-40s Skipped (not supported)\n", "ML-KEM Encapsulate:");
|
|
*ciphertextSz = 0;
|
|
return;
|
|
}
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntGT(*ciphertextSz, 0);
|
|
AssertIntGT(sharedSecretSz, 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s %s\n", "ML-KEM Encapsulate:",
|
|
rc == 0 ? "Passed" : "Failed");
|
|
}
|
|
|
|
/* Test ML-KEM Decapsulate */
|
|
static void test_wolfTPM2_MLKEM_Decapsulate(WOLFTPM2_DEV* dev,
|
|
WOLFTPM2_KEY* mlkemKey, const byte* ciphertext, int ciphertextSz)
|
|
{
|
|
int rc;
|
|
byte sharedSecret[64]; /* Shared secret */
|
|
int sharedSecretSz = (int)sizeof(sharedSecret);
|
|
|
|
XMEMSET(sharedSecret, 0, sizeof(sharedSecret));
|
|
|
|
/* Test Decapsulate */
|
|
rc = wolfTPM2_Decapsulate(dev, mlkemKey, ciphertext, ciphertextSz,
|
|
sharedSecret, &sharedSecretSz);
|
|
if (rc == TPM_RC_VALUE || rc == TPM_RC_SCHEME ||
|
|
rc == TPM_RC_COMMAND_CODE || rc == (int)(RC_VER1 + 0x043)) {
|
|
printf("Test TPM Wrapper: %-40s Skipped (not supported)\n", "ML-KEM Decapsulate:");
|
|
return;
|
|
}
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntGT(sharedSecretSz, 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s %s\n", "ML-KEM Decapsulate:",
|
|
rc == 0 ? "Passed" : "Failed");
|
|
}
|
|
|
|
/* Test ML-KEM Encapsulate/Decapsulate round-trip */
|
|
static void test_wolfTPM2_MLKEM_RoundTrip(WOLFTPM2_DEV* dev,
|
|
WOLFTPM2_KEY* mlkemKey)
|
|
{
|
|
int rc;
|
|
byte ciphertext[2048];
|
|
int ciphertextSz = (int)sizeof(ciphertext);
|
|
byte sharedSecret1[64], sharedSecret2[64];
|
|
int sharedSecret1Sz = (int)sizeof(sharedSecret1);
|
|
int sharedSecret2Sz = (int)sizeof(sharedSecret2);
|
|
|
|
XMEMSET(ciphertext, 0, sizeof(ciphertext));
|
|
XMEMSET(sharedSecret1, 0, sizeof(sharedSecret1));
|
|
XMEMSET(sharedSecret2, 0, sizeof(sharedSecret2));
|
|
|
|
/* Encapsulate */
|
|
rc = wolfTPM2_Encapsulate(dev, mlkemKey, ciphertext, &ciphertextSz,
|
|
sharedSecret1, &sharedSecret1Sz);
|
|
if (rc == TPM_RC_VALUE || rc == TPM_RC_SCHEME ||
|
|
rc == TPM_RC_COMMAND_CODE || rc == (int)(RC_VER1 + 0x043)) {
|
|
printf("Test TPM Wrapper: %-40s Skipped (not supported)\n", "ML-KEM Round Trip:");
|
|
return;
|
|
}
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntGT(ciphertextSz, 0);
|
|
AssertIntGT(sharedSecret1Sz, 0);
|
|
|
|
/* Decapsulate */
|
|
rc = wolfTPM2_Decapsulate(dev, mlkemKey, ciphertext, ciphertextSz,
|
|
sharedSecret2, &sharedSecret2Sz);
|
|
AssertIntEQ(rc, 0);
|
|
AssertIntGT(sharedSecret2Sz, 0);
|
|
|
|
/* Verify shared secrets match */
|
|
AssertIntEQ(sharedSecret1Sz, sharedSecret2Sz);
|
|
AssertIntEQ(XMEMCMP(sharedSecret1, sharedSecret2, sharedSecret1Sz), 0);
|
|
|
|
printf("Test TPM Wrapper: %-40s %s\n", "ML-KEM Round Trip:",
|
|
rc == 0 ? "Passed" : "Failed");
|
|
}
|
|
#endif /* ML-KEM support */
|
|
|
|
/* Main PQC test function */
|
|
/* Returns 1 if the connected TPM advertises @alg in its TPM_CAP_ALGS list.
|
|
* Used to skip sub-tests for optional algorithms (e.g. Hash-ML-DSA) that a
|
|
* given TPM may not implement, without hard-coding any model check. */
|
|
static int test_alg_supported(TPM_ALG_ID alg)
|
|
{
|
|
GetCapability_In in;
|
|
GetCapability_Out out;
|
|
TPML_ALG_PROPERTY* algs;
|
|
word32 i;
|
|
|
|
XMEMSET(&in, 0, sizeof(in));
|
|
XMEMSET(&out, 0, sizeof(out));
|
|
in.capability = TPM_CAP_ALGS;
|
|
in.property = TPM_ALG_FIRST;
|
|
in.propertyCount = MAX_CAP_ALGS;
|
|
if (TPM2_GetCapability(&in, &out) != TPM_RC_SUCCESS)
|
|
return 0;
|
|
|
|
algs = &out.capabilityData.data.algorithms;
|
|
for (i = 0; i < algs->count; i++) {
|
|
if (algs->algProperties[i].alg == alg)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Regression for the VerifySequenceComplete flush-on-error fix: looping
|
|
* failed verifies must not exhaust transient memory (TPM_RC_OBJECT_MEMORY).
|
|
* Uses an ECC key to cover the classical (non-PQC) wrapper path. */
|
|
static void test_wolfTPM2_VerifySequence_NoLeak(void)
|
|
{
|
|
int rc, i;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY eccKey;
|
|
TPMT_PUBLIC pub;
|
|
TPM_HANDLE seqHandle;
|
|
TPMT_TK_VERIFIED validation;
|
|
byte msg[32];
|
|
byte badSig[64]; /* ECC P-256 r||s, deliberately invalid */
|
|
|
|
XMEMSET(&eccKey, 0, sizeof(eccKey));
|
|
XMEMSET(&pub, 0, sizeof(pub));
|
|
XMEMSET(msg, 0xAB, sizeof(msg));
|
|
XMEMSET(badSig, 0xAA, sizeof(badSig));
|
|
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
rc = wolfTPM2_GetKeyTemplate_ECC(&pub,
|
|
TPMA_OBJECT_sign | TPMA_OBJECT_fixedTPM | TPMA_OBJECT_fixedParent |
|
|
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
|
|
TPMA_OBJECT_noDA, TPM_ECC_NIST_P256, TPM_ALG_ECDSA);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
|
|
rc = wolfTPM2_CreatePrimaryKey(&dev, &eccKey, TPM_RH_OWNER, &pub, NULL, 0);
|
|
if (rc != TPM_RC_SUCCESS) {
|
|
printf("Test TPM Wrapper: %-40s Skipped (not supported)\n",
|
|
"VerifySequence no-leak:");
|
|
wolfTPM2_Cleanup(&dev);
|
|
return;
|
|
}
|
|
|
|
/* More iterations than the TPM has transient slots. Without the fix the
|
|
* leaked sequence objects exhaust memory and a later call returns
|
|
* TPM_RC_OBJECT_MEMORY; with it each failed verify is flushed. */
|
|
for (i = 0; i < 8; i++) {
|
|
rc = wolfTPM2_VerifySequenceStart(&dev, &eccKey, NULL, 0, &seqHandle);
|
|
if (rc == TPM_RC_SUCCESS) {
|
|
rc = wolfTPM2_VerifySequenceComplete(&dev, seqHandle, &eccKey,
|
|
msg, (int)sizeof(msg), badSig, (int)sizeof(badSig),
|
|
&validation);
|
|
}
|
|
AssertIntNE(rc, TPM_RC_OBJECT_MEMORY);
|
|
AssertIntNE(rc, TPM_RC_SUCCESS); /* bad signature must be rejected */
|
|
}
|
|
|
|
wolfTPM2_UnloadHandle(&dev, &eccKey.handle);
|
|
wolfTPM2_Cleanup(&dev);
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "VerifySequence no-leak:");
|
|
}
|
|
|
|
static void test_wolfTPM2_PQC(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_DEV dev;
|
|
WOLFTPM2_KEY storageKey;
|
|
WOLFTPM2_KEY mldsaKey;
|
|
TPMT_PUBLIC mldsaPub;
|
|
byte sig[5000];
|
|
int sigSz = (int)sizeof(sig);
|
|
byte testMessage[] = "Test message for ML-DSA signing";
|
|
int testMessageSz = (int)sizeof(testMessage) - 1;
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && \
|
|
(defined(WOLFSSL_HAVE_MLKEM) || defined(WOLFSSL_KYBER512) || \
|
|
defined(WOLFSSL_KYBER768) || defined(WOLFSSL_KYBER1024))
|
|
WOLFTPM2_KEY mlkemKey;
|
|
TPMT_PUBLIC mlkemPub;
|
|
byte testCiphertext[2048];
|
|
int testCiphertextSz;
|
|
#endif
|
|
|
|
/* Initialize TPM */
|
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
|
AssertIntEQ(rc, 0);
|
|
|
|
/* Create storage key (exercises CreateSRK), then flush it immediately.
|
|
* It is not used as a parent below, and holding it would consume one of
|
|
* the few transient object slots on constrained hardware, leaving no
|
|
* room for a sub-test's own primary plus a sign-sequence object
|
|
* (TPM_RC_OBJECT_MEMORY). */
|
|
rc = wolfTPM2_CreateSRK(&dev, &storageKey, TPM_ALG_ECC,
|
|
(byte*)gStorageKeyAuth, sizeof(gStorageKeyAuth)-1);
|
|
AssertIntEQ(rc, 0);
|
|
wolfTPM2_UnloadHandle(&dev, &storageKey.handle);
|
|
|
|
/* Create a real ML-DSA-65 primary key so Sign/Verify sequence tests
|
|
* operate on an actual handle. Pure-MLDSA SignDigest is deferred
|
|
* until wolfCrypt exposes a mu-direct sign API (DEC-0006). */
|
|
printf("Testing ML-DSA functions...\n");
|
|
XMEMSET(&mldsaKey, 0, sizeof(mldsaKey));
|
|
XMEMSET(&mldsaPub, 0, sizeof(mldsaPub));
|
|
/* allowExternalMu=0: fwTPM does not yet implement μ-direct sign, so per
|
|
* Part 2 Sec.12.2.3.6 keys created with allowExternalMu=YES are rejected at
|
|
* object creation with TPM_RC_EXT_MU. Use NO for the suite key. */
|
|
rc = wolfTPM2_GetKeyTemplate_MLDSA(&mldsaPub,
|
|
TPMA_OBJECT_sign | TPMA_OBJECT_fixedTPM | TPMA_OBJECT_fixedParent |
|
|
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth,
|
|
TPM_MLDSA_65, 0 /* allowExternalMu */);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
rc = wolfTPM2_CreatePrimaryKey(&dev, &mldsaKey, TPM_RH_OWNER,
|
|
&mldsaPub, NULL, 0);
|
|
if (rc == TPM_RC_VALUE || rc == TPM_RC_SCHEME ||
|
|
rc == TPM_RC_COMMAND_CODE || rc == (int)(RC_VER1 + 0x043)) {
|
|
printf("Test TPM Wrapper: %-40s Skipped (not supported)\n",
|
|
"ML-DSA PQC suite:");
|
|
goto mldsa_done;
|
|
}
|
|
AssertIntEQ(rc, 0);
|
|
|
|
sigSz = (int)sizeof(sig);
|
|
test_wolfTPM2_MLDSA_SignSequence(&dev, &mldsaKey,
|
|
testMessage, testMessageSz, sig, &sigSz);
|
|
|
|
test_wolfTPM2_MLDSA_VerifySequence(&dev, &mldsaKey,
|
|
testMessage, testMessageSz, sig, sigSz);
|
|
|
|
/* Bug-fix regressions: each test exercises a wrapper / native-API path
|
|
* that no existing test covers, so a re-introduction of the underlying
|
|
* fix would silently pass CI without these. */
|
|
/* These sub-tests each create a HASH_MLDSA key (DataChain included), so
|
|
* only run them when the TPM advertises TPM_ALG_HASH_MLDSA. */
|
|
if (test_alg_supported(TPM_ALG_HASH_MLDSA)) {
|
|
test_wolfTPM2_MLDSA_VerifySequence_DataChain(&dev);
|
|
test_wolfTPM2_HashMLDSA_SignSequence_Streaming(&dev);
|
|
test_wolfTPM2_HashMLDSA_SignDigest_RoundTrip(&dev);
|
|
}
|
|
else {
|
|
printf("Test TPM Wrapper: %-40s Skipped (no Hash-ML-DSA)\n",
|
|
"Hash-ML-DSA:");
|
|
}
|
|
test_TPM2_SignSequenceStart_NoSession(&dev, &mldsaKey);
|
|
test_wolfTPM2_MLDSA_SignSequence_NonEmptyAuth(&dev, &mldsaPub);
|
|
|
|
wolfTPM2_UnloadHandle(&dev, &mldsaKey.handle);
|
|
mldsa_done:
|
|
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && \
|
|
(defined(WOLFSSL_HAVE_MLKEM) || defined(WOLFSSL_KYBER512) || \
|
|
defined(WOLFSSL_KYBER768) || defined(WOLFSSL_KYBER1024))
|
|
printf("Testing ML-KEM functions...\n");
|
|
XMEMSET(&mlkemKey, 0, sizeof(mlkemKey));
|
|
XMEMSET(&mlkemPub, 0, sizeof(mlkemPub));
|
|
rc = wolfTPM2_GetKeyTemplate_MLKEM(&mlkemPub,
|
|
TPMA_OBJECT_decrypt | TPMA_OBJECT_fixedTPM | TPMA_OBJECT_fixedParent |
|
|
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth,
|
|
TPM_MLKEM_768);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
rc = wolfTPM2_CreatePrimaryKey(&dev, &mlkemKey, TPM_RH_OWNER,
|
|
&mlkemPub, NULL, 0);
|
|
if (rc == TPM_RC_VALUE || rc == TPM_RC_SCHEME ||
|
|
rc == TPM_RC_COMMAND_CODE || rc == (int)(RC_VER1 + 0x043)) {
|
|
printf("Test TPM Wrapper: %-40s Skipped (not supported)\n",
|
|
"ML-KEM PQC suite:");
|
|
goto mlkem_done;
|
|
}
|
|
AssertIntEQ(rc, 0);
|
|
|
|
XMEMSET(testCiphertext, 0, sizeof(testCiphertext));
|
|
testCiphertextSz = (int)sizeof(testCiphertext);
|
|
test_wolfTPM2_MLKEM_Encapsulate(&dev, &mlkemKey,
|
|
testCiphertext, &testCiphertextSz);
|
|
if (testCiphertextSz > 0) {
|
|
test_wolfTPM2_MLKEM_Decapsulate(&dev, &mlkemKey,
|
|
testCiphertext, testCiphertextSz);
|
|
}
|
|
|
|
test_wolfTPM2_MLKEM_RoundTrip(&dev, &mlkemKey);
|
|
wolfTPM2_UnloadHandle(&dev, &mlkemKey.handle);
|
|
mlkem_done:
|
|
#endif
|
|
|
|
wolfTPM2_UnloadHandle(&dev, &storageKey.handle);
|
|
wolfTPM2_Cleanup(&dev);
|
|
}
|
|
|
|
/* Test PQC key template creation */
|
|
static void test_wolfTPM2_PQC_KeyTemplates(void)
|
|
{
|
|
int rc;
|
|
TPMT_PUBLIC mldsaTemplate, hashMldsaTemplate, mlkemTemplate;
|
|
|
|
printf("Testing PQC Key Templates...\n");
|
|
|
|
/* Test MLDSA template */
|
|
rc = wolfTPM2_GetKeyTemplate_MLDSA(&mldsaTemplate,
|
|
TPMA_OBJECT_sign | TPMA_OBJECT_userWithAuth,
|
|
TPM_MLDSA_65, 1);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(mldsaTemplate.type, TPM_ALG_MLDSA);
|
|
AssertIntEQ(mldsaTemplate.parameters.mldsaDetail.parameterSet, TPM_MLDSA_65);
|
|
AssertIntEQ(mldsaTemplate.parameters.mldsaDetail.allowExternalMu, YES);
|
|
/* Verify sign is set, decrypt is NOT set */
|
|
AssertTrue(mldsaTemplate.objectAttributes & TPMA_OBJECT_sign);
|
|
AssertFalse(mldsaTemplate.objectAttributes & TPMA_OBJECT_decrypt);
|
|
|
|
/* Test HASH_MLDSA template */
|
|
rc = wolfTPM2_GetKeyTemplate_HASH_MLDSA(&hashMldsaTemplate,
|
|
TPMA_OBJECT_sign | TPMA_OBJECT_userWithAuth,
|
|
TPM_MLDSA_87, TPM_ALG_SHA256);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(hashMldsaTemplate.type, TPM_ALG_HASH_MLDSA);
|
|
AssertIntEQ(hashMldsaTemplate.parameters.hash_mldsaDetail.parameterSet, TPM_MLDSA_87);
|
|
AssertIntEQ(hashMldsaTemplate.parameters.hash_mldsaDetail.hashAlg, TPM_ALG_SHA256);
|
|
|
|
/* Test MLKEM template */
|
|
rc = wolfTPM2_GetKeyTemplate_MLKEM(&mlkemTemplate,
|
|
TPMA_OBJECT_decrypt | TPMA_OBJECT_userWithAuth,
|
|
TPM_MLKEM_768);
|
|
AssertIntEQ(rc, TPM_RC_SUCCESS);
|
|
AssertIntEQ(mlkemTemplate.type, TPM_ALG_MLKEM);
|
|
AssertIntEQ(mlkemTemplate.parameters.mlkemDetail.parameterSet, TPM_MLKEM_768);
|
|
/* Verify decrypt is set, sign is NOT set */
|
|
AssertTrue(mlkemTemplate.objectAttributes & TPMA_OBJECT_decrypt);
|
|
AssertFalse(mlkemTemplate.objectAttributes & TPMA_OBJECT_sign);
|
|
|
|
/* Test NULL argument handling */
|
|
rc = wolfTPM2_GetKeyTemplate_MLDSA(NULL, 0, TPM_MLDSA_44, 0);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
rc = wolfTPM2_GetKeyTemplate_HASH_MLDSA(NULL, 0, TPM_MLDSA_44, TPM_ALG_SHA256);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
rc = wolfTPM2_GetKeyTemplate_MLKEM(NULL, 0, TPM_MLKEM_512);
|
|
AssertIntEQ(rc, BAD_FUNC_ARG);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "PQC Key Templates:");
|
|
}
|
|
|
|
/* Test PQC sizes sanity check */
|
|
static void test_wolfTPM2_PQC_Sizes(void)
|
|
{
|
|
printf("Testing PQC Sizes...\n");
|
|
|
|
/* Verify TPMT_PUBLIC size is reasonable for embedded targets */
|
|
printf(" TPMT_PUBLIC size with PQC: %zu bytes\n", sizeof(TPMT_PUBLIC));
|
|
/* Warn if > 5KB, which could be large for embedded stacks */
|
|
if (sizeof(TPMT_PUBLIC) >= 5120) {
|
|
printf(" WARNING: TPMT_PUBLIC size (%zu bytes) may be large for "
|
|
"embedded stacks\n", sizeof(TPMT_PUBLIC));
|
|
}
|
|
|
|
/* Verify key buffer sizes are correct */
|
|
AssertIntEQ(MAX_MLDSA_PUB_SIZE, 2592); /* ML-DSA-87 */
|
|
AssertIntEQ(MAX_MLDSA_SIG_SIZE, 4627); /* ML-DSA-87 */
|
|
AssertIntEQ(MAX_MLDSA_PRIV_SEED_SIZE, 32);
|
|
AssertIntEQ(MAX_MLKEM_PUB_SIZE, 1568); /* ML-KEM-1024 */
|
|
AssertIntEQ(MAX_MLKEM_PRIV_SEED_SIZE, 64);
|
|
|
|
printf("Test TPM Wrapper: %-40s Passed\n", "PQC Sizes:");
|
|
}
|
|
#endif /* WOLFTPM_MLDSA && WOLFTPM_MLKEM */
|
|
|
|
#endif /* !WOLFTPM2_NO_WRAPPER */
|
|
|
|
#ifndef NO_MAIN_DRIVER
|
|
int main(int argc, char *argv[])
|
|
#else
|
|
int unit_tests(int argc, char *argv[])
|
|
#endif
|
|
{
|
|
#if defined(WOLFTPM_SWTPM) && !defined(NO_GETENV) && \
|
|
!defined(WOLFTPM2_NO_WRAPPER)
|
|
if (argc == 2 && XSTRCMP(argv[1], "--init-upgrade") == 0) {
|
|
test_wolfTPM2_InitUpgrade();
|
|
return 0;
|
|
}
|
|
#endif
|
|
(void)argc;
|
|
(void)argv;
|
|
|
|
#ifndef WOLFTPM2_NO_WRAPPER
|
|
#if defined(WOLFTPM_SPDM) && defined(WOLFTPM_SPDM_TCG) && \
|
|
defined(WOLFSPDM_NUVOTON) && defined(WOLFSPDM_NATIONS)
|
|
test_wolfTPM2_SpdmModeFromDidVid();
|
|
#endif
|
|
test_wolfTPM2_Init();
|
|
test_wolfTPM2_OpenExisting();
|
|
test_wolfTPM2_GetCapabilities();
|
|
test_wolfTPM2_GetRandom();
|
|
test_wolfTPM2_DictionaryAttack();
|
|
test_wolfTPM2_HashFinish_BufferTooSmall();
|
|
test_TPM2_PCRSel();
|
|
test_TPM2_Policy_NULL_Args();
|
|
test_wolfTPM2_SetLocality();
|
|
test_wolfTPM2_PolicyAuthValue_AuthOffset();
|
|
test_wolfTPM2_SetAuthHandle_PolicyAuthOffset();
|
|
test_wolfTPM2_StartSession_SaltedEncryptAttrs();
|
|
test_wolfTPM2_StartSession_ex_authHash();
|
|
test_wolfTPM2_BoundSession_EmptyAuth_ParamEnc();
|
|
test_wolfTPM2_CreateLoaded_ParamEnc();
|
|
test_wolfTPM2_BoundOwnEntity_ParamEnc();
|
|
test_wolfTPM2_NVWriteChunked();
|
|
test_wolfTPM2_PolicyHash();
|
|
test_wolfTPM2_SensitiveToPrivate();
|
|
test_TPM2_KDFa();
|
|
test_TPM2_KDFa_SessionLabels();
|
|
test_TPM2_KDFe();
|
|
test_TPM2_HmacCompute();
|
|
test_TPM2_HashCompute();
|
|
test_TPM2_ConstantCompare();
|
|
test_WOLFTPM_IS_COMMAND_UNAVAILABLE();
|
|
test_TPM2_AesCfbRoundtrip();
|
|
test_TPM2_KDFa_MultiHash();
|
|
test_TPM2_KDFe_MultiHash();
|
|
test_TPM2_HmacCompute_MultiHash();
|
|
test_TPM2_HashCompute_MultiHash();
|
|
test_TPM2_KDF_Errors();
|
|
test_TPM2_GetTpmHashType();
|
|
test_TPM2_ResponseHmacVerification();
|
|
test_TPM2_CalcHmac();
|
|
test_TPM2_ParamEnc_XOR_Vector();
|
|
test_TPM2_ParamEnc_XOR_MaskBoundary();
|
|
test_TPM2_ParamEnc_AESCFB_Vector();
|
|
test_TPM2_ParamEnc_AESCFB_KeyBoundary();
|
|
test_TPM2_ParamEnc_AESCFB_KAT();
|
|
test_TPM2_ParamDec_XOR_Roundtrip();
|
|
test_TPM2_ParamDec_AESCFB_Roundtrip();
|
|
test_TPM2_ParamEncDec_Dispatch_Roundtrip();
|
|
test_TPM2_HashNvPublic();
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC)
|
|
test_wolfTPM2_ComputeName();
|
|
#endif
|
|
test_TPM2_SchemeSerialize();
|
|
test_TPM2_ECC_Parameters_EcdaaResponseParse();
|
|
test_TPM2_ParseAttest_NvDigest();
|
|
test_TPM2_ParsePublic_OuterResync();
|
|
test_TPM2_ParsePoint_OuterResync();
|
|
test_TPM2_ParseSignature_NullAlg();
|
|
#ifdef WOLFTPM_MLDSA_VERIFY
|
|
test_TPM2_PolicyAuthorize_DigestVerifiedMetaAlg();
|
|
#endif
|
|
test_TPM2_BrainpoolCurveMapping();
|
|
test_TPM2_EccDefaultCurveTemplate();
|
|
test_wolfTPM2_RsaEncryptDecrypt_OversizedBufferE();
|
|
test_wolfTPM2_RsaEncryptDecrypt_ex();
|
|
test_wolfTPM2_GetKeyTemplate_ex_nameAlg();
|
|
test_wolfTPM2_SignHashScheme_DigestSize();
|
|
test_wolfTPM2_VerifyHashTicket_DigestSize();
|
|
#ifndef WOLFTPM_NO_RETRY
|
|
test_TPM2_CommandRetries();
|
|
test_TPM2_Packet_RetryRestore();
|
|
#endif
|
|
#if defined(WOLFTPM_FIRMWARE_UPGRADE) && \
|
|
(defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT))
|
|
test_TPM2_DispatchCommand_overflow();
|
|
#endif
|
|
test_wolfTPM2_ParseCapabilities_vendorStr();
|
|
test_st33_detect_blob0();
|
|
#if defined(WOLFTPM_FIRMWARE_UPGRADE) && \
|
|
(defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT))
|
|
test_st33_fu_ordinals();
|
|
#endif
|
|
test_TPM2_ResponseProcess_ParamSizeOverflow();
|
|
test_TPM2_ResponseProcess_DecParamSizeOverflow();
|
|
test_TPM2_ResponseProcess_HmacVerify();
|
|
test_wolfTPM2_NVCreateAuthPolicy_NameAlg();
|
|
test_wolfTPM2_GetKeyTemplate_KeyedHash_Scheme();
|
|
#if defined(WOLFTPM_MFG_IDENTITY) && \
|
|
!defined(WOLFTPM_ST33) && !defined(WOLFTPM_AUTODETECT)
|
|
test_wolfTPM2_SetIdentityAuth_RequiresPassword();
|
|
#endif
|
|
test_wolfTPM2_EccKey_TpmToWolf_ShortCoord();
|
|
test_wolfTPM2_RsaKey_TpmToWolf_Exponent();
|
|
test_wolfTPM2_EccZToBuffer();
|
|
test_wolfTPM2_LoadEccPublicKey_Ex();
|
|
test_TPM2_KeyedHashScheme_XorSerialize();
|
|
test_TPM2_Signature_EcSchnorrSm2Serialize();
|
|
test_TPM2_Signature_RsaHmacSerialize();
|
|
test_TPM2_Public_RsaEcc_Roundtrip();
|
|
test_TPM2_Public_KeyedHashSym_Roundtrip();
|
|
#ifdef WOLFTPM_PQC
|
|
test_TPM2_Signature_PQC_Serialize();
|
|
test_TPM2_Public_PQC_Roundtrip();
|
|
#endif
|
|
test_TPM2_Sensitive_Roundtrip();
|
|
test_TPM2_TIS_ValidateRspSz();
|
|
test_TPM2_ParsePublic_EmptyClears();
|
|
test_TPM2_AppendSensitive_Clamp();
|
|
test_TPM2_AppendPublic_Clamp();
|
|
test_TPM2_Sensitive_MaxRoundtrip();
|
|
test_KeySealTemplate();
|
|
test_SealAndKeyedHash_Boundaries();
|
|
test_GetAlgId();
|
|
test_wolfTPM2_ReadPublicKey();
|
|
test_wolfTPM2_CSR();
|
|
test_wolfTPM2_CryptoDevCb_HashCacheStream();
|
|
test_wolfTPM2_CryptoDevCb_EccVerifyOversizedRS();
|
|
test_wolfTPM2_CryptoDevCb_MlDsaSign();
|
|
test_TPM2_ASN_DecodeX509Cert_Errors();
|
|
test_TPM2_ASN_RsaUnpadPkcsv15();
|
|
test_TPM2_ASN_DecodeX509Cert_Valid();
|
|
test_TPM2_ASN_DecodeTag_Errors();
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFTPM2_PEM_DECODE) && \
|
|
!defined(NO_RSA)
|
|
test_wolfTPM_ImportPublicKey();
|
|
test_wolfTPM2_PCRPolicy();
|
|
#endif
|
|
test_wolfTPM2_EncryptSecret();
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_ASN)
|
|
test_wolfTPM2_DecodeDer_DefaultAttribs();
|
|
test_wolfTPM2_DecodeDer_WrapKeyScaling();
|
|
#endif
|
|
test_wolfTPM2_LoadPrivateKey_NullParent();
|
|
test_wolfTPM2_EncryptDecryptBlock();
|
|
#ifdef HAVE_ECC
|
|
test_wolfTPM2_ImportEccPrivateKeySeed_ErrorPaths();
|
|
#endif
|
|
#ifndef NO_RSA
|
|
test_wolfTPM2_ImportRsaPrivateKeySeed_ErrorPaths();
|
|
#endif
|
|
test_wolfTPM2_NVStoreKey_BoundaryChecks();
|
|
test_wolfTPM2_NVDeleteKey_BoundaryChecks();
|
|
test_wolfTPM2_UnloadHandle_PersistentGuard();
|
|
test_TPM2_GetHashDigestSize_AllAlgs();
|
|
#ifdef WOLFTPM_SWTPM
|
|
test_TPM2_SwtpmValidateRspSz();
|
|
#endif
|
|
#if !defined(NO_FILESYSTEM) && !defined(NO_WRITE_TEMP_FILES) && \
|
|
!defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC)
|
|
test_readKeyBlob_PrivOverflow();
|
|
#endif
|
|
test_wolfTPM2_KeyBlob(TPM_ALG_RSA);
|
|
test_wolfTPM2_KeyBlob(TPM_ALG_ECC);
|
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) && \
|
|
!defined(WOLFTPM2_NO_ASN)
|
|
test_wolfTPM2_EccSignVerify();
|
|
#endif
|
|
#ifdef WOLFTPM_FIRMWARE_UPGRADE
|
|
#if defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT)
|
|
test_wolfTPM2_ST33_FirmwareUpgrade();
|
|
#endif
|
|
#endif
|
|
test_wolfTPM2_PolicyOR();
|
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
|
test_wolfTPM2_PolicyCommandCodeMake();
|
|
#endif
|
|
test_wolfTPM2_SetPrimaryPolicy();
|
|
test_wolfTPM2_SetPrimaryPolicy_rollback();
|
|
test_wolfTPM2_PolicyClear_underPolicy();
|
|
#ifdef WOLFTPM_FIRMWARE_UPGRADE
|
|
test_wolfTPM2_FirmwareUpgrade_ex_session();
|
|
#endif
|
|
test_wolfTPM2_IsAlgSupported();
|
|
test_wolfTPM2_PolicyOR_success();
|
|
#if defined(WOLFTPM_MLDSA) && defined(WOLFTPM_MLKEM)
|
|
/* Run non-TPM-dependent tests first */
|
|
test_wolfTPM2_PQC_KeyTemplates();
|
|
test_wolfTPM2_PQC_Sizes();
|
|
/* Then run TPM-dependent PQC tests */
|
|
test_wolfTPM2_PQC();
|
|
test_wolfTPM2_VerifySequence_NoLeak();
|
|
#endif
|
|
test_wolfTPM2_Cleanup();
|
|
test_wolfTPM2_Reset_contract();
|
|
test_wolfTPM2_thread_local_storage();
|
|
#ifdef WOLFTPM_SPDM
|
|
test_wolfTPM2_SPDM_ValidateRspSz();
|
|
test_wolfTPM2_SPDM_Functions();
|
|
#endif
|
|
#endif /* !WOLFTPM2_NO_WRAPPER */
|
|
|
|
return 0;
|
|
}
|