libsecret/egg/egg-tpm2.c
2023-08-24 13:31:15 +00:00

486 lines
11 KiB
C

/* libsecret - TSS interface implementations for libsecret
*
* Copyright (C) 2021 Dhanuka Warusadura
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* Author: Dhanuka Warusadura
*/
#include "config.h"
#include "egg-tpm2.h"
#include <tss2/tss2_esys.h>
#include <tss2/tss2_mu.h>
#include <tss2/tss2_rc.h>
#include <tss2/tss2_tctildr.h>
#define MAX_BYTE_SIZE 64
struct EggTpm2Context {
TSS2_TCTI_CONTEXT *tcti_context;
ESYS_CONTEXT *esys_context;
ESYS_TR primary_key;
};
static gboolean
egg_tpm2_generate_primary_key(EggTpm2Context *context,
GError **error)
{
TSS2_RC ret;
TPM2B_SENSITIVE_CREATE sensitive_params = {
.size = 0,
.sensitive = {
.userAuth = {
.size = 0,
.buffer = {0},
},
.data = {
.size = 0,
.buffer = {0},
},
},
};
TPM2B_PUBLIC public_key_param = {
.size = 0,
.publicArea = {
.type = TPM2_ALG_RSA,
.nameAlg = TPM2_ALG_SHA256,
.objectAttributes = (
TPMA_OBJECT_USERWITHAUTH |
TPMA_OBJECT_RESTRICTED |
TPMA_OBJECT_DECRYPT |
TPMA_OBJECT_FIXEDTPM |
TPMA_OBJECT_FIXEDPARENT |
TPMA_OBJECT_SENSITIVEDATAORIGIN),
.authPolicy = {
.size = 0,
},
.parameters.rsaDetail = {
.symmetric = {
.algorithm = TPM2_ALG_AES,
.keyBits.aes = 128,
.mode.aes = TPM2_ALG_CFB
},
.scheme = {
.scheme = TPM2_ALG_NULL
},
.keyBits = 2048,
.exponent = 0,
},
.unique.rsa = {
.size = 0,
.buffer = {},
},
},
};
TPM2B_DATA outside_info = {
.size = 0,
.buffer = {},
};
TPML_PCR_SELECTION pcrs = {
.count = 0,
};
TPM2B_PUBLIC *public;
TPM2B_CREATION_DATA *creation_data;
TPM2B_DIGEST *hash;
TPMT_TK_CREATION *ticket;
ret = Esys_CreatePrimary(context->esys_context, ESYS_TR_RH_OWNER,
ESYS_TR_PASSWORD, ESYS_TR_NONE,
ESYS_TR_NONE, &sensitive_params,
&public_key_param, &outside_info,
&pcrs, &context->primary_key, &public,
&creation_data, &hash, &ticket);
if (ret != TSS2_RC_SUCCESS) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Esys_CreatePrimary: %s", Tss2_RC_Decode(ret));
return FALSE;
}
Esys_Free(public);
Esys_Free(creation_data);
Esys_Free(hash);
Esys_Free(ticket);
return TRUE;
}
static GBytes *
egg_tpm2_generate_random_data(EggTpm2Context *context,
GError **error)
{
TSS2_RC ret;
TPM2B_DIGEST *random_data;
GBytes *bytes;
ret = Esys_GetRandom(context->esys_context, ESYS_TR_NONE,
ESYS_TR_NONE, ESYS_TR_NONE, MAX_BYTE_SIZE,
&random_data);
if (ret != TSS2_RC_SUCCESS) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Esys_GetRandom: %s", Tss2_RC_Decode(ret));
return NULL;
}
bytes = g_bytes_new(random_data->buffer, random_data->size);
Esys_Free(random_data);
return bytes;
}
EggTpm2Context *
egg_tpm2_initialize(GError **error)
{
TSS2_RC ret;
EggTpm2Context *context;
gsize n_context;
const gchar *tcti_conf;
gboolean status;
n_context = 1;
context = g_new(EggTpm2Context, n_context);
tcti_conf = g_getenv("TCTI");
ret = Tss2_TctiLdr_Initialize(tcti_conf, &context->tcti_context);
if (ret != TSS2_RC_SUCCESS) {
egg_tpm2_finalize(context);
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Tss2_TctiLdr_Initialize: %s",
Tss2_RC_Decode(ret));
return NULL;
}
ret = Esys_Initialize(&context->esys_context,
context->tcti_context, NULL);
if (ret != TSS2_RC_SUCCESS) {
egg_tpm2_finalize(context);
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Esys_Initialize: %s", Tss2_RC_Decode(ret));
return NULL;
}
ret = Esys_Startup(context->esys_context, TPM2_SU_CLEAR);
if (ret != TSS2_RC_SUCCESS) {
egg_tpm2_finalize(context);
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Esys_Startup: %s", Tss2_RC_Decode(ret));
return NULL;
}
status = egg_tpm2_generate_primary_key(context, error);
if (!status) {
egg_tpm2_finalize(context);
return NULL;
}
return context;
}
void
egg_tpm2_finalize(EggTpm2Context *context)
{
if (context->esys_context)
Esys_Finalize(&context->esys_context);
if (context->tcti_context)
Tss2_TctiLdr_Finalize(&context->tcti_context);
g_free(context);
}
GBytes *
egg_tpm2_generate_master_password(EggTpm2Context *context,
GError **error)
{
TSS2_RC ret;
TPM2B_PRIVATE *out_private;
TPM2B_PUBLIC *out_public;
TPM2B_CREATION_DATA *creation_data;
TPM2B_DIGEST *hash;
TPMT_TK_CREATION *ticket;
gconstpointer data;
gsize size;
GBytes *input;
GBytes *output;
TPM2B_SENSITIVE_CREATE in_sensitive = {
.size = 0,
.sensitive = {
.data = {
.size = MAX_BYTE_SIZE
}
}
};
TPM2B_PUBLIC in_public = {
.size = 0,
.publicArea = {
.type = TPM2_ALG_KEYEDHASH,
.nameAlg = TPM2_ALG_SHA256,
.objectAttributes = (
TPMA_OBJECT_USERWITHAUTH |
TPMA_OBJECT_FIXEDTPM |
TPMA_OBJECT_FIXEDPARENT),
.authPolicy = {
.size = 0,
},
.parameters.keyedHashDetail = {
.scheme = {
.scheme = TPM2_ALG_NULL,
.details = {
.hmac = {
.hashAlg =
TPM2_ALG_SHA256
}
}
}
},
.unique.keyedHash = {
.size = 0,
.buffer = {},
},
}
};
TPM2B_DATA outside_info = {
.size = 0,
.buffer = {}
};
TPML_PCR_SELECTION pcrs = {
.count = 0
};
input = egg_tpm2_generate_random_data(context, error);
if (!input) {
g_bytes_unref(input);
return NULL;
}
data = g_bytes_get_data(input, &size);
if (size > sizeof(in_sensitive.sensitive.data.buffer)) {
g_set_error_literal(error,
G_IO_ERROR,
G_IO_ERROR_INVALID_ARGUMENT,
"Input is too long");
return NULL;
}
memcpy(in_sensitive.sensitive.data.buffer, data, size);
in_sensitive.sensitive.data.size = size;
g_bytes_unref(input);
ret = Esys_Create(context->esys_context, context->primary_key,
ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
&in_sensitive, &in_public, &outside_info,
&pcrs, &out_private, &out_public, &creation_data,
&hash, &ticket);
if (ret != TSS2_RC_SUCCESS) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Esys_Create: %s", Tss2_RC_Decode(ret));
return NULL;
}
gsize out_private_offset = 0;
gsize out_public_offset = 0;
GVariant *out_private_variant;
GVariant *out_public_variant;
GVariant *variant;
guint8 marshaled_out_private[sizeof(*out_private)];
guint8 marshaled_out_public[sizeof(*out_public)];
ret = Tss2_MU_TPM2B_PRIVATE_Marshal(out_private,
marshaled_out_private,
sizeof(marshaled_out_private),
&out_private_offset);
if (ret != TSS2_RC_SUCCESS) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Tss2_MU_TPM2B_PRIVATE_Marshal: %s",
Tss2_RC_Decode(ret));
return NULL;
}
out_private_variant = g_variant_new_fixed_array(
G_VARIANT_TYPE_BYTE,
marshaled_out_private,
out_private_offset,
sizeof(guint8));
ret = Tss2_MU_TPM2B_PUBLIC_Marshal(out_public,
marshaled_out_public,
sizeof(marshaled_out_public),
&out_public_offset);
if (ret != TSS2_RC_SUCCESS) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Tss2_MU_TPM2B_PUBLIC_Marshal: %s",
Tss2_RC_Decode(ret));
return NULL;
}
out_public_variant = g_variant_new_fixed_array(
G_VARIANT_TYPE_BYTE,
marshaled_out_public,
out_public_offset,
sizeof(guint8));
variant = g_variant_new("(u@ayu@ay)",
out_private_offset, out_private_variant,
out_public_offset, out_public_variant);
output = g_variant_get_data_as_bytes(variant);
g_variant_unref(variant);
Esys_Free(out_public);
Esys_Free(out_private);
Esys_Free(creation_data);
Esys_Free(hash);
Esys_Free(ticket);
return output;
}
GBytes *
egg_tpm2_decrypt_master_password(EggTpm2Context *context,
GBytes *input,
GError **error)
{
TSS2_RC ret;
GBytes *output;
TPM2B_SENSITIVE_DATA *out_data;
GVariant *variant;
gconstpointer data;
gsize out_private_offset = 0;
gsize out_public_offset = 0;
gsize count = 0;
gsize offset = 0;
GVariant *out_private_variant;
GVariant *out_public_variant;
ESYS_TR out_key;
variant = g_variant_new_from_bytes(G_VARIANT_TYPE(
"(uayuay)"),
input,
TRUE);
g_variant_get(variant, "(u@ayu@ay)",
&out_private_offset, &out_private_variant,
&out_public_offset, &out_public_variant);
g_variant_unref(variant);
data = g_variant_get_fixed_array(out_private_variant,
&count,
sizeof(guint8));
guint8 *marshaled_out_private = g_memdup2(data, count);
count = 0;
TPM2B_PRIVATE out_private = {
.size = 0
};
ret = Tss2_MU_TPM2B_PRIVATE_Unmarshal(marshaled_out_private,
out_private_offset,
&offset,
&out_private);
g_variant_unref(out_private_variant);
g_free(marshaled_out_private);
if (ret != TSS2_RC_SUCCESS) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Tss2_MU_TPM2B_PRIVATE_Unmarshal: %s",
Tss2_RC_Decode(ret));
return NULL;
}
offset = 0;
data = g_variant_get_fixed_array(out_public_variant,
&count,
sizeof(guint8));
guint8 *marshaled_out_public = g_memdup2(data, count);
TPM2B_PUBLIC out_public = {
.size = 0
};
ret = Tss2_MU_TPM2B_PUBLIC_Unmarshal(marshaled_out_public,
out_public_offset,
&offset,
&out_public);
g_variant_unref(out_public_variant);
g_free(marshaled_out_public);
if (ret != TSS2_RC_SUCCESS) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Tss2_MU_TPM2B_PUBLIC_Unmarshal: %s",
Tss2_RC_Decode(ret));
return NULL;
}
ret = Esys_Load(context->esys_context, context->primary_key,
ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
&out_private, &out_public, &out_key);
if (ret != TSS2_RC_SUCCESS) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Esys_Load: %s", Tss2_RC_Decode(ret));
return NULL;
}
ret = Esys_Unseal(context->esys_context, out_key,
ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
&out_data);
if (ret != TSS2_RC_SUCCESS) {
g_set_error(error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Esys_Unseal: %s", Tss2_RC_Decode(ret));
return NULL;
}
output = g_bytes_new(out_data->buffer, out_data->size);
Esys_Free(out_data);
return output;
}