Skip to content

Commit

Permalink
swtpm_setup: Create IAK hwSerialNum from data extracted from EK cert
Browse files Browse the repository at this point in the history
Signed-off-by: Stefan Berger <[email protected]>
  • Loading branch information
stefanberger committed Aug 26, 2023
1 parent 14e5a69 commit f685cdb
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 17 deletions.
2 changes: 1 addition & 1 deletion src/swtpm_localca/swtpm_localca.c
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ static int create_cert(unsigned long flags, const gchar *typ, const gchar *direc
vmid ? vmid : "unknown",
tpm_serial_num ? ",serialNumber=" : "" ,
tpm_serial_num ? tpm_serial_num : "");
if (tpm_serial_num)
if ((flags & SETUP_TPM2_F) && tpm_serial_num)
options = concat_arrays(options,
(gchar *[]){
"--tpm-serial-num",
Expand Down
6 changes: 4 additions & 2 deletions src/swtpm_setup/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ swtpm_setup_LDFLAGS = \
$(HARDENING_LDFLAGS) \
$(GLIB_LIBS) \
$(JSON_GLIB_LIBS) \
$(LIBCRYPTO_LIBS)
$(LIBCRYPTO_LIBS) \
$(GNUTLS_LIBS)

swtpm_setup_CFLAGS = \
-I$(top_builddir)/include \
Expand All @@ -49,7 +50,8 @@ swtpm_setup_CFLAGS = \
$(CFLAGS) \
$(HARDENING_CFLAGS) \
$(GLIB_CFLAGS) \
$(JSON_GLIB_CFLAGS)
$(JSON_GLIB_CFLAGS) \
$(GNUTLS_CFLAGS)

EXTRA_DIST = \
README
Expand Down
31 changes: 31 additions & 0 deletions src/swtpm_setup/swtpm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1617,6 +1617,36 @@ static int swtpm_tpm2_write_idevid_cert_nvram(struct swtpm *self, gboolean lock_
lock_nvram, "", "IDevID certificate");
}

static int swtpm_tpm2_get_capability(struct swtpm *self, uint32_t cap, uint32_t prop,
uint32_t *res)
{
struct tpm2_get_capability_req {
struct tpm_req_header hdr;
uint32_t cap;
uint32_t prop;
uint32_t count;
} __attribute__((packed)) req = {
.hdr = TPM_REQ_HEADER_INITIALIZER(TPM2_ST_NO_SESSIONS, sizeof(req), TPM2_CC_GETCAPABILITY),
.cap = htobe32(cap),
.prop = htobe32(prop),
.count = htobe32(1),
};
unsigned char tpmresp[27];
size_t tpmresp_len = sizeof(tpmresp);
uint32_t val;
int ret;

ret = transfer(self, &req, sizeof(req), "TPM2_GetCapability", FALSE,
tpmresp, &tpmresp_len, TPM2_DURATION_SHORT);
if (ret != 0)
return 1;

memcpy(&val, &tpmresp[23], sizeof(val));
*res = be32toh(val);

return 0;
}

static const struct swtpm2_ops swtpm_tpm2_ops = {
.shutdown = swtpm_tpm2_shutdown,
.create_iak = swtpm_tpm2_create_iak,
Expand All @@ -1629,6 +1659,7 @@ static const struct swtpm2_ops swtpm_tpm2_ops = {
.write_platform_cert_nvram = swtpm_tpm2_write_platform_cert_nvram,
.write_iak_cert_nvram = swtpm_tpm2_write_iak_cert_nvram,
.write_idevid_cert_nvram = swtpm_tpm2_write_idevid_cert_nvram,
.get_capability = swtpm_tpm2_get_capability,
};

/*
Expand Down
1 change: 1 addition & 0 deletions src/swtpm_setup/swtpm.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ struct swtpm2_ops {
const unsigned char *data, size_t data_len);
int (*write_idevid_cert_nvram)(struct swtpm *self, gboolean lock_nvram,
const unsigned char *data, size_t data_len);
int (*get_capability)(struct swtpm *self, uint32_t cap, uint32_t prop, uint32_t *res);
};

/* common structure for swtpm object */
Expand Down
124 changes: 112 additions & 12 deletions src/swtpm_setup/swtpm_setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@

#include <libtpms/tpm_nvfilename.h>

#include <gnutls/x509.h>

#include "swtpm.h"
#include "swtpm_conf.h"
#include "swtpm_utils.h"
#include "swtpm_setup.h"
#include "swtpm_setup_utils.h"

#include <openssl/sha.h>
Expand Down Expand Up @@ -369,6 +372,52 @@ static int read_certificate_file(const gchar *certsdir, const gchar *filename,
return read_file(*certfile, filecontent, filecontent_len);
}

/* data extracted from EK certificate */
struct ek_certificate_data {
unsigned char id[64];
size_t id_len;
unsigned char serial[20];
size_t serial_len;
};

static int tpm2_extract_certificate_data(gchar *certdata, size_t certdata_len,
struct ek_certificate_data *ecd)
{
gnutls_x509_crt_t cert;
gnutls_datum_t data = {
.data = (unsigned char *)certdata,
.size = certdata_len,
};
int err;
int ret = 1;

if ((err = gnutls_x509_crt_init(&cert)) < 0) {
logerr(gl_LOGFILE, "gnutls_x509_crt_init() failed: %s\n",
gnutls_strerror(err));
return 1;
}
if ((err = gnutls_x509_crt_import(cert, &data, GNUTLS_X509_FMT_DER)) < 0) {
logerr(gl_LOGFILE, "gnutls_x509_crt_import() failed: %s\n",
gnutls_strerror(err));
goto cleanup;
}
if ((err = gnutls_x509_crt_get_authority_key_id(cert, ecd->id, &ecd->id_len, NULL)) < 0) {
logerr(gl_LOGFILE, "gnutls_x509_crt_get_authority_key_id() failed: %s\n",
gnutls_strerror(err));
goto cleanup;
}
if ((err = gnutls_x509_crt_get_serial(cert, ecd->serial, &ecd->serial_len)) < 0) {
logerr(gl_LOGFILE, "gnutls_x509_crt_get_serial() failed: %s\n",
gnutls_strerror(err));
goto cleanup;
}
ret = 0;

cleanup:
gnutls_x509_crt_deinit(cert);
return ret;
}

/*
* Read the certificate from the file where swtpm_cert left it.
* Write the file into the TPM's NVRAM and, if the user wants it,
Expand All @@ -378,7 +427,8 @@ static int tpm2_persist_certificate(unsigned long flags, const gchar *certsdir,
const struct flag_to_certfile *ftc,
unsigned int rsa_keysize, struct swtpm2 *swtpm2,
const gchar *user_certsdir, const gchar *key_type,
const gchar *key_description)
const gchar *key_description,
struct ek_certificate_data *ecd)
{
g_autofree gchar *filecontent = NULL;
g_autofree gchar *certfile = NULL;
Expand All @@ -391,6 +441,12 @@ static int tpm2_persist_certificate(unsigned long flags, const gchar *certsdir,
if (ret != 0)
goto error_unlink;

if (ecd) {
ret = tpm2_extract_certificate_data(filecontent, filecontent_len, ecd);
if (ret != 0)
goto error_unlink;
}

if (ftc->flag == SETUP_IAK_F) {
ret = swtpm2->ops->write_iak_cert_nvram(&swtpm2->swtpm,
!!(flags & SETUP_LOCK_NVRAM_F),
Expand Down Expand Up @@ -428,9 +484,11 @@ static int tpm2_persist_certificate(unsigned long flags, const gchar *certsdir,
static int tpm2_create_ek_and_cert(unsigned long flags, const gchar *config_file,
const gchar *certsdir, const gchar *vmid,
unsigned int rsa_keysize, struct swtpm2 *swtpm2,
const gchar *user_certsdir)
const gchar *user_certsdir,
struct ek_certificate_data *ecd)
{
g_autofree gchar *key_params = NULL;
struct ek_certificate_data *ecd_dup;
const char *key_description;
unsigned long cert_flags;
const gchar *key_type;
Expand All @@ -457,11 +515,19 @@ static int tpm2_create_ek_and_cert(unsigned long flags, const gchar *config_file

for (idx = 0; flags_to_certfiles[idx].filename; idx++) {
if (cert_flags & flags_to_certfiles[idx].flag) {
key_type = flags_to_certfiles[idx].flag & SETUP_EK_CERT_F ? "ek" : "";

ecd_dup = NULL;
if (flags_to_certfiles[idx].flag & SETUP_EK_CERT_F) {
key_type = "ek";
if (rsa_keysize)
ecd_dup = ecd;
} else {
key_type = "";
}

ret = tpm2_persist_certificate(flags, certsdir, &flags_to_certfiles[idx],
rsa_keysize, swtpm2, user_certsdir,
key_type, key_description);
key_type, key_description, ecd_dup);
if (ret)
return 1;
}
Expand All @@ -475,28 +541,55 @@ static int tpm2_create_ek_and_cert(unsigned long flags, const gchar *config_file
static int tpm2_create_eks_and_certs(unsigned long flags, const gchar *config_file,
const gchar *certsdir, const gchar *vmid,
unsigned int rsa_keysize, struct swtpm2 *swtpm2,
const gchar *user_certsdir)
const gchar *user_certsdir,
struct ek_certificate_data *ecd)
{
int ret;

/* 1st key will be RSA */
flags = flags & ~SETUP_TPM2_ECC_F;
ret = tpm2_create_ek_and_cert(flags, config_file, certsdir, vmid, rsa_keysize, swtpm2,
user_certsdir);
user_certsdir, ecd);
if (ret != 0)
return 1;

/* 2nd key will be an ECC; no more platform cert */
flags = (flags & ~SETUP_PLATFORM_CERT_F) | SETUP_TPM2_ECC_F;
return tpm2_create_ek_and_cert(flags, config_file, certsdir, vmid, rsa_keysize, swtpm2,
user_certsdir);
user_certsdir, NULL);
}

