mirror of https://github.com/wolfSSL/wolfBoot.git
1741 lines
56 KiB
C
1741 lines
56 KiB
C
/* tpm.c
|
|
*
|
|
* Copyright (C) 2026 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfBoot.
|
|
*
|
|
* wolfBoot 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.
|
|
*
|
|
* wolfBoot 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
|
|
*/
|
|
/**
|
|
* @file tpm.c
|
|
* @brief This file contains functions related to TPM handling.
|
|
*/
|
|
|
|
#ifdef WOLFBOOT_TPM
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "image.h"
|
|
#include "printf.h"
|
|
#include "spi_drv.h"
|
|
#include "tpm.h"
|
|
#include "wolftpm/tpm2_tis.h" /* for TIS header size and wait state */
|
|
|
|
WOLFTPM2_DEV wolftpm_dev;
|
|
#if defined(WOLFBOOT_TPM_KEYSTORE) || defined(WOLFBOOT_TPM_SEAL)
|
|
WOLFTPM2_SESSION wolftpm_session;
|
|
WOLFTPM2_KEY wolftpm_srk;
|
|
#endif
|
|
|
|
#if defined(WOLFBOOT_TPM_KEYSTORE) && !defined(WOLFBOOT_TPM)
|
|
#error For TPM keystore please make sure WOLFBOOT_TPM is also defined
|
|
#endif
|
|
|
|
#if defined(WOLFBOOT_TPM_SEAL) || defined(WOLFBOOT_TPM_KEYSTORE)
|
|
int NOINLINEFUNCTION wolfBoot_constant_compare(const uint8_t* a, const uint8_t* b,
|
|
uint32_t len)
|
|
{
|
|
uint32_t i;
|
|
volatile uint32_t diff = 0U;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
diff |= (uint32_t)(a[i] ^ b[i]);
|
|
}
|
|
|
|
return (diff != 0U) ? 1 : 0;
|
|
}
|
|
|
|
void wolfBoot_print_hexstr(const unsigned char* bin, unsigned long sz,
|
|
unsigned long maxLine)
|
|
{
|
|
unsigned long i;
|
|
if (maxLine == 0) maxLine = sz;
|
|
for (i = 0; i < sz; i++) {
|
|
if (bin[i]<=15)
|
|
wolfBoot_printf("0");
|
|
wolfBoot_printf("%x", bin[i]);
|
|
if (((i+1) % maxLine) == 0 && i+1 != sz)
|
|
wolfBoot_printf("\n");
|
|
}
|
|
wolfBoot_printf("\n");
|
|
}
|
|
#endif
|
|
|
|
#if defined(DEBUG_WOLFTPM) || defined(WOLFTPM_DEBUG_IO) || \
|
|
defined(WOLFBOOT_DEBUG_TPM)
|
|
#define LINE_LEN 16
|
|
void wolfBoot_print_bin(const uint8_t* buffer, uint32_t length)
|
|
{
|
|
uint32_t i, sz;
|
|
|
|
if (!buffer) {
|
|
wolfBoot_printf("\tNULL\n");
|
|
return;
|
|
}
|
|
|
|
while (length > 0) {
|
|
sz = length;
|
|
if (sz > LINE_LEN)
|
|
sz = LINE_LEN;
|
|
|
|
wolfBoot_printf("\t");
|
|
for (i = 0; i < LINE_LEN; i++) {
|
|
if (i < length)
|
|
wolfBoot_printf("%02x ", buffer[i]);
|
|
else
|
|
wolfBoot_printf(" ");
|
|
}
|
|
wolfBoot_printf("| ");
|
|
for (i = 0; i < sz; i++) {
|
|
if (buffer[i] > 31 && buffer[i] < 127)
|
|
wolfBoot_printf("%c", buffer[i]);
|
|
else
|
|
wolfBoot_printf(".");
|
|
}
|
|
wolfBoot_printf("\r\n");
|
|
|
|
buffer += sz;
|
|
length -= sz;
|
|
}
|
|
}
|
|
#endif /* WOLFTPM_DEBUG_IO || WOLFBOOT_DEBUG_TPM */
|
|
|
|
#if !defined(ARCH_SIM) && !defined(WOLFTPM_MMIO)
|
|
#ifdef WOLFTPM_ADV_IO
|
|
static int TPM2_IoCb(TPM2_CTX* ctx, int isRead, uint32_t addr, uint8_t* buf,
|
|
word16 size, void* userCtx)
|
|
#else
|
|
|
|
/**
|
|
* @brief TPM2 I/O callback function for communication with TPM2 device.
|
|
*
|
|
* This function is used as the I/O callback function for communication
|
|
* with the TPM2 device. It is called during TPM operations to send and
|
|
* receive data from the TPM2 device.
|
|
*
|
|
* @param ctx The pointer to the TPM2 context.
|
|
* @param txBuf The buffer containing data to be sent to the TPM2 device.
|
|
* @param rxBuf The buffer to store the received data from the TPM2 device.
|
|
* @param xferSz The size of the data to be transferred.
|
|
* @param userCtx The user context (not used in this implementation).
|
|
* @return The return code from the TPM2 device operation.
|
|
*/
|
|
static int TPM2_IoCb(TPM2_CTX* ctx, const uint8_t* txBuf, uint8_t* rxBuf,
|
|
word16 xferSz, void* userCtx)
|
|
#endif
|
|
{
|
|
int ret;
|
|
#ifdef WOLFTPM_CHECK_WAIT_STATE
|
|
int timeout = TPM_SPI_WAIT_RETRY;
|
|
#endif
|
|
#ifdef WOLFTPM_ADV_IO
|
|
uint8_t txBuf[MAX_SPI_FRAMESIZE+TPM_TIS_HEADER_SZ];
|
|
uint8_t rxBuf[MAX_SPI_FRAMESIZE+TPM_TIS_HEADER_SZ];
|
|
int xferSz = TPM_TIS_HEADER_SZ + size;
|
|
|
|
#ifdef WOLFTPM_DEBUG_IO
|
|
wolfBoot_printf("TPM2_IoCb (Adv): Read %d, Addr %x, Size %d\n",
|
|
isRead ? 1 : 0, addr, size);
|
|
if (!isRead) {
|
|
wolfBoot_print_bin(buf, size);
|
|
}
|
|
#endif
|
|
|
|
/* Build TPM header */
|
|
txBuf[1] = (addr>>16) & 0xFF;
|
|
txBuf[2] = (addr>>8) & 0xFF;
|
|
txBuf[3] = (addr) & 0xFF;
|
|
if (isRead) {
|
|
txBuf[0] = TPM_TIS_READ | ((size & 0xFF) - 1);
|
|
memset(&txBuf[TPM_TIS_HEADER_SZ], 0, size);
|
|
}
|
|
else {
|
|
txBuf[0] = TPM_TIS_WRITE | ((size & 0xFF) - 1);
|
|
memcpy(&txBuf[TPM_TIS_HEADER_SZ], buf, size);
|
|
}
|
|
memset(rxBuf, 0, sizeof(rxBuf));
|
|
#endif /* WOLFTPM_ADV_IO */
|
|
|
|
#ifdef WOLFTPM_CHECK_WAIT_STATE /* Handle TIS wait states */
|
|
/* Send header - leave CS asserted */
|
|
ret = spi_xfer(SPI_CS_TPM, txBuf, rxBuf, TPM_TIS_HEADER_SZ,
|
|
0x1 /* 1=SPI_XFER_FLAG_CONTINUE */
|
|
);
|
|
|
|
/* Handle wait states */
|
|
while (ret == 0 &&
|
|
--timeout > 0 &&
|
|
(rxBuf[TPM_TIS_HEADER_SZ-1] & TPM_TIS_READY_MASK) == 0)
|
|
{
|
|
/* clock additional uint8_t until 0x01 LSB is set (keep CS asserted) */
|
|
ret = spi_xfer(SPI_CS_TPM,
|
|
&txBuf[TPM_TIS_HEADER_SZ-1],
|
|
&rxBuf[TPM_TIS_HEADER_SZ-1], 1,
|
|
0x1 /* 1=SPI_XFER_FLAG_CONTINUE */
|
|
);
|
|
}
|
|
/* Check for timeout */
|
|
if (ret == 0 && timeout <= 0) {
|
|
ret = TPM_RC_FAILURE;
|
|
}
|
|
|
|
/* Transfer remainder of payload (command / response) */
|
|
if (ret == 0) {
|
|
ret = spi_xfer(SPI_CS_TPM,
|
|
&txBuf[TPM_TIS_HEADER_SZ],
|
|
&rxBuf[TPM_TIS_HEADER_SZ],
|
|
xferSz-TPM_TIS_HEADER_SZ,
|
|
0 /* de-assert CS*/ );
|
|
}
|
|
/* On error make sure SPI is de-asserted */
|
|
else {
|
|
spi_xfer(SPI_CS_TPM, NULL, NULL, 0, 0);
|
|
#ifdef WOLFTPM_ADV_IO
|
|
/* don't leave the command (may hold an authValue) on the stack */
|
|
TPM2_ForceZero(txBuf, sizeof(txBuf));
|
|
TPM2_ForceZero(rxBuf, sizeof(rxBuf));
|
|
#endif
|
|
return ret;
|
|
}
|
|
#else /* Send Entire Message - no wait states */
|
|
ret = spi_xfer(SPI_CS_TPM, txBuf, rxBuf, xferSz, 0);
|
|
|
|
#ifdef WOLFTPM_DEBUG_IO
|
|
wolfBoot_printf("TPM2_IoCb: Ret %d, Sz %d\n", ret, xferSz);
|
|
wolfBoot_print_bin(txBuf, xferSz);
|
|
wolfBoot_print_bin(rxBuf, xferSz);
|
|
#endif
|
|
#endif /* !WOLFTPM_CHECK_WAIT_STATE */
|
|
|
|
#ifdef WOLFTPM_ADV_IO
|
|
if (isRead) {
|
|
memcpy(buf, &rxBuf[TPM_TIS_HEADER_SZ], size);
|
|
#ifdef WOLFTPM_DEBUG_IO
|
|
wolfBoot_print_bin(buf, size);
|
|
#endif
|
|
}
|
|
/* the staging buffers hold the raw command / response, which can carry
|
|
* a plaintext authValue - wipe them like TPM2_TIS_Read/Write() do */
|
|
TPM2_ForceZero(txBuf, sizeof(txBuf));
|
|
TPM2_ForceZero(rxBuf, sizeof(rxBuf));
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
#endif /* !ARCH_SIM && !WOLFTPM_MMIO */
|
|
|
|
#ifdef WOLFBOOT_MEASURED_BOOT
|
|
|
|
#ifdef WOLFBOOT_MEASURED_BOOT_APP_PARTITION
|
|
/* Legacy: measure the boot (application) partition */
|
|
#ifndef WOLFBOOT_NO_PARTITIONS
|
|
#define SELF_HASH_ADDR ((uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS)
|
|
#define SELF_HASH_SZ ((uint32_t)WOLFBOOT_PARTITION_SIZE)
|
|
#endif
|
|
#elif defined(ARCH_SIM)
|
|
/* Simulator: no linker script, use bootloader partition region */
|
|
#if defined(WOLFBOOT_PARTITION_BOOT_ADDRESS) && defined(ARCH_FLASH_OFFSET)
|
|
#define SELF_HASH_ADDR ((uintptr_t)ARCH_FLASH_OFFSET)
|
|
#define SELF_HASH_SZ ((uint32_t)((uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS - \
|
|
(uintptr_t)ARCH_FLASH_OFFSET))
|
|
#endif
|
|
#elif defined(WOLFBOOT_FSP)
|
|
/* FSP: stage1 boot_x86_fsp.c handles measurement via self_extend_pcr()
|
|
* and wolfBoot_image_measure(). Skip generic self-measurement here since
|
|
* stage2 .data is interleaved with .text making the hash non-deterministic */
|
|
#else
|
|
/* Default: measure wolfBoot's own code using linker script symbols */
|
|
extern unsigned int _start_text;
|
|
extern unsigned int _stored_data;
|
|
#define SELF_HASH_ADDR ((uintptr_t)&_start_text)
|
|
#define SELF_HASH_SZ ((uint32_t)((uintptr_t)&_stored_data - \
|
|
(uintptr_t)&_start_text))
|
|
#endif
|
|
|
|
#ifdef SELF_HASH_ADDR
|
|
#ifdef WOLFBOOT_HASH_SHA256
|
|
#include <wolfssl/wolfcrypt/sha256.h>
|
|
static int self_sha256(uint8_t *hash)
|
|
{
|
|
uintptr_t p = SELF_HASH_ADDR;
|
|
uint32_t sz = SELF_HASH_SZ;
|
|
uint32_t blksz, position = 0;
|
|
wc_Sha256 sha256_ctx;
|
|
|
|
wc_InitSha256(&sha256_ctx);
|
|
do {
|
|
blksz = WOLFBOOT_SHA_BLOCK_SIZE;
|
|
if (position + blksz > sz)
|
|
blksz = sz - position;
|
|
#if defined(EXT_FLASH) && defined(NO_XIP) && \
|
|
defined(WOLFBOOT_MEASURED_BOOT_APP_PARTITION)
|
|
rc = ext_flash_read(p, ext_hash_block, WOLFBOOT_SHA_BLOCK_SIZE);
|
|
if (rc != WOLFBOOT_SHA_BLOCK_SIZE)
|
|
return -1;
|
|
wc_Sha256Update(&sha256_ctx, ext_hash_block, blksz);
|
|
#else
|
|
wc_Sha256Update(&sha256_ctx, (uint8_t*)p, blksz);
|
|
#endif
|
|
position += blksz;
|
|
p += blksz;
|
|
} while (position < sz);
|
|
wc_Sha256Final(&sha256_ctx, hash);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#elif defined(WOLFBOOT_HASH_SHA384)
|
|
#include <wolfssl/wolfcrypt/sha512.h>
|
|
static int self_sha384(uint8_t *hash)
|
|
{
|
|
uintptr_t p = SELF_HASH_ADDR;
|
|
uint32_t sz = SELF_HASH_SZ;
|
|
uint32_t blksz, position = 0;
|
|
wc_Sha384 sha384_ctx;
|
|
|
|
wc_InitSha384(&sha384_ctx);
|
|
do {
|
|
blksz = WOLFBOOT_SHA_BLOCK_SIZE;
|
|
if (position + blksz > sz)
|
|
blksz = sz - position;
|
|
#if defined(EXT_FLASH) && defined(NO_XIP) && \
|
|
defined(WOLFBOOT_MEASURED_BOOT_APP_PARTITION)
|
|
rc = ext_flash_read(p, ext_hash_block, WOLFBOOT_SHA_BLOCK_SIZE);
|
|
if (rc != WOLFBOOT_SHA_BLOCK_SIZE)
|
|
return -1;
|
|
wc_Sha384Update(&sha384_ctx, ext_hash_block, blksz);
|
|
#else
|
|
wc_Sha384Update(&sha384_ctx, (uint8_t*)p, blksz);
|
|
#endif
|
|
position += blksz;
|
|
p += blksz;
|
|
} while (position < sz);
|
|
wc_Sha384Final(&sha384_ctx, hash);
|
|
|
|
return 0;
|
|
}
|
|
#endif /* HASH type */
|
|
#endif /* SELF_HASH_ADDR */
|
|
|
|
/**
|
|
* @brief Extends a PCR in the TPM with a hash.
|
|
*
|
|
* Extends a specified PCR's value in the TPM with a given hash. Uses
|
|
* TPM2_PCR_Extend. Optionally, if DEBUG_WOLFTPM or WOLFBOOT_DEBUG_TPM defined,
|
|
* prints debug info.
|
|
*
|
|
* @param[in] pcrIndex The PCR Index (0-23 is valid range).
|
|
* @param[in] hash Pointer to the hash value to extend into the PCR.
|
|
* @param[in] line Line number where the function is called (for debugging).
|
|
* @return 0 on success, an error code on failure.
|
|
*
|
|
*/
|
|
int wolfBoot_tpm2_extend(uint8_t pcrIndex, uint8_t* hash, int line)
|
|
{
|
|
int rc;
|
|
#ifdef WOLFBOOT_DEBUG_TPM
|
|
uint8_t digest[WOLFBOOT_TPM_PCR_DIG_SZ];
|
|
int digestSz = 0;
|
|
#endif
|
|
|
|
/* clear auth session for PCR */
|
|
wolfTPM2_SetAuthPassword(&wolftpm_dev, 0, NULL);
|
|
|
|
#ifdef ARCH_SIM
|
|
if (pcrIndex >= 16) {
|
|
/* reset the PCR for testing */
|
|
wolfTPM2_ResetPCR(&wolftpm_dev, pcrIndex);
|
|
}
|
|
#endif
|
|
|
|
rc = wolfTPM2_ExtendPCR(&wolftpm_dev, pcrIndex, WOLFBOOT_TPM_PCR_ALG, hash,
|
|
TPM2_GetHashDigestSize(WOLFBOOT_TPM_PCR_ALG));
|
|
#ifdef WOLFBOOT_DEBUG_TPM
|
|
if (rc == 0) {
|
|
int read_rc;
|
|
wolfBoot_printf("Measured boot: Index %d, Line %d\n", pcrIndex, line);
|
|
|
|
read_rc = wolfTPM2_ReadPCR(&wolftpm_dev, pcrIndex, WOLFBOOT_TPM_PCR_ALG,
|
|
digest, &digestSz);
|
|
|
|
wolfBoot_printf("PCR %d: Res %d, Digest Sz %d\n",
|
|
pcrIndex, read_rc, digestSz);
|
|
wolfBoot_print_bin(digest, digestSz);
|
|
}
|
|
else {
|
|
wolfBoot_printf("Measure boot failed! Index %d, %x (%s)\n",
|
|
pcrIndex, rc, wolfTPM2_GetRCString(rc));
|
|
}
|
|
#endif
|
|
(void)line;
|
|
|
|
return rc;
|
|
}
|
|
#endif /* WOLFBOOT_MEASURED_BOOT */
|
|
|
|
#if defined(WOLFBOOT_TPM_VERIFY) || defined(WOLFBOOT_TPM_SEAL)
|
|
int wolfBoot_load_pubkey(const uint8_t* pubkey_hint, WOLFTPM2_KEY* pubKey,
|
|
TPM_ALG_ID* pAlg)
|
|
{
|
|
int rc = 0;
|
|
uint32_t key_type;
|
|
int key_slot = -1;
|
|
uint8_t *hdr;
|
|
int hdrSz;
|
|
|
|
*pAlg = TPM_ALG_NULL;
|
|
|
|
/* get public key */
|
|
key_slot = keyslot_id_by_sha(pubkey_hint);
|
|
if (key_slot < 0)
|
|
rc = -1;
|
|
|
|
if (rc == 0) {
|
|
key_type = keystore_get_key_type(key_slot);
|
|
hdr = keystore_get_buffer(key_slot);
|
|
hdrSz = keystore_get_size(key_slot);
|
|
if (hdr == NULL || hdrSz <= 0 || hdrSz > KEYSTORE_PUBKEY_SIZE)
|
|
rc = -1;
|
|
}
|
|
/* Parse public key to TPM public key. Note: this loads as temp handle,
|
|
* however we don't use the handle. We still need to unload it. */
|
|
if (rc == 0) {
|
|
#if defined(WOLFBOOT_SIGN_ECC256) || \
|
|
defined(WOLFBOOT_SIGN_ECC384) || \
|
|
defined(WOLFBOOT_SIGN_ECC521)
|
|
int tpmcurve;
|
|
int point_sz = hdrSz/2;
|
|
if ( key_type == AUTH_KEY_ECC256) tpmcurve = TPM_ECC_NIST_P256;
|
|
else if (key_type == AUTH_KEY_ECC384) tpmcurve = TPM_ECC_NIST_P384;
|
|
else if (key_type == AUTH_KEY_ECC521) tpmcurve = TPM_ECC_NIST_P521;
|
|
else rc = -1; /* not supported algorithm */
|
|
if (rc == 0) {
|
|
*pAlg = TPM_ALG_ECC;
|
|
rc = wolfTPM2_LoadEccPublicKey(&wolftpm_dev, pubKey,
|
|
tpmcurve, /* Curve */
|
|
hdr, point_sz, /* Public X */
|
|
hdr + point_sz, point_sz /* Public Y */
|
|
);
|
|
}
|
|
#elif defined(WOLFBOOT_SIGN_RSA2048) || \
|
|
defined(WOLFBOOT_SIGN_RSA3072) || \
|
|
defined(WOLFBOOT_SIGN_RSA4096)
|
|
uint32_t inOutIdx = 0;
|
|
uint32_t exponent = 0;
|
|
uint32_t j;
|
|
const uint8_t*n = NULL, *e = NULL;
|
|
uint32_t nSz = 0, eSz = 0;
|
|
if (key_type != AUTH_KEY_RSA2048 && key_type != AUTH_KEY_RSA3072 &&
|
|
key_type != AUTH_KEY_RSA4096) {
|
|
rc = -1;
|
|
}
|
|
if (rc == 0) {
|
|
*pAlg = TPM_ALG_RSA;
|
|
rc = wc_RsaPublicKeyDecode_ex(hdr, &inOutIdx, hdrSz,
|
|
&n, &nSz, /* modulus */
|
|
&e, &eSz /* exponent */
|
|
);
|
|
}
|
|
if (rc == 0) {
|
|
if (eSz == 0 || eSz > sizeof(exponent))
|
|
rc = -1;
|
|
}
|
|
if (rc == 0) {
|
|
for (j = 0; j < eSz; j++) {
|
|
exponent = (exponent << 8) | e[j];
|
|
}
|
|
}
|
|
if (rc == 0) {
|
|
/* Load public key into TPM */
|
|
rc = wolfTPM2_LoadRsaPublicKey_ex(&wolftpm_dev, pubKey,
|
|
n, nSz, exponent,
|
|
TPM_ALG_NULL, WOLFBOOT_TPM_HASH_ALG);
|
|
}
|
|
#else
|
|
rc = -1; /* not supported */
|
|
#endif
|
|
}
|
|
return rc;
|
|
}
|
|
#endif /* WOLFBOOT_TPM_VERIFY || WOLFBOOT_TPM_SEAL */
|
|
|
|
#ifdef WOLFBOOT_TPM_SEAL
|
|
int wolfBoot_get_random(uint8_t* buf, int sz)
|
|
{
|
|
return wolfTPM2_GetRandom(&wolftpm_dev, buf, sz);
|
|
}
|
|
|
|
static int is_zero_digest(uint8_t* buf, size_t sz)
|
|
{
|
|
while (sz--) {
|
|
if (*buf++)
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int wolfBoot_get_pcr_active(uint8_t pcrAlg, uint32_t* pcrMask, uint8_t pcrMax)
|
|
{
|
|
int rc = 0;
|
|
PCR_Read_In pcrReadIn;
|
|
PCR_Read_Out pcrReadOut;
|
|
uint8_t pcrIndex, count = 0;
|
|
|
|
/* PCR0-15 are best for policy because they cannot be reset manually */
|
|
if (pcrMax == 0) {
|
|
#ifdef ARCH_SIM /* allow use of testing PCR's on simulator */
|
|
pcrMax = 16;
|
|
#else
|
|
pcrMax = 15;
|
|
#endif
|
|
}
|
|
*pcrMask = 0;
|
|
|
|
#ifdef WOLFBOOT_DEBUG_TPM
|
|
wolfBoot_printf("Getting active PCR's (0-%d)\n", pcrMax);
|
|
#endif
|
|
for (pcrIndex=0; pcrIndex<=pcrMax; pcrIndex++) {
|
|
memset(&pcrReadIn, 0, sizeof(pcrReadIn));
|
|
memset(&pcrReadOut, 0, sizeof(pcrReadOut));
|
|
wolfTPM2_SetupPCRSel(&pcrReadIn.pcrSelectionIn, pcrAlg, pcrIndex);
|
|
rc = TPM2_PCR_Read(&pcrReadIn, &pcrReadOut);
|
|
if (rc == 0 && !is_zero_digest(
|
|
pcrReadOut.pcrValues.digests[0].buffer,
|
|
pcrReadOut.pcrValues.digests[0].size))
|
|
{
|
|
*pcrMask |= (1 << pcrIndex);
|
|
count++;
|
|
#ifdef WOLFBOOT_DEBUG_TPM
|
|
wolfBoot_printf("PCR %d (counter %d)\n",
|
|
pcrIndex, pcrReadOut.pcrUpdateCounter);
|
|
wolfBoot_print_hexstr(pcrReadOut.pcrValues.digests[0].buffer,
|
|
pcrReadOut.pcrValues.digests[0].size, 0);
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef WOLFBOOT_DEBUG_TPM
|
|
wolfBoot_printf("Found %d active PCR's (mask 0x%08x)\n", count, *pcrMask);
|
|
#endif
|
|
return rc;
|
|
}
|
|
|
|
uint32_t wolfBoot_tpm_pcrmask_sel(uint32_t pcrMask, uint8_t* pcrArray,
|
|
uint32_t pcrArraySz)
|
|
{
|
|
int i;
|
|
uint32_t pcrArraySzAct = 0;
|
|
for (i=0; i<IMPLEMENTATION_PCR; i++) {
|
|
if (pcrMask & (1 << i)) {
|
|
/* add if we have room */
|
|
if (pcrArraySzAct < pcrArraySz) {
|
|
pcrArray[pcrArraySzAct++] = i;
|
|
}
|
|
}
|
|
}
|
|
return pcrArraySzAct;
|
|
}
|
|
|
|
/* Builds a policy digest that can be signed based on selected PCR's */
|
|
int wolfBoot_build_policy(uint8_t pcrAlg, uint32_t pcrMask,
|
|
uint8_t* policy, uint32_t* policySz,
|
|
uint8_t* policyRef, uint32_t policyRefSz)
|
|
{
|
|
int rc, i;
|
|
uint8_t pcrArray[PCR_SELECT_MAX*2];
|
|
uint32_t pcrArraySz;
|
|
uint8_t digest[WOLFBOOT_TPM_PCR_DIG_SZ];
|
|
uint32_t digestSz = 0;
|
|
uint8_t pcrDigest[WOLFBOOT_TPM_PCR_DIG_SZ];
|
|
uint32_t pcrDigestSz = 0;
|
|
|
|
/* populate PCR selection array */
|
|
memset(pcrArray, 0, sizeof(pcrArray));
|
|
pcrArraySz = wolfBoot_tpm_pcrmask_sel(pcrMask, pcrArray, sizeof(pcrArray));
|
|
|
|
/* Create a Policy PCR digest to sign externally */
|
|
memcpy(policy, (uint8_t*)&pcrMask, sizeof(pcrMask));
|
|
*policySz = (uint32_t)sizeof(pcrMask);
|
|
|
|
rc = wolfTPM2_PCRGetDigest(&wolftpm_dev, pcrAlg,
|
|
pcrArray, pcrArraySz, pcrDigest, &pcrDigestSz);
|
|
if (rc == 0) {
|
|
#ifdef WOLFBOOT_DEBUG_TPM
|
|
wolfBoot_printf("PCR Digest (%d bytes):\n", pcrDigestSz);
|
|
wolfBoot_print_hexstr(pcrDigest, pcrDigestSz, 0);
|
|
#endif
|
|
|
|
/* Build PCR Policy */
|
|
memset(digest, 0, sizeof(digest));
|
|
digestSz = TPM2_GetHashDigestSize(pcrAlg);
|
|
rc = wolfTPM2_PolicyPCRMake(pcrAlg, pcrArray, pcrArraySz,
|
|
pcrDigest, pcrDigestSz, digest, &digestSz);
|
|
}
|
|
if (rc == 0) {
|
|
/* Add policyRef (if blank just re-hash) */
|
|
rc = wolfTPM2_PolicyRefMake(pcrAlg, digest, &digestSz,
|
|
policyRef, policyRefSz);
|
|
}
|
|
if (rc == 0) {
|
|
memcpy(&policy[*policySz], digest, digestSz);
|
|
*policySz += digestSz;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/* Get the signed policy from the image header. If not found get active PCR's
|
|
* and build expected policy for current state */
|
|
int wolfBoot_get_policy(struct wolfBoot_image* img,
|
|
uint8_t** policy, uint16_t* policySz)
|
|
{
|
|
int rc = 0;
|
|
*policySz = wolfBoot_get_header(img, HDR_POLICY_SIGNATURE, policy);
|
|
if (*policySz <= 0) {
|
|
uint32_t pcrMask = 0;
|
|
TPM_ALG_ID pcrAlg = WOLFBOOT_TPM_PCR_ALG;
|
|
|
|
/* Report the status of the PCR's for signing externally */
|
|
wolfBoot_printf("Policy header not found!\n");
|
|
wolfBoot_printf("Generating policy based on active PCR's!\n");
|
|
|
|
/* Discover and print active PCR's */
|
|
rc = wolfBoot_get_pcr_active(pcrAlg, &pcrMask, 0);
|
|
if (rc == 0 && pcrMask > 0) {
|
|
uint8_t newPolicy[sizeof(pcrMask) + WOLFBOOT_TPM_PCR_DIG_SZ];
|
|
uint32_t newPolicySz = 0;
|
|
rc = wolfBoot_build_policy(pcrAlg, pcrMask, newPolicy, &newPolicySz,
|
|
NULL, 0);
|
|
if (rc == 0) {
|
|
wolfBoot_printf("PCR Mask (0x%08x) and "
|
|
"PCR Policy Digest (%d bytes):\n",
|
|
pcrMask, newPolicySz);
|
|
wolfBoot_print_hexstr(newPolicy, newPolicySz, 0);
|
|
|
|
wolfBoot_printf("Use this policy with the sign tool "
|
|
"(--policy arg) or POLICY_FILE config\n");
|
|
}
|
|
else {
|
|
wolfBoot_printf("Error building policy! %d\n", rc);
|
|
}
|
|
}
|
|
else {
|
|
wolfBoot_printf("No PCR's have been extended!\n");
|
|
}
|
|
rc = -TPM_RC_POLICY_FAIL; /* failure */
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/* authHandle = TPM_RH_PLATFORM or TPM_RH_OWNER */
|
|
/* auth is optional */
|
|
int wolfBoot_store_blob(TPMI_RH_NV_AUTH authHandle, uint32_t nvIndex,
|
|
word32 nvAttributes, WOLFTPM2_KEYBLOB* blob,
|
|
const uint8_t* auth, uint32_t authSz)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_HANDLE parent;
|
|
WOLFTPM2_NV nv;
|
|
uint8_t pubAreaBuffer[sizeof(TPM2B_PUBLIC)]; /* oversized buffer */
|
|
int nvSz, pos, pubAreaSize;
|
|
|
|
memset(&parent, 0, sizeof(parent));
|
|
memset(&nv, 0, sizeof(nv));
|
|
|
|
nv.handle.hndl = nvIndex;
|
|
if (authSz > 0) {
|
|
if (auth == NULL)
|
|
return BAD_FUNC_ARG;
|
|
if (authSz > sizeof(nv.handle.auth.buffer))
|
|
return BAD_FUNC_ARG;
|
|
nv.handle.auth.size = authSz;
|
|
memcpy(nv.handle.auth.buffer, auth, authSz);
|
|
}
|
|
|
|
parent.hndl = authHandle;
|
|
|
|
/* encode public for smaller storage */
|
|
rc = TPM2_AppendPublic(pubAreaBuffer, (word32)sizeof(pubAreaBuffer),
|
|
&pubAreaSize, &blob->pub);
|
|
if (rc == 0) {
|
|
blob->pub.size = pubAreaSize;
|
|
|
|
nvSz = (uint32_t)sizeof(blob->pub.size) + blob->pub.size;
|
|
nvSz += (uint32_t)sizeof(blob->priv.size) + blob->priv.size;
|
|
|
|
/* Create NV */
|
|
rc = wolfTPM2_NVCreateAuth(&wolftpm_dev, &parent, &nv,
|
|
nv.handle.hndl, nvAttributes, nvSz, auth, authSz);
|
|
if (rc == TPM_RC_NV_DEFINED) {
|
|
/* allow use of existing handle - ignore this error */
|
|
rc = 0;
|
|
}
|
|
}
|
|
/* write sealed blob to NV */
|
|
if (rc == 0) {
|
|
wolfTPM2_UnsetAuth(&wolftpm_dev, 1);
|
|
pos = 0;
|
|
/* write pub size */
|
|
rc = wolfTPM2_NVWriteAuth(&wolftpm_dev, &nv, nv.handle.hndl,
|
|
(uint8_t*)&blob->pub.size,
|
|
(uint32_t)sizeof(blob->pub.size), pos);
|
|
}
|
|
if (rc == 0) {
|
|
pos += sizeof(blob->pub.size);
|
|
/* write pub */
|
|
rc = wolfTPM2_NVWriteAuth(&wolftpm_dev, &nv, nv.handle.hndl,
|
|
pubAreaBuffer, blob->pub.size, pos);
|
|
}
|
|
if (rc == 0) {
|
|
pos += blob->pub.size;
|
|
/* write priv size */
|
|
rc = wolfTPM2_NVWriteAuth(&wolftpm_dev, &nv, nv.handle.hndl,
|
|
(uint8_t*)&blob->priv.size,
|
|
(uint32_t)sizeof(blob->priv.size), pos);
|
|
}
|
|
if (rc == 0) {
|
|
pos += sizeof(blob->priv.size);
|
|
/* write priv */
|
|
rc = wolfTPM2_NVWriteAuth(&wolftpm_dev, &nv, nv.handle.hndl,
|
|
blob->priv.buffer, blob->priv.size, pos);
|
|
}
|
|
if (rc == 0) {
|
|
pos += blob->priv.size;
|
|
}
|
|
if (rc == 0) {
|
|
wolfBoot_printf("Wrote %d bytes to NV index 0x%x\n",
|
|
pos, nv.handle.hndl);
|
|
}
|
|
else {
|
|
wolfBoot_printf("Error %d writing blob to NV index %x (error %s)\n",
|
|
rc, nv.handle.hndl, wolfTPM2_GetRCString(rc));
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int wolfBoot_read_blob(uint32_t nvIndex, WOLFTPM2_KEYBLOB* blob,
|
|
const uint8_t* auth, uint32_t authSz)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_NV nv;
|
|
uint8_t pubAreaBuffer[sizeof(TPM2B_PUBLIC)];
|
|
uint32_t readSz;
|
|
int nvSz, pubAreaSize = 0, pos;
|
|
|
|
memset(&nv, 0, sizeof(nv));
|
|
|
|
nv.handle.hndl = nvIndex;
|
|
if (authSz > 0) {
|
|
if (auth == NULL)
|
|
return BAD_FUNC_ARG;
|
|
if (authSz > sizeof(nv.handle.auth.buffer))
|
|
return BAD_FUNC_ARG;
|
|
nv.handle.auth.size = authSz;
|
|
memcpy(nv.handle.auth.buffer, auth, authSz);
|
|
}
|
|
wolfTPM2_SetAuthHandle(&wolftpm_dev, 0, &nv.handle);
|
|
|
|
pos = 0;
|
|
readSz = sizeof(blob->pub.size);
|
|
rc = wolfTPM2_NVReadAuth(&wolftpm_dev, &nv, nv.handle.hndl,
|
|
(uint8_t*)&blob->pub.size, &readSz, pos);
|
|
if (rc == 0) {
|
|
pos += readSz;
|
|
if (blob->pub.size > sizeof(pubAreaBuffer)) {
|
|
rc = BUFFER_E;
|
|
}
|
|
else {
|
|
readSz = blob->pub.size;
|
|
rc = wolfTPM2_NVReadAuth(&wolftpm_dev, &nv, nv.handle.hndl,
|
|
pubAreaBuffer, &readSz, pos);
|
|
}
|
|
}
|
|
if (rc == 0) {
|
|
pos += readSz;
|
|
rc = TPM2_ParsePublic(&blob->pub, pubAreaBuffer,
|
|
(word32)sizeof(pubAreaBuffer), &pubAreaSize);
|
|
}
|
|
if (rc == 0) {
|
|
readSz = sizeof(blob->priv.size);
|
|
rc = wolfTPM2_NVReadAuth(&wolftpm_dev, &nv, nv.handle.hndl,
|
|
(uint8_t*)&blob->priv.size, &readSz, pos);
|
|
}
|
|
if (rc == 0) {
|
|
pos += sizeof(blob->priv.size);
|
|
if (blob->priv.size > sizeof(blob->priv.buffer)) {
|
|
rc = BUFFER_E;
|
|
}
|
|
else {
|
|
readSz = blob->priv.size;
|
|
rc = wolfTPM2_NVReadAuth(&wolftpm_dev, &nv, nv.handle.hndl,
|
|
blob->priv.buffer, &readSz, pos);
|
|
}
|
|
}
|
|
if (rc == 0) {
|
|
pos += blob->priv.size;
|
|
}
|
|
if (rc == 0) {
|
|
wolfBoot_printf("Read %d bytes from NV index 0x%x\n",
|
|
pos, nv.handle.hndl);
|
|
}
|
|
else {
|
|
wolfBoot_printf("Error %d reading blob from NV index %x (error %s)\n",
|
|
rc, nv.handle.hndl, wolfTPM2_GetRCString(rc));
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int wolfBoot_delete_blob(TPMI_RH_NV_AUTH authHandle, uint32_t nvIndex,
|
|
const uint8_t* auth, uint32_t authSz)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_HANDLE parent;
|
|
WOLFTPM2_NV nv;
|
|
|
|
memset(&parent, 0, sizeof(parent));
|
|
memset(&nv, 0, sizeof(nv));
|
|
|
|
nv.handle.hndl = nvIndex;
|
|
if (authSz > 0) {
|
|
if (auth == NULL)
|
|
return BAD_FUNC_ARG;
|
|
if (authSz > sizeof(nv.handle.auth.buffer))
|
|
return BAD_FUNC_ARG;
|
|
nv.handle.auth.size = authSz;
|
|
memcpy(nv.handle.auth.buffer, auth, authSz);
|
|
}
|
|
|
|
parent.hndl = authHandle;
|
|
|
|
rc = wolfTPM2_NVOpen(&wolftpm_dev, &nv, nvIndex, auth, authSz);
|
|
if (rc == 0) {
|
|
rc = wolfTPM2_NVDeleteAuth(&wolftpm_dev, &parent, nvIndex);
|
|
}
|
|
if (rc != 0) {
|
|
wolfBoot_printf("Error %d deleting blob from NV index %x (error %s)\n",
|
|
rc, nv.handle.hndl, wolfTPM2_GetRCString(rc));
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/* The secret is sealed based on a policy authorization from a public key. */
|
|
int wolfBoot_seal_blob(const uint8_t* pubkey_hint,
|
|
const uint8_t* policy, uint16_t policySz,
|
|
WOLFTPM2_KEYBLOB* seal_blob, const uint8_t* secret, int secret_sz,
|
|
const uint8_t* auth, int authSz)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_SESSION policy_session;
|
|
TPM_ALG_ID pcrAlg = WOLFBOOT_TPM_PCR_ALG;
|
|
TPM_ALG_ID alg;
|
|
TPMT_PUBLIC template;
|
|
WOLFTPM2_KEY authKey;
|
|
uint8_t *hdr;
|
|
uint16_t hdrSz;
|
|
|
|
if (policy == NULL || policySz <= 0 || secret == NULL ||
|
|
secret_sz > WOLFBOOT_MAX_SEAL_SZ) {
|
|
return -1;
|
|
}
|
|
|
|
memset(&authKey, 0, sizeof(authKey));
|
|
memset(&template, 0, sizeof(template));
|
|
memset(&policy_session, 0, sizeof(policy_session));
|
|
|
|
/* get public key for policy authorization */
|
|
rc = wolfBoot_load_pubkey(pubkey_hint, &authKey, &alg);
|
|
|
|
#ifdef WOLFBOOT_DEBUG_TPM
|
|
wolfBoot_printf("Seal: Pub Key %d\n", alg);
|
|
#endif
|
|
|
|
/* The handle for the public key if not needed, so unload it.
|
|
* For seal only a populated TPM2B_PUBLIC is required */
|
|
wolfTPM2_UnloadHandle(&wolftpm_dev, &authKey.handle);
|
|
|
|
if (rc == 0) {
|
|
/* Setup a TPM session that can be used for parameter encryption */
|
|
rc = wolfTPM2_StartSession(&wolftpm_dev, &policy_session, &wolftpm_srk,
|
|
NULL, TPM_SE_POLICY, TPM_ALG_CFB);
|
|
}
|
|
if (rc == 0) {
|
|
/* enable parameter encryption for seal */
|
|
rc = wolfTPM2_SetAuthSession(&wolftpm_dev, 1, &policy_session,
|
|
(TPMA_SESSION_decrypt | TPMA_SESSION_encrypt |
|
|
TPMA_SESSION_continueSession));
|
|
}
|
|
if (rc == 0) {
|
|
/* build authorization policy based on public key */
|
|
/* digest here is input and output, must be zero'd */
|
|
uint32_t digestSz = TPM2_GetHashDigestSize(pcrAlg);
|
|
/* Create a new key for sealing using external signing auth */
|
|
wolfTPM2_GetKeyTemplate_KeySeal(&template, pcrAlg);
|
|
memset(template.authPolicy.buffer, 0, digestSz);
|
|
rc = wolfTPM2_PolicyAuthorizeMake(pcrAlg, &authKey.pub,
|
|
template.authPolicy.buffer, &digestSz, NULL, 0);
|
|
template.authPolicy.size = digestSz;
|
|
}
|
|
if (rc == 0) {
|
|
#ifdef WOLFBOOT_DEBUG_TPM
|
|
wolfBoot_printf("Policy Authorize Digest (%d bytes):\n",
|
|
template.authPolicy.size);
|
|
wolfBoot_print_hexstr(template.authPolicy.buffer,
|
|
template.authPolicy.size, 0);
|
|
#endif
|
|
|
|
if (auth != NULL && authSz > 0) {
|
|
/* allow password based sealing */
|
|
template.objectAttributes |= TPMA_OBJECT_userWithAuth;
|
|
}
|
|
else {
|
|
/* disable password based sealing, require policy */
|
|
template.objectAttributes &= ~TPMA_OBJECT_userWithAuth;
|
|
}
|
|
rc = wolfTPM2_CreateKeySeal_ex(&wolftpm_dev, seal_blob,
|
|
&wolftpm_srk.handle, &template, auth, authSz,
|
|
pcrAlg, NULL, 0, secret, secret_sz);
|
|
}
|
|
|
|
wolfTPM2_UnloadHandle(&wolftpm_dev, &policy_session.handle);
|
|
wolfTPM2_UnsetAuthSession(&wolftpm_dev, 1, &wolftpm_session);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int wolfBoot_delete_seal(int index)
|
|
{
|
|
return wolfBoot_delete_blob(TPM_RH_PLATFORM,
|
|
WOLFBOOT_TPM_SEAL_NV_BASE + index, NULL, 0);
|
|
}
|
|
|
|
/* Index (0-X) determines location in NV from WOLFBOOT_TPM_SEAL_NV_BASE to
|
|
* store sealed blob */
|
|
int wolfBoot_seal_auth(const uint8_t* pubkey_hint,
|
|
const uint8_t* policy, uint16_t policySz,
|
|
int index, const uint8_t* secret, int secret_sz,
|
|
const uint8_t* auth, int authSz)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_KEYBLOB seal_blob;
|
|
word32 nvAttributes;
|
|
|
|
if (authSz < 0)
|
|
return BAD_FUNC_ARG;
|
|
if (auth == NULL && authSz > 0)
|
|
return BAD_FUNC_ARG;
|
|
if (authSz > (int)sizeof(seal_blob.handle.auth.buffer))
|
|
return BAD_FUNC_ARG;
|
|
|
|
memset(&seal_blob, 0, sizeof(seal_blob));
|
|
|
|
seal_blob.handle.auth.size = authSz;
|
|
|
|
if (auth != NULL)
|
|
memcpy(seal_blob.handle.auth.buffer, auth, authSz);
|
|
|
|
/* creates a sealed keyed hash object (not loaded to TPM) */
|
|
rc = wolfBoot_seal_blob(pubkey_hint, policy, policySz, &seal_blob,
|
|
secret, secret_sz, auth, authSz);
|
|
if (rc == 0) {
|
|
#ifdef WOLFBOOT_DEBUG_TPM
|
|
wolfBoot_printf("Sealed keyed hash (pub %d, priv %d bytes):\n",
|
|
seal_blob.pub.size, seal_blob.priv.size);
|
|
#endif
|
|
|
|
/* Get NV attributes amd allow it to be locked (if desired) */
|
|
wolfTPM2_GetNvAttributesTemplate(TPM_RH_PLATFORM, &nvAttributes);
|
|
nvAttributes |= TPMA_NV_WRITEDEFINE;
|
|
|
|
/* delete if already exists */
|
|
(void)wolfBoot_delete_blob(TPM_RH_PLATFORM,
|
|
WOLFBOOT_TPM_SEAL_NV_BASE + index, NULL, 0);
|
|
|
|
rc = wolfBoot_store_blob(TPM_RH_PLATFORM,
|
|
WOLFBOOT_TPM_SEAL_NV_BASE + index,
|
|
nvAttributes, &seal_blob,
|
|
/* do not use NV auth to store blob, since the password cannot be
|
|
* encrypted and sealed blob is already symmetrically encrypted
|
|
* using the a derived key from the seed */
|
|
NULL, 0
|
|
);
|
|
}
|
|
if (rc != 0) {
|
|
wolfBoot_printf("Error %d sealing secret! (%s)\n",
|
|
rc, wolfTPM2_GetRCString(rc));
|
|
}
|
|
return rc;
|
|
}
|
|
int wolfBoot_seal(const uint8_t* pubkey_hint,
|
|
const uint8_t* policy, uint16_t policySz,
|
|
int index, const uint8_t* secret, int secret_sz)
|
|
{
|
|
const char* auth = NULL;
|
|
int authSz = 0;
|
|
#ifdef WOLFBOOT_TPM_SEAL_AUTH
|
|
auth = WOLFBOOT_TPM_SEAL_AUTH;
|
|
authSz = (int)strlen(auth);
|
|
#endif
|
|
return wolfBoot_seal_auth(pubkey_hint, policy, policySz, index,
|
|
secret, secret_sz, (const uint8_t*)auth, authSz);
|
|
}
|
|
|
|
/* The unseal requires a signed policy from HDR_POLICY_SIGNATURE */
|
|
int wolfBoot_unseal_blob(const uint8_t* pubkey_hint,
|
|
const uint8_t* policy, uint16_t policySz,
|
|
WOLFTPM2_KEYBLOB* seal_blob, uint8_t* secret, int* secret_sz,
|
|
const uint8_t* auth, int authSz)
|
|
{
|
|
int rc, i;
|
|
int secret_capacity;
|
|
WOLFTPM2_SESSION policy_session;
|
|
uint32_t key_type;
|
|
TPM_ALG_ID pcrAlg = WOLFBOOT_TPM_PCR_ALG;
|
|
TPM_ALG_ID alg = TPM_ALG_NULL, sigAlg;
|
|
TPMT_PUBLIC template;
|
|
WOLFTPM2_KEY authKey;
|
|
TPMT_TK_VERIFIED checkTicket;
|
|
Unseal_In unsealIn;
|
|
Unseal_Out unsealOut;
|
|
uint32_t pcrMask;
|
|
uint8_t pcrDigest[WOLFBOOT_TPM_PCR_DIG_SZ];
|
|
uint32_t pcrDigestSz;
|
|
uint8_t policyDigest[WOLFBOOT_TPM_PCR_DIG_SZ];
|
|
uint32_t policyDigestSz;
|
|
uint8_t pcrArray[PCR_SELECT_MAX*2];
|
|
uint32_t pcrArraySz = 0;
|
|
uint8_t* policyRef = NULL; /* optional nonce */
|
|
uint32_t policyRefSz = 0;
|
|
|
|
if (policy == NULL || policySz < (uint16_t)sizeof(pcrMask) ||
|
|
secret == NULL || secret_sz == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
secret_capacity = *secret_sz;
|
|
*secret_sz = 0; /* init */
|
|
|
|
if (secret_capacity < 0) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* extract pcrMask and populate PCR selection array */
|
|
memcpy(&pcrMask, policy, sizeof(pcrMask));
|
|
memset(pcrArray, 0, sizeof(pcrArray));
|
|
pcrArraySz = wolfBoot_tpm_pcrmask_sel(pcrMask, pcrArray, sizeof(pcrArray));
|
|
|
|
#ifdef WOLFBOOT_DEBUG_TPM
|
|
wolfBoot_printf("Unseal: PCR mask 0x%x (sz %d)\n", pcrMask, pcrArraySz);
|
|
#endif
|
|
|
|
/* skip to signature */
|
|
policy += sizeof(pcrMask);
|
|
policySz -= sizeof(pcrMask);
|
|
|
|
memset(&authKey, 0, sizeof(authKey));
|
|
memset(&template, 0, sizeof(template));
|
|
memset(&policy_session, 0, sizeof(policy_session));
|
|
memset(&checkTicket, 0, sizeof(checkTicket));
|
|
|
|
/* Setup a TPM session that can be used for parameter encryption */
|
|
rc = wolfTPM2_StartSession(&wolftpm_dev, &policy_session, &wolftpm_srk,
|
|
NULL, TPM_SE_POLICY, TPM_ALG_CFB);
|
|
if (rc == 0) {
|
|
/* enable parameter encryption for unseal */
|
|
rc = wolfTPM2_SetAuthSession(&wolftpm_dev, 1, &policy_session,
|
|
(TPMA_SESSION_decrypt | TPMA_SESSION_encrypt |
|
|
TPMA_SESSION_continueSession));
|
|
}
|
|
if (rc == 0) {
|
|
/* Get PCR policy digest */
|
|
rc = wolfTPM2_PolicyPCR(&wolftpm_dev, policy_session.handle.hndl,
|
|
pcrAlg, pcrArray, pcrArraySz);
|
|
}
|
|
if (rc == 0) {
|
|
pcrDigestSz = (uint32_t)sizeof(pcrDigest);
|
|
rc = wolfTPM2_GetPolicyDigest(&wolftpm_dev, policy_session.handle.hndl,
|
|
pcrDigest, &pcrDigestSz);
|
|
}
|
|
if (rc == 0) {
|
|
#ifdef WOLFBOOT_DEBUG_TPM
|
|
wolfBoot_printf("PCR Policy Digest (%d bytes):\n", pcrDigestSz);
|
|
wolfBoot_print_hexstr(pcrDigest, pcrDigestSz, pcrDigestSz);
|
|
#endif
|
|
|
|
/* Add policyRef (if blank just re-hash) */
|
|
policyDigestSz = pcrDigestSz;
|
|
memcpy(policyDigest, pcrDigest, pcrDigestSz);
|
|
rc = wolfTPM2_PolicyRefMake(pcrAlg, policyDigest, &policyDigestSz,
|
|
policyRef, policyRefSz);
|
|
}
|
|
if (rc == 0) {
|
|
#ifdef WOLFBOOT_DEBUG_TPM
|
|
wolfBoot_printf("PCR Policy (%d bytes):\n", policyDigestSz);
|
|
wolfBoot_print_hexstr(policyDigest, policyDigestSz, policyDigestSz);
|
|
#endif
|
|
|
|
/* get public key for policy authorization */
|
|
rc = wolfBoot_load_pubkey(pubkey_hint, &authKey, &alg);
|
|
}
|
|
if (rc == 0) {
|
|
sigAlg = alg == TPM_ALG_RSA ? TPM_ALG_RSASSA : TPM_ALG_ECDSA;
|
|
rc = wolfTPM2_VerifyHashTicket(&wolftpm_dev, &authKey,
|
|
policy, policySz, policyDigest, policyDigestSz,
|
|
sigAlg, pcrAlg, &checkTicket);
|
|
}
|
|
if (rc == 0) {
|
|
#ifdef WOLFBOOT_DEBUG_TPM
|
|
wolfBoot_printf("Verify ticket: tag 0x%x, hi 0x%x, digest %d\n",
|
|
checkTicket.tag, checkTicket.hierarchy, checkTicket.digest.size);
|
|
wolfBoot_print_hexstr(checkTicket.digest.buffer,
|
|
checkTicket.digest.size, 32);
|
|
#endif
|
|
rc = wolfTPM2_PolicyAuthorize(&wolftpm_dev, policy_session.handle.hndl,
|
|
&authKey.pub, &checkTicket, pcrDigest, pcrDigestSz,
|
|
policyRef, policyRefSz);
|
|
if (rc != 0) {
|
|
/* A failure here means the signed policy did not match expected
|
|
* policy. Use this PCR mask and policy digest with the sign tool
|
|
* --policy= argument to sign */
|
|
wolfBoot_printf("Policy signature failed!\n");
|
|
wolfBoot_printf("Expected PCR Mask (0x%08x) and PCR Policy (%d)\n",
|
|
pcrMask, policyDigestSz);
|
|
wolfBoot_print_hexstr(policyDigest, policyDigestSz, 0);
|
|
}
|
|
}
|
|
|
|
/* done with authorization public key */
|
|
wolfTPM2_UnloadHandle(&wolftpm_dev, &authKey.handle);
|
|
|
|
if (rc == 0) {
|
|
/* load the seal blob */
|
|
rc = wolfTPM2_LoadKey(&wolftpm_dev, seal_blob, &wolftpm_srk.handle);
|
|
}
|
|
if (rc == 0) {
|
|
#ifdef WOLFBOOT_DEBUG_TPM
|
|
wolfBoot_printf("Loaded seal blob to 0x%x\n",
|
|
(uint32_t)seal_blob->handle.hndl);
|
|
#endif
|
|
|
|
/* if using password auth, set it otherwise use policy auth */
|
|
if (authSz < 0) {
|
|
rc = BAD_FUNC_ARG;
|
|
goto exit;
|
|
}
|
|
if (auth != NULL && authSz > 0) {
|
|
if (authSz > (int)sizeof(seal_blob->handle.auth.buffer)) {
|
|
rc = BAD_FUNC_ARG;
|
|
goto exit;
|
|
}
|
|
seal_blob->handle.auth.size = authSz;
|
|
memcpy(seal_blob->handle.auth.buffer, auth, authSz);
|
|
wolfTPM2_SetAuthHandle(&wolftpm_dev, 0, &seal_blob->handle);
|
|
}
|
|
else {
|
|
/* use the policy session for unseal */
|
|
rc = wolfTPM2_SetAuthSession(&wolftpm_dev, 0, &policy_session,
|
|
(TPMA_SESSION_decrypt | TPMA_SESSION_encrypt |
|
|
TPMA_SESSION_continueSession));
|
|
/* set the sealed object name 0 (required) */
|
|
wolfTPM2_SetAuthHandleName(&wolftpm_dev, 0, &seal_blob->handle);
|
|
}
|
|
|
|
/* unseal */
|
|
unsealIn.itemHandle = seal_blob->handle.hndl;
|
|
rc = TPM2_Unseal(&unsealIn, &unsealOut);
|
|
}
|
|
if (rc == 0) {
|
|
if (unsealOut.outData.size > WOLFBOOT_MAX_SEAL_SZ ||
|
|
(int)unsealOut.outData.size > secret_capacity) {
|
|
rc = BUFFER_E;
|
|
}
|
|
else {
|
|
*secret_sz = unsealOut.outData.size;
|
|
memcpy(secret, unsealOut.outData.buffer, *secret_sz);
|
|
}
|
|
}
|
|
TPM2_ForceZero(&unsealOut, sizeof(unsealOut));
|
|
|
|
exit:
|
|
wolfTPM2_UnloadHandle(&wolftpm_dev, &seal_blob->handle);
|
|
wolfTPM2_UnloadHandle(&wolftpm_dev, &policy_session.handle);
|
|
wolfTPM2_UnsetAuthSession(&wolftpm_dev, 1, &wolftpm_session);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int wolfBoot_unseal_auth(const uint8_t* pubkey_hint,
|
|
const uint8_t* policy, uint16_t policySz,
|
|
int index, uint8_t* secret, int* secret_sz,
|
|
const uint8_t* auth, int authSz)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_KEYBLOB seal_blob;
|
|
|
|
memset(&seal_blob, 0, sizeof(seal_blob));
|
|
|
|
/* Do not use NV auth, since it cannot be encrypted on transport. The
|
|
* sealed blob is already encrypted and can optionally require a password
|
|
* to unseal */
|
|
rc = wolfBoot_read_blob(WOLFBOOT_TPM_SEAL_NV_BASE + index, &seal_blob,
|
|
NULL, 0);
|
|
if (rc == 0) {
|
|
rc = wolfBoot_unseal_blob(pubkey_hint, policy, policySz, &seal_blob,
|
|
secret, secret_sz, auth, authSz);
|
|
#ifdef WOLFBOOT_DEBUG_TPM
|
|
if (rc == 0) {
|
|
wolfBoot_printf("Unsealed keyed hash (pub %d, priv %d bytes):\n",
|
|
seal_blob.pub.size, seal_blob.priv.size);
|
|
}
|
|
#endif
|
|
}
|
|
if (rc != 0) {
|
|
wolfBoot_printf("Error %d unsealing secret! (%s)\n",
|
|
rc, wolfTPM2_GetRCString(rc));
|
|
}
|
|
return rc;
|
|
}
|
|
int wolfBoot_unseal(const uint8_t* pubkey_hint,
|
|
const uint8_t* policy, uint16_t policySz,
|
|
int index, uint8_t* secret, int* secret_sz)
|
|
{
|
|
const char* auth = NULL;
|
|
int authSz = 0;
|
|
#ifdef WOLFBOOT_TPM_SEAL_AUTH
|
|
auth = WOLFBOOT_TPM_SEAL_AUTH;
|
|
authSz = (int)strlen(auth);
|
|
#endif
|
|
return wolfBoot_unseal_auth(pubkey_hint, policy, policySz, index,
|
|
secret, secret_sz, (const uint8_t*)auth, authSz);
|
|
}
|
|
#endif /* WOLFBOOT_TPM_SEAL */
|
|
|
|
#if (defined(WOLFBOOT_TPM_KEYSTORE) || defined(WOLFBOOT_TPM_SEAL)) && \
|
|
defined(WC_RNG_SEED_CB)
|
|
static int wolfRNG_GetSeedCB(OS_Seed* os, uint8_t* seed, uint32_t sz)
|
|
{
|
|
int rc;
|
|
(void)os;
|
|
/* enable parameter encryption for the RNG request */
|
|
rc = wolfTPM2_SetAuthSession(&wolftpm_dev, 0, &wolftpm_session,
|
|
(TPMA_SESSION_decrypt | TPMA_SESSION_encrypt |
|
|
TPMA_SESSION_continueSession));
|
|
if (rc == 0) {
|
|
rc = wolfTPM2_GetRandom(&wolftpm_dev, seed, sz);
|
|
}
|
|
wolfTPM2_UnsetAuthSession(&wolftpm_dev, 0, &wolftpm_session);
|
|
return rc;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* API's that are callable from non-secure code */
|
|
|
|
/* Validate that a buffer supplied by the non-secure caller lives in the
|
|
* non-secure world before the secure side dereferences it. Without this check a
|
|
* non-secure caller could pass a pointer into Secure SRAM and turn these veneers
|
|
* into a confused-deputy write primitive against Secure memory. The check
|
|
* verifies only the Secure/Non-secure attribution (CMSE_NONSECURE); the MPU
|
|
* read/write permission bits are deliberately not required, as they read back as
|
|
* 0 when the NS MPU is disabled (NO_MPU) and do not constrain Secure accesses to
|
|
* NS memory anyway. Outside of a CMSE secure build there is no security
|
|
* boundary, so the checks collapse to a simple non-NULL pass-through. */
|
|
#if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
|
|
#include <arm_cmse.h>
|
|
#define WOLFBOOT_TPM_NS_RW(p, sz) \
|
|
cmse_check_address_range((void*)(p), (size_t)(sz), CMSE_NONSECURE)
|
|
#define WOLFBOOT_TPM_NS_R(p, sz) \
|
|
cmse_check_address_range((void*)(p), (size_t)(sz), CMSE_NONSECURE)
|
|
#else
|
|
#define WOLFBOOT_TPM_NS_RW(p, sz) ((void*)(p))
|
|
#define WOLFBOOT_TPM_NS_R(p, sz) ((void*)(p))
|
|
#endif
|
|
|
|
int CSME_NSE_API wolfBoot_tpm2_caps(WOLFTPM2_CAPS* caps)
|
|
{
|
|
if (WOLFBOOT_TPM_NS_RW(caps, sizeof(*caps)) == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
memset(caps, 0, sizeof(*caps));
|
|
return wolfTPM2_GetCapabilities(&wolftpm_dev, caps);
|
|
}
|
|
|
|
int CSME_NSE_API wolfBoot_tpm2_get_handles(TPM_HANDLE handle, TPML_HANDLE* handles)
|
|
{
|
|
if (WOLFBOOT_TPM_NS_RW(handles, sizeof(*handles)) == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
memset(handles, 0, sizeof(*handles));
|
|
return wolfTPM2_GetHandles(handle, handles);
|
|
}
|
|
|
|
const char* CSME_NSE_API wolfBoot_tpm2_get_alg_name(TPM_ALG_ID alg,
|
|
char* name, int name_sz)
|
|
{
|
|
const char* s_name;
|
|
if (name == NULL || name_sz <= 0) {
|
|
return NULL;
|
|
}
|
|
if (WOLFBOOT_TPM_NS_RW(name, name_sz) == NULL) {
|
|
return NULL;
|
|
}
|
|
s_name = TPM2_GetAlgName(alg);
|
|
if (s_name != NULL && name != NULL && name_sz > 0) {
|
|
strncpy(name, s_name, name_sz - 1);
|
|
name[name_sz - 1] = '\0';
|
|
}
|
|
else {
|
|
strncpy(name, "Unknown", name_sz - 1);
|
|
name[name_sz - 1] = '\0';
|
|
}
|
|
return (const char*)name;
|
|
}
|
|
|
|
const char* CSME_NSE_API wolfBoot_tpm2_get_rc_string(int rc, char* error, int error_sz)
|
|
{
|
|
const char* s_error;
|
|
if (error == NULL || error_sz <= 0) {
|
|
return NULL;
|
|
}
|
|
if (WOLFBOOT_TPM_NS_RW(error, error_sz) == NULL) {
|
|
return NULL;
|
|
}
|
|
s_error = TPM2_GetRCString(rc);
|
|
if (s_error != NULL && error != NULL && error_sz > 0) {
|
|
strncpy(error, s_error, error_sz - 1);
|
|
error[error_sz - 1] = '\0';
|
|
}
|
|
else {
|
|
strncpy(error, "Unknown", error_sz - 1);
|
|
error[error_sz - 1] = '\0';
|
|
}
|
|
return (const char*)error;
|
|
}
|
|
|
|
int CSME_NSE_API wolfBoot_tpm2_get_capability(GetCapability_In* in, GetCapability_Out* out)
|
|
{
|
|
if (WOLFBOOT_TPM_NS_R(in, sizeof(*in)) == NULL ||
|
|
WOLFBOOT_TPM_NS_RW(out, sizeof(*out)) == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
return (int)TPM2_GetCapability(in, out);
|
|
}
|
|
|
|
int CSME_NSE_API wolfBoot_tpm2_read_pcr(uint8_t pcrIndex, uint8_t* digest, int* digestSz)
|
|
{
|
|
if (WOLFBOOT_TPM_NS_RW(digest,
|
|
TPM2_GetHashDigestSize(WOLFBOOT_TPM_PCR_ALG)) == NULL ||
|
|
WOLFBOOT_TPM_NS_RW(digestSz, sizeof(*digestSz)) == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
return wolfTPM2_ReadPCR(&wolftpm_dev, pcrIndex, WOLFBOOT_TPM_PCR_ALG,
|
|
digest, digestSz);
|
|
}
|
|
|
|
int CSME_NSE_API wolfBoot_tpm2_read_cert(uint32_t handle, uint8_t* cert, uint32_t* certSz)
|
|
{
|
|
uint32_t certCapacity;
|
|
int rc;
|
|
|
|
if (WOLFBOOT_TPM_NS_RW(certSz, sizeof(*certSz)) == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
/* single-fetch *certSz so it cannot be re-read after validation: wolfTPM
|
|
* checks the capacity again before filling 'cert', and a racing non-secure
|
|
* agent would otherwise enlarge it in between to reopen the write past the
|
|
* range validated here */
|
|
certCapacity = *(volatile const uint32_t*)certSz;
|
|
if (certCapacity == 0) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
if (WOLFBOOT_TPM_NS_RW(cert, certCapacity) == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
wolfTPM2_SetAuthPassword(&wolftpm_dev, 0, NULL);
|
|
rc = wolfTPM2_NVReadCert(&wolftpm_dev, handle, cert, &certCapacity);
|
|
*certSz = certCapacity;
|
|
return rc;
|
|
}
|
|
|
|
#ifdef WOLFTPM_MFG_IDENTITY
|
|
#ifndef WOLFBOOT_TPM_MFG_AUTH_DERIVE
|
|
/* Copy a precomputed authValue directly into a TPM handle. */
|
|
static int wolfBoot_tpm2_set_handle_auth(WOLFTPM2_HANDLE* handle,
|
|
const uint8_t* auth, uint16_t authSz)
|
|
{
|
|
if (authSz > (uint16_t)sizeof(handle->auth.buffer)) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
handle->auth.size = authSz;
|
|
XMEMCPY(handle->auth.buffer, auth, authSz);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int CSME_NSE_API wolfBoot_tpm2_get_aik(WOLFTPM2_KEY* aik,
|
|
uint8_t* authOverride, uint16_t authOverrideSz)
|
|
{
|
|
int rc;
|
|
if (aik == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
if (WOLFBOOT_TPM_NS_RW(aik, sizeof(*aik)) == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
if (authOverride != NULL &&
|
|
WOLFBOOT_TPM_NS_R(authOverride, authOverrideSz) == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* Load existing AIK and set auth */
|
|
rc = wolfTPM2_ReadPublicKey(&wolftpm_dev, aik, TPM2_IAK_KEY_HANDLE);
|
|
if (rc == 0) {
|
|
#ifdef WOLFBOOT_TPM_MFG_AUTH_DERIVE
|
|
/* Derives the authValue on-device from a master secret shared across the
|
|
* reel; the precomputed default is preferred. Supply NULL for
|
|
* authOverride to use the sample default. */
|
|
rc = wolfTPM2_SetIdentityAuth(&wolftpm_dev, &aik->handle,
|
|
authOverride, authOverrideSz);
|
|
#else
|
|
/* Precomputed (default): set the final per-device authValue directly (no
|
|
* master secret on device). Caller may override the default via
|
|
* authOverride. */
|
|
static const uint8_t aikAuth[] = WOLFBOOT_TPM_MFG_AIK_AUTH;
|
|
const uint8_t* auth = (authOverride != NULL) ? authOverride : aikAuth;
|
|
uint16_t authSz = (authOverride != NULL) ?
|
|
authOverrideSz : (uint16_t)sizeof(aikAuth);
|
|
rc = wolfBoot_tpm2_set_handle_auth(&aik->handle, auth, authSz);
|
|
#endif
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int CSME_NSE_API wolfBoot_tpm2_get_timestamp(WOLFTPM2_KEY* aik, GetTime_Out* getTime)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_HANDLE eh_handle;
|
|
#ifdef WOLFBOOT_TPM_MFG_AUTH_DERIVE
|
|
/* EH master secret (shared across the reel) */
|
|
uint8_t Master_EH_AuthValue[] = WOLFBOOT_TPM_MFG_EH_MASTER;
|
|
#else
|
|
/* final per-device EH authValue */
|
|
static const uint8_t eh_auth[] = WOLFBOOT_TPM_MFG_EH_AUTH;
|
|
#endif
|
|
|
|
if (aik == NULL || getTime == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
if (WOLFBOOT_TPM_NS_RW(aik, sizeof(*aik)) == NULL ||
|
|
WOLFBOOT_TPM_NS_RW(getTime, sizeof(*getTime)) == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
memset(getTime, 0, sizeof(*getTime));
|
|
memset(&eh_handle, 0, sizeof(eh_handle));
|
|
|
|
eh_handle.hndl = TPM_RH_ENDORSEMENT;
|
|
|
|
#ifdef WOLFBOOT_TPM_MFG_AUTH_DERIVE
|
|
/* Calculate EH auth value */
|
|
rc = wolfTPM2_SetIdentityAuth(&wolftpm_dev, &eh_handle,
|
|
Master_EH_AuthValue, (uint16_t)sizeof(Master_EH_AuthValue));
|
|
/* master secret consumed; clear it from the stack */
|
|
TPM2_ForceZero(Master_EH_AuthValue, sizeof(Master_EH_AuthValue));
|
|
#else
|
|
/* Set EH authValue directly */
|
|
rc = wolfBoot_tpm2_set_handle_auth(&eh_handle, eh_auth,
|
|
(uint16_t)sizeof(eh_auth));
|
|
#endif
|
|
if (rc == 0) {
|
|
/* Set EH auth */
|
|
wolfTPM2_SetAuthHandle(&wolftpm_dev, 0, &eh_handle);
|
|
|
|
/* set auth for using the AIK */
|
|
wolfTPM2_SetAuthHandle(&wolftpm_dev, 1, &aik->handle);
|
|
}
|
|
if (rc == 0) {
|
|
rc = wolfTPM2_GetTime(aik, getTime);
|
|
}
|
|
|
|
wolfTPM2_UnsetAuth(&wolftpm_dev, 1);
|
|
wolfTPM2_UnsetAuth(&wolftpm_dev, 0);
|
|
/* EH authValue consumed; clear it from the stack */
|
|
TPM2_ForceZero(&eh_handle, sizeof(eh_handle));
|
|
|
|
return rc;
|
|
}
|
|
|
|
int CSME_NSE_API wolfBoot_tpm2_parse_attest(const TPM2B_ATTEST* in, TPMS_ATTEST* out)
|
|
{
|
|
if (WOLFBOOT_TPM_NS_R(in, sizeof(*in)) == NULL ||
|
|
WOLFBOOT_TPM_NS_RW(out, sizeof(*out)) == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
return TPM2_ParseAttest(in, out);
|
|
}
|
|
|
|
int CSME_NSE_API wolfBoot_tpm2_quote(WOLFTPM2_KEY* aik,
|
|
byte* pcrArray, word32 pcrArraySz, Quote_Out* quoteResult)
|
|
{
|
|
int rc;
|
|
Quote_In quoteAsk;
|
|
TPMT_ASYM_SCHEME* scheme;
|
|
|
|
if (aik == NULL || pcrArray == NULL || pcrArraySz == 0 ||
|
|
quoteResult == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
if (WOLFBOOT_TPM_NS_RW(aik, sizeof(*aik)) == NULL ||
|
|
WOLFBOOT_TPM_NS_R(pcrArray, pcrArraySz) == NULL ||
|
|
WOLFBOOT_TPM_NS_RW(quoteResult, sizeof(*quoteResult)) == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
/* set auth for using the AIK */
|
|
wolfTPM2_SetAuthHandle(&wolftpm_dev, 0, &aik->handle);
|
|
|
|
/* Prepare Quote request */
|
|
XMEMSET("eAsk, 0, sizeof(quoteAsk));
|
|
XMEMSET(quoteResult, 0, sizeof(*quoteResult));
|
|
|
|
scheme = &aik->pub.publicArea.parameters.asymDetail.scheme;
|
|
quoteAsk.signHandle = aik->handle.hndl;
|
|
quoteAsk.inScheme.scheme = scheme->scheme;
|
|
quoteAsk.inScheme.details.any.hashAlg = scheme->details.anySig.hashAlg;
|
|
quoteAsk.qualifyingData.size = 0; /* optional */
|
|
/* Choose PCR(s) for signing */
|
|
TPM2_SetupPCRSelArray("eAsk.PCRselect, scheme->details.anySig.hashAlg,
|
|
pcrArray, pcrArraySz);
|
|
|
|
/* Get AIK signed attestation of PCR(s) */
|
|
rc = TPM2_Quote("eAsk, quoteResult);
|
|
|
|
wolfTPM2_UnsetAuth(&wolftpm_dev, 0);
|
|
|
|
return rc;
|
|
}
|
|
#endif /* WOLFTPM_MFG_IDENTITY */
|
|
|
|
|
|
/**
|
|
* @brief Initialize the TPM2 device and retrieve its capabilities.
|
|
*
|
|
* This function initializes the TPM2 device and retrieves its capabilities.
|
|
*
|
|
* @return 0 on success, an error code on failure.
|
|
*/
|
|
int wolfBoot_tpm2_init(void)
|
|
{
|
|
int rc;
|
|
WOLFTPM2_CAPS caps;
|
|
#if defined(WOLFBOOT_TPM_KEYSTORE) || defined(WOLFBOOT_TPM_SEAL)
|
|
TPM_ALG_ID alg;
|
|
#endif
|
|
#ifdef WOLFBOOT_MEASURED_BOOT
|
|
uint8_t digest[WOLFBOOT_SHA_DIGEST_SIZE];
|
|
#endif
|
|
|
|
#if !defined(ARCH_SIM) && !defined(WOLFTPM_MMIO)
|
|
spi_init(0,0);
|
|
#endif
|
|
|
|
#if defined(WOLFBOOT_TPM_KEYSTORE) || defined(WOLFBOOT_TPM_SEAL)
|
|
memset(&wolftpm_session, 0, sizeof(wolftpm_session));
|
|
memset(&wolftpm_srk, 0, sizeof(wolftpm_srk));
|
|
#endif
|
|
|
|
/* Init the TPM2 device */
|
|
/* simulator should use the network connection, not spi */
|
|
#if defined(ARCH_SIM) || defined(WOLFTPM_MMIO)
|
|
rc = wolfTPM2_Init(&wolftpm_dev, NULL, NULL);
|
|
#else
|
|
rc = wolfTPM2_Init(&wolftpm_dev, TPM2_IoCb, NULL);
|
|
#endif
|
|
if (rc == 0) {
|
|
/* Get device capabilities + options */
|
|
rc = wolfTPM2_GetCapabilities(&wolftpm_dev, &caps);
|
|
}
|
|
if (rc == 0) {
|
|
wolfBoot_printf("Mfg %s (%d), Vendor %s, Fw %u.%u (0x%x), "
|
|
"FIPS 140-2 %d, CC-EAL4 %d\n",
|
|
caps.mfgStr, caps.mfg, caps.vendorStr, caps.fwVerMajor,
|
|
caps.fwVerMinor, caps.fwVerVendor, caps.fips140_2, caps.cc_eal4);
|
|
}
|
|
|
|
|
|
if (rc != 0) {
|
|
wolfBoot_printf("TPM Init failed! %d\n", rc);
|
|
}
|
|
|
|
#if defined(WOLFBOOT_TPM_KEYSTORE) || defined(WOLFBOOT_TPM_SEAL)
|
|
if (rc == 0) {
|
|
#ifdef WC_RNG_SEED_CB
|
|
/* setup callback for RNG seed to use TPM */
|
|
wc_SetSeed_Cb(wolfRNG_GetSeedCB);
|
|
#endif
|
|
|
|
/* Create a primary storage key - no auth needed for param enc to work */
|
|
/* Prefer ECC as its faster */
|
|
#ifdef HAVE_ECC
|
|
alg = TPM_ALG_ECC;
|
|
#elif !defined(NO_RSA)
|
|
alg = TPM_ALG_RSA;
|
|
#else
|
|
alg = TPM_ALG_NULL;
|
|
#endif
|
|
rc = wolfTPM2_CreateSRK(&wolftpm_dev, &wolftpm_srk, alg, NULL, 0);
|
|
if (rc == 0) {
|
|
/* Setup a TPM session that can be used for parameter encryption */
|
|
rc = wolfTPM2_StartSession(&wolftpm_dev, &wolftpm_session,
|
|
&wolftpm_srk, NULL, TPM_SE_HMAC, TPM_ALG_CFB);
|
|
}
|
|
if (rc != 0) {
|
|
wolfBoot_printf("TPM Create SRK or Session error %d (%s)!\n",
|
|
rc, wolfTPM2_GetRCString(rc));
|
|
}
|
|
}
|
|
#endif /* WOLFBOOT_TPM_KEYSTORE | WOLFBOOT_TPM_SEAL */
|
|
|
|
#if defined(WOLFBOOT_MEASURED_BOOT) && defined(SELF_HASH_ADDR)
|
|
/* measured boot: hash wolfBoot code (or boot partition if
|
|
* WOLFBOOT_MEASURED_BOOT_APP_PARTITION) and extend PCR */
|
|
if (rc == 0) {
|
|
rc = self_hash(digest);
|
|
if (rc == 0) {
|
|
rc = measure_boot(digest);
|
|
}
|
|
if (rc != 0) {
|
|
wolfBoot_printf("Error %d performing wolfBoot measurement!\n", rc);
|
|
}
|
|
}
|
|
#endif /* WOLFBOOT_MEASURED_BOOT && SELF_HASH_ADDR */
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* @brief Deinitialize the TPM2 device.
|
|
*
|
|
* This function deinitializes the TPM2 device and cleans up any resources.
|
|
*
|
|
* @return None.
|
|
*/
|
|
void wolfBoot_tpm2_deinit(void)
|
|
{
|
|
#ifdef WOLFBOOT_TPM_KEYSTORE
|
|
#if !defined(ARCH_SIM) && !defined(WOLFBOOT_TPM_NO_CHG_PLAT_AUTH)
|
|
/* Enable parameter encryption for session */
|
|
int rc = wolfTPM2_SetAuthSession(&wolftpm_dev, 0, &wolftpm_session,
|
|
(TPMA_SESSION_decrypt | TPMA_SESSION_encrypt |
|
|
TPMA_SESSION_continueSession));
|
|
if (rc == 0) {
|
|
/* Change platform auth to random value, to prevent application
|
|
* from being able to use platform hierarchy. This is defined in
|
|
* section 10 of the TCG PC Client Platform specification. */
|
|
rc = wolfTPM2_ChangePlatformAuth(&wolftpm_dev, &wolftpm_session);
|
|
}
|
|
if (rc != 0) {
|
|
wolfBoot_printf("Error %d setting platform auth\n", rc);
|
|
}
|
|
#endif
|
|
wolfTPM2_UnloadHandle(&wolftpm_dev, &wolftpm_session.handle);
|
|
wolfTPM2_UnloadHandle(&wolftpm_dev, &wolftpm_srk.handle);
|
|
#endif /* WOLFBOOT_TPM_KEYSTORE */
|
|
|
|
wolfTPM2_Cleanup(&wolftpm_dev);
|
|
}
|
|
|
|
/**
|
|
* @brief Clear the content of the TPM2 device.
|
|
*
|
|
* This function clears the TPM2 device and remove all stored secrets.
|
|
*
|
|
* @return BAD_FUNC_ARG if any of the underlying functions has passed an invalid
|
|
* argument, e.g. wolftpm_dev is not initialized
|
|
* @return TPM_RC_SUCCESS in case of success
|
|
*/
|
|
int wolfBoot_tpm2_clear(void)
|
|
{
|
|
return wolfTPM2_Clear(&wolftpm_dev);
|
|
}
|
|
|
|
|
|
#ifdef WOLFBOOT_TPM_KEYSTORE
|
|
/* check root of trust based on key_slot (index) and public key hint */
|
|
int wolfBoot_check_rot(int key_slot, uint8_t* pubkey_hint)
|
|
{
|
|
int rc;
|
|
uint8_t digest[WOLFBOOT_SHA_DIGEST_SIZE];
|
|
uint32_t digestSz = WOLFBOOT_SHA_DIGEST_SIZE;
|
|
WOLFTPM2_NV nv;
|
|
|
|
memset(&nv, 0, sizeof(nv));
|
|
nv.handle.hndl = WOLFBOOT_TPM_KEYSTORE_NV_BASE + key_slot;
|
|
#ifdef WOLFBOOT_TPM_KEYSTORE_AUTH
|
|
size_t auth_sz = strlen(WOLFBOOT_TPM_KEYSTORE_AUTH);
|
|
if (auth_sz > (size_t)UINT16_MAX ||
|
|
auth_sz > sizeof(nv.handle.auth.buffer))
|
|
return BAD_FUNC_ARG;
|
|
nv.handle.auth.size = (UINT16)auth_sz;
|
|
memcpy(nv.handle.auth.buffer, WOLFBOOT_TPM_KEYSTORE_AUTH,
|
|
nv.handle.auth.size);
|
|
#endif
|
|
wolfTPM2_SetAuthHandle(&wolftpm_dev, 0, &nv.handle);
|
|
|
|
/* Enable parameter encryption for session - to protect auth */
|
|
rc = wolfTPM2_SetAuthSession(&wolftpm_dev, 1, &wolftpm_session,
|
|
(TPMA_SESSION_decrypt | TPMA_SESSION_encrypt |
|
|
TPMA_SESSION_continueSession));
|
|
if (rc == 0) {
|
|
wolfTPM2_UnsetAuth(&wolftpm_dev, 1);
|
|
|
|
/* read index */
|
|
rc = wolfTPM2_NVReadAuth(&wolftpm_dev, &nv, nv.handle.hndl,
|
|
digest, &digestSz, 0);
|
|
if (rc == 0) {
|
|
/* verify the hint (hash) matches */
|
|
if (digestSz == WOLFBOOT_SHA_DIGEST_SIZE &&
|
|
wolfBoot_constant_compare(digest, pubkey_hint,
|
|
WOLFBOOT_SHA_DIGEST_SIZE) == 0) {
|
|
wolfBoot_printf("TPM Root of Trust valid (id %d)\n", key_slot);
|
|
}
|
|
else {
|
|
rc = -1; /* digest match failure */
|
|
}
|
|
}
|
|
if (rc != 0) {
|
|
wolfBoot_printf("TPM Root of Trust failed! %d (%s)\n",
|
|
rc, wolfTPM2_GetRCString(rc));
|
|
wolfBoot_printf("Expected Hash %d\n", digestSz);
|
|
wolfBoot_print_hexstr(pubkey_hint, digestSz, 0);
|
|
}
|
|
}
|
|
wolfTPM2_UnsetAuthSession(&wolftpm_dev, 1, &wolftpm_session);
|
|
|
|
return rc;
|
|
}
|
|
#endif
|
|
/**
|
|
* @brief Perform TPM2 self test.
|
|
*
|
|
* This function performs the TPM2 self test.
|
|
*
|
|
* @return TPM_RC_SUCCESS in case of success
|
|
*
|
|
*/
|
|
int wolfBoot_tpm_self_test(void)
|
|
{
|
|
return wolfTPM2_SelfTest(&wolftpm_dev);
|
|
}
|
|
|
|
#endif /* WOLFBOOT_TPM */
|