swtpm_setup: Create IAK hwSerialNum from data extracted from EK cert

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>
This commit is contained in:
Stefan Berger 2023-08-21 17:58:36 -04:00
parent 969f50f9c6
commit 5c4b2ba3a1
7 changed files with 207 additions and 16 deletions

View File

@ -39,7 +39,8 @@ swtpm_setup_LDFLAGS = \
$(HARDENING_LDFLAGS) \
$(GLIB_LIBS) \
$(JSON_GLIB_LIBS) \
$(LIBCRYPTO_LIBS)
$(LIBCRYPTO_LIBS) \
$(GNUTLS_LIBS)
swtpm_setup_CFLAGS = \
-I$(top_builddir)/include \
@ -51,7 +52,8 @@ swtpm_setup_CFLAGS = \
$(CFLAGS) \
$(HARDENING_CFLAGS) \
$(GLIB_CFLAGS) \
$(JSON_GLIB_CFLAGS)
$(JSON_GLIB_CFLAGS) \
$(GNUTLS_CFLAGS)
EXTRA_DIST = \
README

View File

@ -1674,6 +1674,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,
@ -1687,6 +1717,7 @@ static const struct swtpm2_ops swtpm_tpm2_ops = {
.get_active_profile = swtpm_tpm2_get_active_profile,
.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,
};
/*

View File

@ -63,6 +63,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 */

View File

@ -32,10 +32,13 @@
#include <libtpms/tpm_nvfilename.h>
#include <gnutls/x509.h>
#include "profile.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>
@ -371,6 +374,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,
@ -380,7 +429,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;
@ -393,6 +443,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),
@ -430,9 +486,11 @@ error_unlink:
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;
@ -459,11 +517,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;
}
@ -477,28 +543,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;
@ -511,6 +605,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) {
@ -529,13 +625,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;
}
@ -677,6 +774,10 @@ static int init_tpm2(unsigned long flags, gchar **swtpm_prg_l, const gchar *conf
const gchar *user_certsdir, const gchar *json_profile,
int json_profile_fd, const gchar *profile_remove_disabled_param)
{
struct ek_certificate_data ecd = {
.id_len = sizeof(ecd.id),
.serial_len = sizeof(ecd.serial),
};
struct swtpm2 *swtpm2;
struct swtpm *swtpm;
int ret;
@ -706,12 +807,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;
}

View File

@ -14,4 +14,7 @@
extern gchar *gl_LOGFILE;
#define TPM_CAP_TPM_PROPERTIES 6
#define TPM_PT_MANUFACTURER 0x105
#endif /* SWTPM_SETUP_H */

View File

@ -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
@ -25,6 +46,7 @@ main() {
vmid="$1"
;;
--tpm2)
tpm2=1
;;
esac
shift
@ -32,7 +54,12 @@ main() {
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

View File

@ -17,6 +17,8 @@ CERTSERIAL=${workdir}/certserial
USER_CERTSDIR=${workdir}/mycerts
mkdir -p "${USER_CERTSDIR}"
vmid="test"
PATH=${TOPBUILD}/src/swtpm_bios:$PATH
trap "cleanup" SIGTERM EXIT
@ -197,6 +199,7 @@ for pwdfile in "" "${PWDFILE}"; do
--logfile "${workdir}/logfile" \
--tpm "${SWTPM_EXE} socket ${SWTPM_TEST_SECCOMP_OPT}" \
--overwrite \
--vmid "${vmid}" \
--write-ek-cert-files "${workdir}" \
${pwdfile:+--pwdfile "${pwdfile}"}; then
echo "Error: Could not run $SWTPM_SETUP."
@ -235,6 +238,29 @@ for pwdfile in "" "${PWDFILE}"; do
$CERTTOOL --inder --infile "${certfile}" -i
exit 1
fi
# IAK and IDevID need serialNumber to match <company>:<ek cert akid>:<ek cert serial num>
if [[ ${certfile} =~ (iak|idevid) ]]; then
cert_vmid=$($CERTTOOL --inder --infile "${certfile}" -i \
| sed -n 's/.*Subject:.*serialNumber=//p')
if [ "${cert_vmid}" != "${vmid}" ]; then
echo "Error: The serialNumber in the cert is wrong"
echo "expected: ${vmid}"
echo "actual : ${cert_vmid}"
exit 1
fi
# The serial number starts at the 12th ASCII char
serial=$($CERTTOOL --inder --infile "${certfile}" -i \
| sed -n "s/.*otherName ASCII: [[:print:]]\{11\}//p")
if [ -z "$serial" ] || \
[ "${serial}" != \
"$(echo "${serial}" | \
sed -n 's/\([[:print:]^:]*\):\([[:xdigit:]^:]*\):\([[:xdigit:]^:]*\)/\1:\2:\3/p')" ];
then
echo "Error: serial Number does not seem to be properly formatted"
echo " serial: ${serial}"
exit 1
fi
fi
done
swtpm_setup_reconfigure "${workdir}" "${pwdfile}"