static gchar *tpm2_create_tpm_serial_num(struct swtpm2 *swtpm2, const struct ek_certificate_data *ecd)
{
struct swtpm *swtpm = &swtpm2->swtpm;
g_autofree gchar *cert_ser = NULL;
g_autofree gchar *ca_akid = NULL;
char code[5] = {0, };
uint32_t res;
size_t i;
int ret;

ret = swtpm2->ops->get_capability(swtpm, TPM_CAP_TPM_PROPERTIES, TPM_PT_MANUFACTURER, &res);
if (ret != 0) {
logerr(gl_LOGFILE, "TPM_GetCapability failed\n");
return NULL;
}

ca_akid = print_as_hex(ecd->id, ecd->id_len);
cert_ser = print_as_hex(ecd->serial, ecd->serial_len);
for (i = 0; i < sizeof(res); i++)
code[i] = res >> (8 * (3 - i));

return g_strdup_printf("%s:%s:%s", code, ca_akid, cert_ser);
}

/* Create the IAK and cert */
static int tpm2_create_iak_idevid_and_certs(unsigned long flags, const gchar *config_file,
const gchar *certsdir, const char *vmid,
struct swtpm2 *swtpm2, const gchar *user_certsdir)
struct swtpm2 *swtpm2, const gchar *user_certsdir,
const struct ek_certificate_data *ecd)
{
g_autofree gchar *tpm_serial_num = NULL;
g_autofree gchar *key_params = NULL;
const char *key_description;
const char *key_type = NULL;
Expand All @@ -509,6 +602,8 @@ static int tpm2_create_iak_idevid_and_certs(unsigned long flags, const gchar *co
if (!cert_flags)
return 0;

tpm_serial_num = tpm2_create_tpm_serial_num(swtpm2, ecd);

for (idx = 0; flags_to_certfiles[idx].filename; idx++) {
if (cert_flags & flags_to_certfiles[idx].flag) {

Expand All @@ -525,13 +620,14 @@ static int tpm2_create_iak_idevid_and_certs(unsigned long flags, const gchar *co
return 1;

ret = call_create_certs(flags, flags_to_certfiles[idx].flag, config_file,
certsdir, key_params, vmid, "TEST", &swtpm2->swtpm);
certsdir, key_params, vmid, tpm_serial_num,
&swtpm2->swtpm);
if (ret != 0)
return 1;

ret = tpm2_persist_certificate(flags, certsdir, &flags_to_certfiles[idx],
0, swtpm2, user_certsdir,
key_type, key_description);
key_type, key_description, NULL);
if (ret)
return 1;
}
Expand Down Expand Up @@ -605,6 +701,10 @@ static int init_tpm2(unsigned long flags, gchar **swtpm_prg_l, const gchar *conf
unsigned int rsa_keysize, const gchar *certsdir,
const gchar *user_certsdir)
{
struct ek_certificate_data ecd = {
.id_len = sizeof(ecd.id),
.serial_len = sizeof(ecd.serial),
};
struct swtpm2 *swtpm2;
struct swtpm *swtpm;
int ret;
Expand All @@ -629,12 +729,12 @@ static int init_tpm2(unsigned long flags, gchar **swtpm_prg_l, const gchar *conf
}

ret = tpm2_create_eks_and_certs(flags, config_file, certsdir, vmid, rsa_keysize, swtpm2,
user_certsdir);
user_certsdir, &ecd);
if (ret != 0)
goto destroy;

ret = tpm2_create_iak_idevid_and_certs(flags, config_file, certsdir, vmid,
swtpm2, user_certsdir);
swtpm2, user_certsdir, &ecd);
if (ret != 0)
goto destroy;
}
Expand Down
3 changes: 3 additions & 0 deletions src/swtpm_setup/swtpm_setup.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@

