mirror of
https://github.com/morgan9e/grd
synced 2026-04-14 00:14:18 +09:00
810 lines
23 KiB
C
810 lines
23 KiB
C
/*
|
|
* Copyright (C) 2022 Red Hat Inc.
|
|
*
|
|
* This program 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 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program 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., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "grd-tpm.h"
|
|
|
|
#include <gio/gio.h>
|
|
#include <stdio.h>
|
|
|
|
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
|
#include <tss2_esys.h>
|
|
#include <tss2_mu.h>
|
|
#include <tss2_rc.h>
|
|
#include <tss2_tctildr.h>
|
|
G_GNUC_END_IGNORE_DEPRECATIONS
|
|
|
|
#include "grd-debug.h"
|
|
|
|
struct _GrdTpm
|
|
{
|
|
GObject parent;
|
|
|
|
TSS2_TCTI_CONTEXT *tcti_context;
|
|
ESYS_CONTEXT *esys_context;
|
|
|
|
ESYS_TR hmac_session;
|
|
ESYS_TR trial_session;
|
|
ESYS_TR policy_session;
|
|
};
|
|
|
|
G_DEFINE_TYPE (GrdTpm, grd_tpm, G_TYPE_OBJECT)
|
|
|
|
static gboolean
|
|
check_tpm_existence (GError **error)
|
|
{
|
|
g_autoptr (GFile) tpm_dev = NULL;
|
|
g_autoptr (GFile) tpmrm_dev = NULL;
|
|
|
|
tpm_dev = g_file_new_for_path ("/dev/tpm0");
|
|
tpmrm_dev = g_file_new_for_path ("/dev/tpmrm0");
|
|
if (!g_file_query_exists (tpm_dev, NULL) ||
|
|
!g_file_query_exists (tpmrm_dev, NULL))
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
|
|
"No TPM device found");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
init_transmission_interface (GrdTpm *tpm,
|
|
GError **error)
|
|
{
|
|
TSS2_RC rc;
|
|
|
|
rc = Tss2_TctiLdr_Initialize (NULL, &tpm->tcti_context);
|
|
|
|
if (rc != TSS2_RC_SUCCESS)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to initialize transmission interface context: %s",
|
|
Tss2_RC_Decode (rc));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
init_tss2_esys (GrdTpm *tpm,
|
|
GError **error)
|
|
{
|
|
TSS2_RC rc;
|
|
|
|
rc = Esys_Initialize (&tpm->esys_context, tpm->tcti_context, NULL);
|
|
if (rc != TSS2_RC_SUCCESS)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to initialize ESYS context: %s",
|
|
Tss2_RC_Decode (rc));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
start_hmac_session (GrdTpm *tpm,
|
|
GError **error)
|
|
{
|
|
TPMT_SYM_DEF symmetric = {
|
|
.algorithm = TPM2_ALG_NULL,
|
|
};
|
|
TSS2_RC rc;
|
|
|
|
rc = Esys_StartAuthSession (tpm->esys_context,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
NULL,
|
|
TPM2_SE_HMAC,
|
|
&symmetric,
|
|
TPM2_ALG_SHA256,
|
|
&tpm->hmac_session);
|
|
if (rc != TSS2_RC_SUCCESS)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to start auth session: %s (%d)",
|
|
Tss2_RC_Decode (rc), rc);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
start_trial_session (GrdTpm *tpm,
|
|
GError **error)
|
|
{
|
|
TPMT_SYM_DEF symmetric = {
|
|
.algorithm = TPM2_ALG_NULL,
|
|
};
|
|
TSS2_RC rc;
|
|
|
|
rc = Esys_StartAuthSession (tpm->esys_context,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
NULL,
|
|
TPM2_SE_TRIAL,
|
|
&symmetric,
|
|
TPM2_ALG_SHA256,
|
|
&tpm->trial_session);
|
|
if (rc != TSS2_RC_SUCCESS)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to start policy session: %s (%d)",
|
|
Tss2_RC_Decode (rc), rc);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
start_policy_session (GrdTpm *tpm,
|
|
GError **error)
|
|
{
|
|
TPMT_SYM_DEF symmetric = {
|
|
.algorithm = TPM2_ALG_NULL,
|
|
};
|
|
TSS2_RC rc;
|
|
|
|
rc = Esys_StartAuthSession (tpm->esys_context,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
NULL,
|
|
TPM2_SE_POLICY,
|
|
&symmetric,
|
|
TPM2_ALG_SHA256,
|
|
&tpm->policy_session);
|
|
if (rc != TSS2_RC_SUCCESS)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to start policy session: %s (%d)",
|
|
Tss2_RC_Decode (rc), rc);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
grd_tpm_create_primary (GrdTpm *tpm,
|
|
ESYS_TR *primary_handle_out,
|
|
GError **error)
|
|
{
|
|
TPM2B_SENSITIVE_CREATE in_sensitive_primary = {};
|
|
TPM2B_PUBLIC in_public = {
|
|
.publicArea = {
|
|
.type = TPM2_ALG_RSA,
|
|
.nameAlg = TPM2_ALG_SHA256,
|
|
.objectAttributes = (TPMA_OBJECT_RESTRICTED |
|
|
TPMA_OBJECT_DECRYPT |
|
|
TPMA_OBJECT_FIXEDTPM |
|
|
TPMA_OBJECT_FIXEDPARENT |
|
|
TPMA_OBJECT_SENSITIVEDATAORIGIN |
|
|
TPMA_OBJECT_USERWITHAUTH),
|
|
},
|
|
};
|
|
TPM2B_DATA outside_info = {};
|
|
TPML_PCR_SELECTION creation_pcr = {};
|
|
TSS2_RC rc;
|
|
|
|
g_assert (tpm->hmac_session);
|
|
|
|
in_public.publicArea.parameters.rsaDetail.keyBits = 2048;
|
|
in_public.publicArea.parameters.asymDetail.scheme.scheme = TPM2_ALG_NULL;
|
|
in_public.publicArea.parameters.symDetail.sym.algorithm = TPM2_ALG_AES;
|
|
in_public.publicArea.parameters.symDetail.sym.keyBits.sym = 128;
|
|
in_public.publicArea.parameters.symDetail.sym.mode.sym = TPM2_ALG_CFB;
|
|
|
|
rc = Esys_CreatePrimary (tpm->esys_context,
|
|
ESYS_TR_RH_OWNER,
|
|
tpm->hmac_session,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
&in_sensitive_primary,
|
|
&in_public,
|
|
&outside_info,
|
|
&creation_pcr,
|
|
primary_handle_out,
|
|
NULL, NULL, NULL, NULL);
|
|
if (rc != TSS2_RC_SUCCESS)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Esys_CreatePrimary failed: %s",
|
|
Tss2_RC_Decode (rc));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
grd_tpm_read_pcr (GrdTpm *tpm,
|
|
TPML_PCR_SELECTION **pcr_selection_out,
|
|
TPML_DIGEST **pcr_digest_out,
|
|
GError **error)
|
|
{
|
|
TPML_PCR_SELECTION pcr_selection_in = {
|
|
.count = 1,
|
|
.pcrSelections = {
|
|
{
|
|
.hash = TPM2_ALG_SHA256,
|
|
.sizeofSelect = 3,
|
|
.pcrSelect = { 1 << 0 | 1 << 1 | 1 << 2 | 1 << 3, },
|
|
},
|
|
}
|
|
};
|
|
TSS2_RC rc;
|
|
|
|
rc = Esys_PCR_Read (tpm->esys_context,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
&pcr_selection_in,
|
|
NULL,
|
|
pcr_selection_out,
|
|
pcr_digest_out);
|
|
if (rc != TSS2_RC_SUCCESS)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Esys_PCR_Read failed: %s",
|
|
Tss2_RC_Decode (rc));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
do_create_policy (GrdTpm *tpm,
|
|
ESYS_TR session,
|
|
TPML_PCR_SELECTION *pcr_selection,
|
|
TPML_DIGEST *pcr_digest,
|
|
TPM2B_DIGEST **policy_digest_out,
|
|
GError **error)
|
|
{
|
|
g_autoptr (GChecksum) checksum = NULL;
|
|
TPM2B_DIGEST pcr_hash_digest = {};
|
|
size_t size;
|
|
int i;
|
|
TSS2_RC rc;
|
|
|
|
checksum = g_checksum_new (G_CHECKSUM_SHA256);
|
|
for (i = 0; i < pcr_digest->count; i++)
|
|
{
|
|
g_checksum_update (checksum,
|
|
pcr_digest->digests[i].buffer,
|
|
pcr_digest->digests[i].size);
|
|
}
|
|
|
|
size = G_N_ELEMENTS (pcr_hash_digest.buffer);
|
|
g_checksum_get_digest (checksum,
|
|
(uint8_t *) &pcr_hash_digest.buffer,
|
|
&size);
|
|
pcr_hash_digest.size = size;
|
|
|
|
rc = Esys_PolicyPCR (tpm->esys_context, session,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
&pcr_hash_digest,
|
|
pcr_selection);
|
|
if (rc != TSS2_RC_SUCCESS)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Esys_PolicyPCR failed: %s",
|
|
Tss2_RC_Decode (rc));
|
|
return FALSE;
|
|
}
|
|
|
|
rc = Esys_PolicyGetDigest (tpm->esys_context, session,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
policy_digest_out);
|
|
if (rc != TSS2_RC_SUCCESS)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Esys_PolicyGetDigest failed: %s",
|
|
Tss2_RC_Decode (rc));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
grd_tpm_store_secret (GrdTpm *tpm,
|
|
const char *secret,
|
|
ESYS_TR primary_handle,
|
|
TPML_PCR_SELECTION *pcr_selection,
|
|
TPML_DIGEST *pcr_digest,
|
|
TPMS_CONTEXT **secret_tpms_context_out,
|
|
GError **error)
|
|
{
|
|
size_t secret_size;
|
|
size_t offset = 0;
|
|
g_autofree TPM2B_DIGEST *policy_digest = NULL;
|
|
TPM2B_TEMPLATE template = {};
|
|
TPM2B_PUBLIC template_public;
|
|
TPM2B_SENSITIVE_CREATE in_sensitive;
|
|
ESYS_TR secret_handle;
|
|
TSS2_RC rc;
|
|
|
|
g_assert (tpm->trial_session);
|
|
|
|
secret_size = strlen (secret) + 1;
|
|
if (secret_size > G_N_ELEMENTS (in_sensitive.sensitive.data.buffer))
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Secret too large (%lu exceeds %lu max)",
|
|
secret_size,
|
|
G_N_ELEMENTS (in_sensitive.sensitive.data.buffer));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!do_create_policy (tpm, tpm->trial_session,
|
|
pcr_selection,
|
|
pcr_digest,
|
|
&policy_digest,
|
|
error))
|
|
return FALSE;
|
|
|
|
template_public = (TPM2B_PUBLIC) {
|
|
.publicArea = {
|
|
.type = TPM2_ALG_KEYEDHASH,
|
|
.nameAlg = TPM2_ALG_SHA256,
|
|
.objectAttributes = TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT,
|
|
.parameters.keyedHashDetail.scheme.scheme = TPM2_ALG_NULL,
|
|
},
|
|
};
|
|
memcpy (template_public.publicArea.authPolicy.buffer,
|
|
policy_digest->buffer,
|
|
policy_digest->size);
|
|
template_public.publicArea.authPolicy.size = policy_digest->size;
|
|
|
|
rc = Tss2_MU_TPMT_PUBLIC_Marshal (&template_public.publicArea,
|
|
template.buffer,
|
|
G_N_ELEMENTS (template.buffer),
|
|
&offset);
|
|
if (rc != TSS2_RC_SUCCESS)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to marshal public template: %s",
|
|
Tss2_RC_Decode (rc));
|
|
return FALSE;
|
|
}
|
|
template.size = offset;
|
|
|
|
in_sensitive = (TPM2B_SENSITIVE_CREATE) {
|
|
.sensitive.data.size = secret_size,
|
|
};
|
|
memcpy (in_sensitive.sensitive.data.buffer, secret, secret_size);
|
|
|
|
rc = Esys_CreateLoaded (tpm->esys_context,
|
|
primary_handle,
|
|
tpm->hmac_session,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
&in_sensitive,
|
|
&template,
|
|
&secret_handle,
|
|
NULL, NULL);
|
|
if (rc != TSS2_RC_SUCCESS)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Esys_CreateLoaded failed: %s",
|
|
Tss2_RC_Decode (rc));
|
|
return FALSE;
|
|
}
|
|
|
|
rc = Esys_ContextSave (tpm->esys_context,
|
|
secret_handle,
|
|
secret_tpms_context_out);
|
|
if (rc != TSS2_RC_SUCCESS)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Esys_ContextSave failed: %s",
|
|
Tss2_RC_Decode (rc));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GVariant *
|
|
grd_tpms_context_to_variant (TPMS_CONTEXT *tpms_context)
|
|
{
|
|
g_autofree char *base64 = NULL;
|
|
GVariant *variant;
|
|
|
|
base64 = g_base64_encode (tpms_context->contextBlob.buffer,
|
|
tpms_context->contextBlob.size);
|
|
|
|
variant = g_variant_new ("(uutqs)",
|
|
tpms_context->hierarchy,
|
|
tpms_context->savedHandle,
|
|
tpms_context->sequence,
|
|
tpms_context->contextBlob.size,
|
|
base64);
|
|
|
|
return variant;
|
|
}
|
|
|
|
static gboolean
|
|
decode_variant (GVariant *variant,
|
|
TPMS_CONTEXT *tpms_context,
|
|
GError **error)
|
|
{
|
|
g_autofree char *base64 = NULL;
|
|
g_autofree uint8_t *decoded = NULL;
|
|
size_t size;
|
|
|
|
if (!g_variant_is_of_type (variant, G_VARIANT_TYPE ("(uutqs)")))
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Invalid tpms context variant type");
|
|
return FALSE;
|
|
}
|
|
g_variant_get (variant, "(uutqs)",
|
|
&tpms_context->hierarchy,
|
|
&tpms_context->savedHandle,
|
|
&tpms_context->sequence,
|
|
&tpms_context->contextBlob.size,
|
|
&base64);
|
|
|
|
decoded = g_base64_decode (base64, &size);
|
|
if (size != tpms_context->contextBlob.size)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Invalid tpms context blob length");
|
|
return FALSE;
|
|
}
|
|
|
|
memcpy (tpms_context->contextBlob.buffer, decoded, size);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
char *
|
|
grd_tpm_restore_secret (GrdTpm *tpm,
|
|
GVariant *variant,
|
|
TPML_PCR_SELECTION *pcr_selection,
|
|
TPML_DIGEST *pcr_digest,
|
|
GError **error)
|
|
{
|
|
TSS2_RC rc;
|
|
TPMS_CONTEXT secret_tpms_context;
|
|
ESYS_TR loaded_secret_handle;
|
|
TPM2B_SENSITIVE_DATA *unsealed_data;
|
|
|
|
g_assert (tpm->policy_session);
|
|
|
|
if (!decode_variant (variant, &secret_tpms_context, error))
|
|
return NULL;
|
|
|
|
rc = Esys_ContextLoad (tpm->esys_context, &secret_tpms_context,
|
|
&loaded_secret_handle);
|
|
if (rc != TSS2_RC_SUCCESS)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Esys_ContextLoad failed: %s",
|
|
Tss2_RC_Decode (rc));
|
|
return NULL;
|
|
}
|
|
|
|
if (!do_create_policy (tpm, tpm->policy_session,
|
|
pcr_selection,
|
|
pcr_digest,
|
|
NULL,
|
|
error))
|
|
return NULL;
|
|
|
|
rc = Esys_Unseal (tpm->esys_context,
|
|
loaded_secret_handle,
|
|
tpm->policy_session,
|
|
ESYS_TR_NONE,
|
|
ESYS_TR_NONE,
|
|
&unsealed_data);
|
|
if (rc != TSS2_RC_SUCCESS)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Esys_Unseal failed: %s",
|
|
Tss2_RC_Decode (rc));
|
|
return NULL;
|
|
}
|
|
|
|
if (strnlen ((char *) unsealed_data->buffer, unsealed_data->size) !=
|
|
unsealed_data->size - 1)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Secret not a zero terminated string");
|
|
return NULL;
|
|
}
|
|
|
|
return g_strdup ((char *) unsealed_data->buffer);
|
|
}
|
|
|
|
static const char *
|
|
get_algorithm_name (uint32_t algorithm)
|
|
{
|
|
switch (algorithm)
|
|
{
|
|
case TPM2_ALG_SHA256:
|
|
return "SHA-256";
|
|
case TPM2_ALG_RSA:
|
|
return "RSA";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
static const char *
|
|
get_command_name (uint32_t command)
|
|
{
|
|
switch (command)
|
|
{
|
|
case TPM2_CC_CreatePrimary:
|
|
return "CreatePrimary";
|
|
case TPM2_CC_StartAuthSession:
|
|
return "StartAuthSession";
|
|
case TPM2_CC_FlushContext:
|
|
return "FlushContext";
|
|
case TPM2_CC_PCR_Read:
|
|
return "PCR_Read";
|
|
case TPM2_CC_PolicyPCR:
|
|
return "PolicyPCR";
|
|
case TPM2_CC_PolicyGetDigest:
|
|
return "PolicyGetDigest";
|
|
case TPM2_CC_CreateLoaded:
|
|
return "CreateLoaded";
|
|
case TPM2_CC_ContextSave:
|
|
return "ContextSave";
|
|
case TPM2_CC_ContextLoad:
|
|
return "ContextLoad";
|
|
case TPM2_CC_Unseal:
|
|
return "Unseal";
|
|
default:
|
|
return "unkown";
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
grd_tpm_check_capabilities (GrdTpm *tpm,
|
|
GError **error)
|
|
{
|
|
const struct
|
|
{
|
|
TPM2_CAP capability;
|
|
uint32_t property;
|
|
int count;
|
|
} capabilities[] = {
|
|
{ .capability = TPM2_CAP_ALGS, .property = TPM2_ALG_SHA256 },
|
|
{ .capability = TPM2_CAP_ALGS, .property = TPM2_ALG_RSA },
|
|
{ .capability = TPM2_CAP_COMMANDS, .property = TPM2_CC_CreatePrimary },
|
|
{ .capability = TPM2_CAP_COMMANDS, .property = TPM2_CC_StartAuthSession },
|
|
{ .capability = TPM2_CAP_COMMANDS, .property = TPM2_CC_FlushContext },
|
|
{ .capability = TPM2_CAP_COMMANDS, .property = TPM2_CC_PCR_Read },
|
|
{ .capability = TPM2_CAP_COMMANDS, .property = TPM2_CC_PolicyPCR },
|
|
{ .capability = TPM2_CAP_COMMANDS, .property = TPM2_CC_PolicyGetDigest },
|
|
{ .capability = TPM2_CAP_COMMANDS, .property = TPM2_CC_CreateLoaded },
|
|
{ .capability = TPM2_CAP_COMMANDS, .property = TPM2_CC_ContextSave },
|
|
{ .capability = TPM2_CAP_COMMANDS, .property = TPM2_CC_ContextLoad },
|
|
};
|
|
gboolean sha256_pcrs_found = FALSE;
|
|
uint32_t property = 0;
|
|
uint32_t count = TPM2_MAX_TPM_PROPERTIES;
|
|
int i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (capabilities); i++)
|
|
{
|
|
g_autofree TPMS_CAPABILITY_DATA *data = NULL;
|
|
TSS2_RC rc;
|
|
|
|
rc = Esys_GetCapability (tpm->esys_context,
|
|
ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
|
|
capabilities[i].capability,
|
|
capabilities[i].property,
|
|
1,
|
|
NULL,
|
|
&data);
|
|
if (rc != TSS2_RC_SUCCESS)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to get capability: %s",
|
|
Tss2_RC_Decode (rc));
|
|
return FALSE;
|
|
}
|
|
|
|
switch (capabilities[i].capability)
|
|
{
|
|
case TPM2_CAP_ALGS:
|
|
g_warn_if_fail (data->data.algorithms.count == 1);
|
|
|
|
if (capabilities[i].property !=
|
|
data->data.algorithms.algProperties[0].alg)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
"Algorithm %s not supported",
|
|
get_algorithm_name (capabilities[i].property));
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
case TPM2_CAP_COMMANDS:
|
|
g_warn_if_fail (data->data.command.count == 1);
|
|
|
|
if ((data->data.command.commandAttributes[0] &
|
|
TPMA_CC_COMMANDINDEX_MASK) !=
|
|
capabilities[i].property)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
"Command %s not supported",
|
|
get_command_name (capabilities[i].property));
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
case TPM2_CAP_PCRS:
|
|
g_warn_if_fail (data->data.assignedPCR.count == 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (TRUE)
|
|
{
|
|
TPMI_YES_NO more_data = TPM2_NO;
|
|
g_autofree TPMS_CAPABILITY_DATA *data = NULL;
|
|
TSS2_RC rc;
|
|
|
|
rc = Esys_GetCapability (tpm->esys_context,
|
|
ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
|
|
TPM2_CAP_PCRS,
|
|
property,
|
|
count,
|
|
&more_data,
|
|
&data);
|
|
if (rc != TSS2_RC_SUCCESS)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
"Failed to get capability: %s",
|
|
Tss2_RC_Decode (rc));
|
|
return FALSE;
|
|
}
|
|
|
|
property += data->data.assignedPCR.count;
|
|
count -= data->data.assignedPCR.count;
|
|
|
|
for (i = 0; i < data->data.assignedPCR.count; i++)
|
|
{
|
|
if (data->data.assignedPCR.pcrSelections[i].hash != TPM2_ALG_SHA256)
|
|
continue;
|
|
|
|
if (data->data.assignedPCR.pcrSelections[i].pcrSelect[0] &
|
|
(1 << 0 | 1 << 1 | 1 << 2))
|
|
{
|
|
sha256_pcrs_found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (more_data == TPM2_NO)
|
|
break;
|
|
}
|
|
|
|
if (!sha256_pcrs_found)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
"SHA-256 PCRs not available");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GrdTpm *
|
|
grd_tpm_new (GrdTpmMode mode,
|
|
GError **error)
|
|
{
|
|
g_autoptr (GrdTpm) tpm = NULL;
|
|
|
|
tpm = g_object_new (GRD_TYPE_TPM, NULL);
|
|
|
|
if (!(grd_get_debug_flags () & GRD_DEBUG_TPM))
|
|
g_setenv ("TSS2_LOGFILE", "/dev/null", TRUE);
|
|
|
|
if (!check_tpm_existence (error))
|
|
return NULL;
|
|
|
|
if (!init_transmission_interface (tpm, error))
|
|
return NULL;
|
|
|
|
if (!init_tss2_esys (tpm, error))
|
|
return NULL;
|
|
|
|
switch (mode)
|
|
{
|
|
case GRD_TPM_MODE_NONE:
|
|
break;
|
|
case GRD_TPM_MODE_WRITE:
|
|
if (!start_hmac_session (tpm, error))
|
|
return NULL;
|
|
if (!start_trial_session (tpm, error))
|
|
return NULL;
|
|
break;
|
|
case GRD_TPM_MODE_READ:
|
|
if (!start_hmac_session (tpm, error))
|
|
return NULL;
|
|
if (!start_policy_session (tpm, error))
|
|
return NULL;
|
|
break;
|
|
}
|
|
|
|
return g_steal_pointer (&tpm);
|
|
}
|
|
|
|
static void
|
|
grd_tpm_finalize (GObject *object)
|
|
{
|
|
GrdTpm *tpm = GRD_TPM (object);
|
|
|
|
if (tpm->policy_session)
|
|
Esys_FlushContext (tpm->esys_context, tpm->policy_session);
|
|
if (tpm->trial_session)
|
|
Esys_FlushContext (tpm->esys_context, tpm->trial_session);
|
|
if (tpm->hmac_session)
|
|
Esys_FlushContext (tpm->esys_context, tpm->hmac_session);
|
|
if (tpm->esys_context)
|
|
Esys_Finalize (&tpm->esys_context);
|
|
if (tpm->tcti_context)
|
|
Tss2_TctiLdr_Finalize (&tpm->tcti_context);
|
|
|
|
G_OBJECT_CLASS (grd_tpm_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
grd_tpm_init (GrdTpm *tpm)
|
|
{
|
|
}
|
|
|
|
static void
|
|
grd_tpm_class_init (GrdTpmClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = grd_tpm_finalize;
|
|
}
|