mirror of
https://gitlab.gnome.org/GNOME/libsecret.git
synced 2024-12-22 12:48:51 +00:00
486 lines
11 KiB
C
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;
|
|
}
|