extern gchar *gl_LOGFILE;

#define TPM_CAP_TPM_PROPERTIES 6
#define TPM_PT_MANUFACTURER 0x105

#endif /* SWTPM_SETUP_H */
31 changes: 29 additions & 2 deletions tests/create_certs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,29 @@

#echo $@

ek_cert=\
MIID9TCCAl2gAwIBAgICBL4wDQYJKoZIhvcNAQELBQAwGDEWMBQGA1UEAxMNc3d0cG0tbG9jYWxj\
YTAgFw0yMzA4MjIwMTM2MDdaGA85OTk5MTIzMTIzNTk1OVowEjEQMA4GA1UEAxMHdW5rbm93bjCC\
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJSKqmO3O1KnFQFXENX+z8kcL0eIFW8xVhPW\
eCJrChwRil36KaxJHPf5aqzWkMuZnCHI2U5susPq7BAeCCPXaOb3pwbs3tgEC5eGAEApv6Y+5Yep\
ia8chBGI58q3CuKL/HcToNnzT1MwwTtq4dCzav9BcmR/tkh9fSUvjOwZyPIDktgvMXAkLFzqhAx3\
8TKSqdhjWgbUAr8fGsAUGm1bvepphEpycjCfftdC6/GnGEbHsDWjBS944k3sHWD7Aik/VV9wEHLT\
EIN8r3rKdj5nzqUrGRFqSupN5e25YRhKxS1SB9wFOiOYoevsPR7Bh3/5MdZd33zhnTbrA9f0RDxu\
qjUCAwEAAaOBzDCByTAQBgNVHSUECTAHBgVngQUIATBSBgNVHREBAf8ESDBGpEQwQjEWMBQGBWeB\
BQIBDAtpZDowMDAwMTAxNDEQMA4GBWeBBQICDAVzd3RwbTEWMBQGBWeBBQIDDAtpZDoyMDE5MTAy\
MzAMBgNVHRMBAf8EAjAAMCIGA1UdCQQbMBkwFwYFZ4EFAhAxDjAMDAMyLjACAQACAgCkMB8GA1Ud\
IwQYMBaAFMFMlKkwREqQv5MU2eVVkxUZIOP0MA4GA1UdDwEB/wQEAwIFIDANBgkqhkiG9w0BAQsF\
AAOCAYEAihjxS4PmR5xO7jAYsF5hqeMvEh5+wH+OWjkj9oH+a8N6oFlxUJeQgLLFgK+Jyq4zt0kz\
tdAaFX0XrWfPH/s1AQdwtUzqOHdSHWcBmfPV+MlMCtz1HjfC5GGKmCHPgwDRLiowwzsWyKFRPKlu\
UtmtP0ukRTbzGa/j3GpBSnIn7l2yTrnXZ6XXeZ/gvHghzyp02aGJ2Ei873X57zOuFmz1z++WwXRN\
ipRoAjga57NAz/f1RceJuF+zA8aAX7GY2dcvDCVpU1yoBsWt9gXtZ/4ZO400fbwnxz3zVLJEXgpR\
jd+XbUUxsGMWqWZ3qEApbrkWjS77TXkDmOqK8Nh92mZvLSMHBJa/mzWFJBPpu+MCSPbO9kAhfB5W\
F2ynlGuQfeBue4ju5PmcID3xs2FbCItWyj8bJhuA2QQDYmUrSnqQJ9zNLj7ibbq7hDWsaeko65/E\
HYBXBvksWO4cdoR7F9pcuyhsJDMU7jyGAo0RKuRkUrGnN2Aja4GKSXTilXTCeq/5


main() {
local typ ek dir vmid
local typ ek dir vmid tpm2=0

while [ $# -ne 0 ]; do
#echo $1
Expand All @@ -25,14 +46,20 @@ main() {
vmid="$1"
;;
--tpm2)
tpm2=1
;;
esac
shift
done

case "$typ" in
ek)
echo -n "ek" > ${dir}/ek.cert
# ek cert must be parseable for a TPM 2
if [ "${tpm2}" -ne 0 ]; then
base64 -d <<< "${ek_cert}" > ${dir}/ek.cert
else
echo -n "ek" > ${dir}/ek.cert
fi
;;
platform)
echo -n "platform" > ${dir}/platform.cert
Expand Down

0 comments on commit f685cdb

Please sign in to comment.