File syn_tpm2.c¶
File List > crypto > syn_tpm2.c
Go to the documentation of this file
#if __has_include("syn_config.h")
#include "syn_config.h"
#endif
#if !defined(SYN_USE_TPM2) || SYN_USE_TPM2
#include "../port/syn_port_system.h"
#include "syn_tpm2.h"
#include <string.h>
/* ── Internal Big-Endian Marshalling Helpers ─────────────────────────────── */
static bool tpm2_write_u16(uint8_t *buf, size_t *pos, size_t max_len, uint16_t v)
{
if (*pos + 2U > max_len) {
return false;
}
buf[*pos] = (uint8_t)((v >> 8U) & 0xFFU);
buf[*pos + 1U] = (uint8_t)(v & 0xFFU);
*pos += 2U;
return true;
}
static bool tpm2_write_u32(uint8_t *buf, size_t *pos, size_t max_len, uint32_t v)
{
if (*pos + 4U > max_len) {
return false;
}
buf[*pos] = (uint8_t)((v >> 24U) & 0xFFU);
buf[*pos + 1U] = (uint8_t)((v >> 16U) & 0xFFU);
buf[*pos + 2U] = (uint8_t)((v >> 8U) & 0xFFU);
buf[*pos + 3U] = (uint8_t)(v & 0xFFU);
*pos += 4U;
return true;
}
static bool tpm2_write_bytes(uint8_t *buf, size_t *pos, size_t max_len, const uint8_t *data,
size_t len)
{
if (*pos + len > max_len) {
return false;
}
if (len > 0U && data != NULL) {
(void)memcpy(&buf[*pos], data, len);
}
*pos += len;
return true;
}
static bool tpm2_read_u16(const uint8_t *buf, size_t *pos, size_t max_len, uint16_t *out_v)
{
if (*pos + 2U > max_len) {
return false;
}
if (out_v != NULL) {
*out_v = ((uint16_t)buf[*pos] << 8U) | (uint16_t)buf[*pos + 1U];
}
*pos += 2U;
return true;
}
static bool tpm2_read_u32(const uint8_t *buf, size_t *pos, size_t max_len, uint32_t *out_v)
{
if (*pos + 4U > max_len) {
return false;
}
if (out_v != NULL) {
*out_v = ((uint32_t)buf[*pos] << 24U) | ((uint32_t)buf[*pos + 1U] << 16U) |
((uint32_t)buf[*pos + 2U] << 8U) | (uint32_t)buf[*pos + 3U];
}
*pos += 4U;
return true;
}
static bool tpm2_read_bytes(const uint8_t *buf, size_t *pos, size_t max_len, uint8_t *out_data,
size_t len)
{
if (*pos + len > max_len) {
return false;
}
if (len > 0U && out_data != NULL) {
(void)memcpy(out_data, &buf[*pos], len);
}
*pos += len;
return true;
}
static SYN_Status tpm2_execute_command(SYN_TPM2_Context *ctx, size_t cmd_len, size_t *out_resp_len)
{
/* Patch paramSize in command header (bytes 2..5) */
ctx->cfg.tx_buf[2] = (uint8_t)((cmd_len >> 24U) & 0xFFU);
ctx->cfg.tx_buf[3] = (uint8_t)((cmd_len >> 16U) & 0xFFU);
ctx->cfg.tx_buf[4] = (uint8_t)((cmd_len >> 8U) & 0xFFU);
ctx->cfg.tx_buf[5] = (uint8_t)(cmd_len & 0xFFU);
if (!syn_transport_send(ctx->cfg.transport, ctx->cfg.tx_buf, cmd_len)) {
return SYN_ERROR;
}
size_t rx_len = 0U;
if (!syn_transport_recv(ctx->cfg.transport, ctx->cfg.rx_buf, ctx->cfg.rx_buf_size, &rx_len) ||
rx_len < 10U) {
return SYN_ERROR;
}
size_t rpos = 0U;
uint16_t tag = 0U;
uint32_t param_size = 0U;
uint32_t rc = 0U;
(void)tpm2_read_u16(ctx->cfg.rx_buf, &rpos, rx_len, &tag);
(void)tpm2_read_u32(ctx->cfg.rx_buf, &rpos, rx_len, ¶m_size);
(void)tpm2_read_u32(ctx->cfg.rx_buf, &rpos, rx_len, &rc);
ctx->last_rc = rc;
if ((tag != SYN_TPM2_ST_NO_SESSIONS && tag != SYN_TPM2_ST_SESSIONS) ||
rc != SYN_TPM2_RC_SUCCESS || param_size > rx_len) {
return SYN_ERROR;
}
if (out_resp_len != NULL) {
*out_resp_len = rx_len;
}
return SYN_OK;
}
static bool tpm2_write_pw_auth_session(uint8_t *tx, size_t *pos, size_t max_len)
{
/* Auth area size = 9 bytes (Handle 4 + Nonce 2 + Attr 1 + Password 2) */
if (!tpm2_write_u32(tx, pos, max_len, 9U)) {
return false;
}
if (!tpm2_write_u32(tx, pos, max_len, SYN_TPM2_RS_PW)) {
return false;
}
if (!tpm2_write_u16(tx, pos, max_len, 0U)) { /* Empty nonce */
return false;
}
if (*pos + 1U > max_len) {
return false;
}
tx[(*pos)++] = 0U; /* Session attributes */
return tpm2_write_u16(tx, pos, max_len, 0U); /* Empty password */
}
/* ── Public API Implementation ───────────────────────────────────────────── */
SYN_Status syn_tpm2_init(SYN_TPM2_Context *ctx, const SYN_TPM2_Config *cfg)
{
if (ctx == NULL || cfg == NULL) {
return SYN_INVALID_PARAM;
}
if (cfg->transport == NULL || cfg->rx_buf == NULL || cfg->tx_buf == NULL ||
cfg->rx_buf_size < 256U || cfg->tx_buf_size < 256U) {
return SYN_INVALID_PARAM;
}
(void)memset(ctx, 0, sizeof(*ctx));
ctx->cfg = *cfg;
ctx->initialized = true;
return SYN_OK;
}
SYN_Status syn_tpm2_startup(SYN_TPM2_Context *ctx, uint16_t startup_type)
{
if (ctx == NULL || !ctx->initialized) {
return SYN_INVALID_PARAM;
}
uint8_t *tx = ctx->cfg.tx_buf;
size_t pos = 0U;
size_t max_len = ctx->cfg.tx_buf_size;
(void)tpm2_write_u16(tx, &pos, max_len, SYN_TPM2_ST_NO_SESSIONS);
(void)tpm2_write_u32(tx, &pos, max_len, 0U); /* Placeholder for paramSize */
(void)tpm2_write_u32(tx, &pos, max_len, SYN_TPM2_CC_STARTUP);
(void)tpm2_write_u16(tx, &pos, max_len, startup_type);
return tpm2_execute_command(ctx, pos, NULL);
}
SYN_Status syn_tpm2_self_test(SYN_TPM2_Context *ctx, bool full_test)
{
if (ctx == NULL || !ctx->initialized) {
return SYN_INVALID_PARAM;
}
uint8_t *tx = ctx->cfg.tx_buf;
size_t pos = 0U;
size_t max_len = ctx->cfg.tx_buf_size;
(void)tpm2_write_u16(tx, &pos, max_len, SYN_TPM2_ST_NO_SESSIONS);
(void)tpm2_write_u32(tx, &pos, max_len, 0U);
(void)tpm2_write_u32(tx, &pos, max_len, SYN_TPM2_CC_SELFTEST);
if (pos + 1U > max_len) {
return SYN_ERROR;
}
tx[pos++] = full_test ? 1U : 0U;
return tpm2_execute_command(ctx, pos, NULL);
}
SYN_Status syn_tpm2_get_random(SYN_TPM2_Context *ctx, uint16_t num_bytes, uint8_t *out_random,
uint16_t *out_len)
{
if (ctx == NULL || !ctx->initialized || out_random == NULL) {
return SYN_INVALID_PARAM;
}
uint8_t *tx = ctx->cfg.tx_buf;
size_t pos = 0U;
size_t max_len = ctx->cfg.tx_buf_size;
(void)tpm2_write_u16(tx, &pos, max_len, SYN_TPM2_ST_NO_SESSIONS);
(void)tpm2_write_u32(tx, &pos, max_len, 0U);
(void)tpm2_write_u32(tx, &pos, max_len, SYN_TPM2_CC_GETRANDOM);
(void)tpm2_write_u16(tx, &pos, max_len, num_bytes);
size_t resp_len = 0U;
if (tpm2_execute_command(ctx, pos, &resp_len) != SYN_OK) {
return SYN_ERROR;
}
size_t rpos = 10U; /* Skip Tag, Size, RC */
uint16_t rand_len = 0U;
if (!tpm2_read_u16(ctx->cfg.rx_buf, &rpos, resp_len, &rand_len) ||
!tpm2_read_bytes(ctx->cfg.rx_buf, &rpos, resp_len, out_random, rand_len)) {
return SYN_ERROR;
}
if (out_len != NULL) {
*out_len = rand_len;
}
return SYN_OK;
}
SYN_Status syn_tpm2_pcr_read(SYN_TPM2_Context *ctx, uint32_t pcr_index, uint16_t hash_alg,
uint8_t *out_digest, size_t *out_digest_len)
{
if (ctx == NULL || !ctx->initialized || out_digest == NULL || pcr_index > 23U) {
return SYN_INVALID_PARAM;
}
uint8_t *tx = ctx->cfg.tx_buf;
size_t pos = 0U;
size_t max_len = ctx->cfg.tx_buf_size;
(void)tpm2_write_u16(tx, &pos, max_len, SYN_TPM2_ST_NO_SESSIONS);
(void)tpm2_write_u32(tx, &pos, max_len, 0U);
(void)tpm2_write_u32(tx, &pos, max_len, SYN_TPM2_CC_PCR_READ);
/* TPML_PCR_SELECTION: count = 1 */
(void)tpm2_write_u32(tx, &pos, max_len, 1U);
(void)tpm2_write_u16(tx, &pos, max_len, hash_alg);
if (pos + 4U > max_len) {
return SYN_ERROR;
}
tx[pos++] = 3U; /* sizeofSelect = 3 bytes (24 PCRs) */
tx[pos++] = (uint8_t)((1U << (pcr_index & 7U)) * (pcr_index < 8U ? 1U : 0U));
tx[pos++] =
(uint8_t)((1U << (pcr_index & 7U)) * ((pcr_index >= 8U && pcr_index < 16U) ? 1U : 0U));
tx[pos++] = (uint8_t)((1U << (pcr_index & 7U)) * (pcr_index >= 16U ? 1U : 0U));
size_t resp_len = 0U;
if (tpm2_execute_command(ctx, pos, &resp_len) != SYN_OK) {
return SYN_ERROR;
}
size_t rpos = 10U;
uint32_t pcr_update_counter = 0U;
(void)tpm2_read_u32(ctx->cfg.rx_buf, &rpos, resp_len, &pcr_update_counter);
/* Skip selection echoes in response */
uint32_t sel_count = 0U;
(void)tpm2_read_u32(ctx->cfg.rx_buf, &rpos, resp_len, &sel_count);
for (uint32_t i = 0U; i < sel_count; i++) {
uint16_t alg = 0U;
(void)tpm2_read_u16(ctx->cfg.rx_buf, &rpos, resp_len, &alg);
if (rpos >= resp_len) {
return SYN_ERROR;
}
uint8_t sz = ctx->cfg.rx_buf[rpos++];
rpos += sz;
}
/* TPML_DIGEST: count, then TPM2B_DIGEST */
uint32_t digest_count = 0U;
(void)tpm2_read_u32(ctx->cfg.rx_buf, &rpos, resp_len, &digest_count);
if (digest_count == 0U) {
return SYN_ERROR;
}
uint16_t dlen = 0U;
if (!tpm2_read_u16(ctx->cfg.rx_buf, &rpos, resp_len, &dlen) ||
!tpm2_read_bytes(ctx->cfg.rx_buf, &rpos, resp_len, out_digest, dlen)) {
return SYN_ERROR;
}
if (out_digest_len != NULL) {
*out_digest_len = dlen;
}
return SYN_OK;
}
SYN_Status syn_tpm2_pcr_extend(SYN_TPM2_Context *ctx, uint32_t pcr_index, uint16_t hash_alg,
const uint8_t *in_digest, size_t digest_len)
{
if (ctx == NULL || !ctx->initialized || in_digest == NULL || pcr_index > 23U ||
digest_len == 0U) {
return SYN_INVALID_PARAM;
}
uint8_t *tx = ctx->cfg.tx_buf;
size_t pos = 0U;
size_t max_len = ctx->cfg.tx_buf_size;
(void)tpm2_write_u16(tx, &pos, max_len, SYN_TPM2_ST_SESSIONS);
(void)tpm2_write_u32(tx, &pos, max_len, 0U);
(void)tpm2_write_u32(tx, &pos, max_len, SYN_TPM2_CC_PCR_EXTEND);
(void)tpm2_write_u32(tx, &pos, max_len, pcr_index);
/* Session area */
if (!tpm2_write_pw_auth_session(tx, &pos, max_len)) {
return SYN_ERROR;
}
/* TPML_DIGEST_VALUES: count = 1, TPMT_HA (hashAlg, digest) */
if (!tpm2_write_u32(tx, &pos, max_len, 1U) || !tpm2_write_u16(tx, &pos, max_len, hash_alg) ||
!tpm2_write_bytes(tx, &pos, max_len, in_digest, digest_len)) {
return SYN_ERROR;
}
return tpm2_execute_command(ctx, pos, NULL);
}
SYN_Status syn_tpm2_quote(SYN_TPM2_Context *ctx, uint32_t key_handle,
const uint8_t *qualifying_data, size_t qual_len, uint32_t pcr_mask,
SYN_TPM2_QuoteResult *out_quote)
{
if (ctx == NULL || !ctx->initialized || out_quote == NULL ||
(qualifying_data == NULL && qual_len > 0U)) {
return SYN_INVALID_PARAM;
}
uint8_t *tx = ctx->cfg.tx_buf;
size_t pos = 0U;
size_t max_len = ctx->cfg.tx_buf_size;
(void)tpm2_write_u16(tx, &pos, max_len, SYN_TPM2_ST_SESSIONS);
(void)tpm2_write_u32(tx, &pos, max_len, 0U);
(void)tpm2_write_u32(tx, &pos, max_len, SYN_TPM2_CC_QUOTE);
(void)tpm2_write_u32(tx, &pos, max_len, key_handle);
/* Session Area */
if (!tpm2_write_pw_auth_session(tx, &pos, max_len)) {
return SYN_ERROR;
}
/* TPM2B_DATA qualifyingData */
if (!tpm2_write_u16(tx, &pos, max_len, (uint16_t)qual_len) ||
!tpm2_write_bytes(tx, &pos, max_len, qualifying_data, qual_len)) {
return SYN_ERROR;
}
/* TPMT_SIG_SCHEME (scheme = TPM_ALG_NULL) */
if (!tpm2_write_u16(tx, &pos, max_len, SYN_TPM2_ALG_NULL)) {
return SYN_ERROR;
}
/* TPML_PCR_SELECTION */
if (!tpm2_write_u32(tx, &pos, max_len, 1U) ||
!tpm2_write_u16(tx, &pos, max_len, SYN_TPM2_ALG_SHA256) || (pos + 4U > max_len)) {
return SYN_ERROR;
}
tx[pos++] = 3U;
tx[pos++] = (uint8_t)(pcr_mask & 0xFFU);
tx[pos++] = (uint8_t)((pcr_mask >> 8U) & 0xFFU);
tx[pos++] = (uint8_t)((pcr_mask >> 16U) & 0xFFU);
size_t resp_len = 0U;
if (tpm2_execute_command(ctx, pos, &resp_len) != SYN_OK) {
return SYN_ERROR;
}
size_t rpos = 10U;
/* Skip parameter size if present in sessions response */
uint32_t param_sz = 0U;
(void)tpm2_read_u32(ctx->cfg.rx_buf, &rpos, resp_len, ¶m_sz);
/* TPM2B_ATTEST quoted data */
uint16_t attest_len = 0U;
if (!tpm2_read_u16(ctx->cfg.rx_buf, &rpos, resp_len, &attest_len) ||
attest_len > SYN_TPM2_MAX_QUOTE_LEN ||
!tpm2_read_bytes(ctx->cfg.rx_buf, &rpos, resp_len, out_quote->attest_data, attest_len)) {
return SYN_ERROR;
}
out_quote->attest_len = attest_len;
/* TPMT_SIGNATURE signature */
uint16_t sig_alg = 0U;
(void)tpm2_read_u16(ctx->cfg.rx_buf, &rpos, resp_len, &sig_alg);
uint16_t sig_len = 0U;
if (!tpm2_read_u16(ctx->cfg.rx_buf, &rpos, resp_len, &sig_len) ||
sig_len > SYN_TPM2_MAX_QUOTE_LEN ||
!tpm2_read_bytes(ctx->cfg.rx_buf, &rpos, resp_len, out_quote->signature, sig_len)) {
return SYN_ERROR;
}
out_quote->signature_len = sig_len;
return SYN_OK;
}
SYN_Status syn_tpm2_nv_read(SYN_TPM2_Context *ctx, uint32_t auth_handle, uint32_t nv_index,
uint16_t offset, uint16_t size, uint8_t *out_data, uint16_t *out_len)
{
if (ctx == NULL || !ctx->initialized || out_data == NULL || size == 0U) {
return SYN_INVALID_PARAM;
}
uint8_t *tx = ctx->cfg.tx_buf;
size_t pos = 0U;
size_t max_len = ctx->cfg.tx_buf_size;
(void)tpm2_write_u16(tx, &pos, max_len, SYN_TPM2_ST_SESSIONS);
(void)tpm2_write_u32(tx, &pos, max_len, 0U);
(void)tpm2_write_u32(tx, &pos, max_len, SYN_TPM2_CC_NV_READ);
(void)tpm2_write_u32(tx, &pos, max_len, auth_handle);
(void)tpm2_write_u32(tx, &pos, max_len, nv_index);
if (!tpm2_write_pw_auth_session(tx, &pos, max_len)) {
return SYN_ERROR;
}
if (!tpm2_write_u16(tx, &pos, max_len, size) || !tpm2_write_u16(tx, &pos, max_len, offset)) {
return SYN_ERROR;
}
size_t resp_len = 0U;
if (tpm2_execute_command(ctx, pos, &resp_len) != SYN_OK) {
return SYN_ERROR;
}
size_t rpos = 10U;
uint32_t param_sz = 0U;
(void)tpm2_read_u32(ctx->cfg.rx_buf, &rpos, resp_len, ¶m_sz);
uint16_t read_len = 0U;
if (!tpm2_read_u16(ctx->cfg.rx_buf, &rpos, resp_len, &read_len) ||
!tpm2_read_bytes(ctx->cfg.rx_buf, &rpos, resp_len, out_data, read_len)) {
return SYN_ERROR;
}
if (out_len != NULL) {
*out_len = read_len;
}
return SYN_OK;
}
SYN_Status syn_tpm2_nv_write(SYN_TPM2_Context *ctx, uint32_t auth_handle, uint32_t nv_index,
uint16_t offset, const uint8_t *in_data, uint16_t size)
{
if (ctx == NULL || !ctx->initialized || in_data == NULL || size == 0U) {
return SYN_INVALID_PARAM;
}
uint8_t *tx = ctx->cfg.tx_buf;
size_t pos = 0U;
size_t max_len = ctx->cfg.tx_buf_size;
(void)tpm2_write_u16(tx, &pos, max_len, SYN_TPM2_ST_SESSIONS);
(void)tpm2_write_u32(tx, &pos, max_len, 0U);
(void)tpm2_write_u32(tx, &pos, max_len, SYN_TPM2_CC_NV_WRITE);
(void)tpm2_write_u32(tx, &pos, max_len, auth_handle);
(void)tpm2_write_u32(tx, &pos, max_len, nv_index);
if (!tpm2_write_pw_auth_session(tx, &pos, max_len)) {
return SYN_ERROR;
}
/* TPM2B_MAX_NV_BUFFER */
if (!tpm2_write_u16(tx, &pos, max_len, size) ||
!tpm2_write_bytes(tx, &pos, max_len, in_data, size) ||
!tpm2_write_u16(tx, &pos, max_len, offset)) {
return SYN_ERROR;
}
return tpm2_execute_command(ctx, pos, NULL);
}
uint32_t syn_tpm2_get_last_rc(const SYN_TPM2_Context *ctx)
{
return (ctx != NULL) ? ctx->last_rc : 0U;
}
#endif /* !defined(SYN_USE_TPM2) || SYN_USE_TPM2 */