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
Create the IAK hwSerialNum from the authority key identifier and
serial number extracted from the EK certificate.

Adjust a test script that now needs to use a valid certificate for the EK
so that we can get the authority key identifier and serial from it to
create the serial number for the IAK certificate.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
  • Loading branch information
stefanberger committed Mar 19, 2024
1 parent c72cb42 commit f85d43c
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 16 deletions.
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
125 changes: 113 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,56 @@ 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;
uint32_t res;
char code[sizeof(res) + 1];
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));
code[4] = 0;

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 +603,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 +621,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 +702,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 +730,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
Loading

0 comments on commit f85d43c

Please sign in to comment.