mirror of
https://github.com/stefanberger/swtpm.git
synced 2026-01-15 13:57:05 +00:00
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:
parent
969f50f9c6
commit
5c4b2ba3a1
@ -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
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -14,4 +14,7 @@
|
||||
|
||||
extern gchar *gl_LOGFILE;
|
||||
|
||||
#define TPM_CAP_TPM_PROPERTIES 6
|
||||
#define TPM_PT_MANUFACTURER 0x105
|
||||
|
||||
#endif /* SWTPM_SETUP_H */
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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}"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user