swtpm_cert: Add support for TPM2

TPM2 allows the primary key to also be a signing key, so in case
--tpm2 is provided, --allow-signing can be provided as well in
case the primary can also be used for signing operations.

We use SHA256 for the signing algorithm when TPM 2 is being used.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
This commit is contained in:
Stefan Berger 2016-12-21 17:05:40 -05:00
parent 8fcec541f8
commit e5ffc74dc8
5 changed files with 249 additions and 16 deletions

View File

@ -1,4 +1,4 @@
.\" Automatically generated by Pod::Man 2.27 (Pod::Simple 3.28)
.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.31)
.\"
.\" Standard preamble:
.\" ========================================================================
@ -133,7 +133,7 @@
.\" ========================================================================
.\"
.IX Title "swtpm_cert 8"
.TH swtpm_cert 8 "2014-09-23" "swtpm" ""
.TH swtpm_cert 8 "2016-12-20" "swtpm" ""
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
@ -162,6 +162,12 @@ The public key (\s-1EK\s0) in \s-1PEM\s0 format.
.IX Item "--modulus <hex digits>"
The modulus of the public key as a string of hex digits. This option
can be used in place of the \-\-pubkey option.
.IP "<\-\-ecc\-x <hex digits>>" 4
.IX Item "<--ecc-x <hex digits>>"
The elliptic curve parameter x as string of hex digits.
.IP "<\-\-ecc\-y <hex digits>>" 4
.IX Item "<--ecc-y <hex digits>>"
The elliptic curve parameter y as string of hex digits.
.IP "\fB\-\-exponent <exponent\fR>" 4
.IX Item "--exponent <exponent>"
The exponent of the public key. By default 0x10001 is assumed.
@ -210,6 +216,12 @@ The platform's version.
Subject to for example provide the location of the \s-1TPM\s0 in the format of
C=<country>,ST=<state>,L=<location>.
Note that the location must no contain any spaces.
.IP "\fB\-\-tpm2\fR" 4
.IX Item "--tpm2"
Issue \s-1TPM 2\s0 compliant certificates.
.IP "\fB\-\-allow\-signing\fR" 4
.IX Item "--allow-signing"
Create an \s-1EK\s0 that can also be used for signing. This option requires \-\-tpm2.
.IP "\fB\-\-help, \-h\fR" 4
.IX Item "--help, -h"
Display the help screen

View File

@ -95,6 +95,14 @@ Subject to for example provide the location of the TPM in the format of
C=<country>,ST=<state>,L=<location>.
Note that the location must no contain any spaces.
=item B<--tpm2>
Issue TPM 2 compliant certificates.
=item B<--allow-signing>
Create an EK that can also be used for signing. This option requires --tpm2.
=item B<--help, -h>
Display the help screen

View File

@ -64,6 +64,10 @@ enum cert_type_t {
CERT_TYPE_AIK,
};
/* some flags */
#define CERT_TYPE_TPM2_F 1
#define ALLOW_SIGNING_F 2 /* EK can be used for signing */
extern const ASN1_ARRAY_TYPE tpm_asn1_tab[];
ASN1_TYPE _tpm_asn;
@ -124,6 +128,9 @@ usage(const char *prg)
" C=US,ST=NY,L=NewYork; not used with TPM1.2\n"
"--add-header : Add the TCG certificate header describing\n"
" a TCG_PCCLIENT_STORED_CERT for TPM1.2 NVRAM\n"
"--tpm2 : Issue a TPM 2 compliant certificate\n"
"--allow-signing : The EK of a TPM 2 allows signing;\n"
" requires --tpm2\n"
"--version : Display version and exit\n"
"--help : Display this help screen and exit\n"
"\n",
@ -468,6 +475,43 @@ cleanup:
return err;
}
static int
create_platf_manufacturer_info(const char *manufacturer,
const char *platf_model,
const char *platf_version,
gnutls_datum_t *asn1)
{
ASN1_TYPE at = ASN1_TYPE_EMPTY;
int err;
err = asn_init();
if (err != ASN1_SUCCESS) {
goto cleanup;
}
err = build_platf_manufacturer_info(&at, manufacturer,
platf_model, platf_version);
if (err != ASN1_SUCCESS) {
goto cleanup;
}
err = encode_asn1(asn1, at);
#if 0
fprintf(stderr, "size=%d\n", asn1->size);
unsigned int i = 0;
for (i = 0; i < asn1->size; i++) {
fprintf(stderr, "%02x ", asn1->data[i]);
}
fprintf(stderr, "\n");
#endif
cleanup:
asn1_delete_structure(&at);
return err;
}
static int
create_tpm_and_platform_manuf_info(
const char *tpm_manufacturer,
@ -767,6 +811,7 @@ main(int argc, char *argv[])
unsigned char *modulus_bin = NULL;
int modulus_len = 0;
gnutls_datum_t datum = { NULL, 0}, out = { NULL, 0};
gnutls_digest_algorithm_t hashAlgo = GNUTLS_DIG_SHA1;
int serial = 1;
time_t now;
int err;
@ -793,6 +838,7 @@ main(int argc, char *argv[])
char *spec_family = NULL;
long int spec_level = ~0;
long int spec_revision = ~0;
int flags = 0;
i = 1;
while (i < argc) {
@ -970,6 +1016,10 @@ main(int argc, char *argv[])
write_pem = true;
} else if (!strcmp(argv[i], "--add-header")) {
add_header = true;
} else if (!strcmp(argv[i], "--tpm2")) {
flags |= CERT_TYPE_TPM2_F;
} else if (!strcmp(argv[i], "--allow-signing")) {
flags |= ALLOW_SIGNING_F;
} else if (!strcmp(argv[i], "--version")) {
versioninfo(argv[0]);
exit(0);
@ -983,7 +1033,10 @@ main(int argc, char *argv[])
}
i++;
}
if (flags & CERT_TYPE_TPM2_F)
hashAlgo = GNUTLS_DIG_SHA256;
ser_number = htonl(serial);
if (pubkey_filename == NULL && modulus_bin == NULL) {
@ -1140,7 +1193,7 @@ if (_err != GNUTLS_E_SUCCESS) { \
gnutls_strerror(err))
/* 3.5.6 Subject -- must be empty for TPM 1.2 */
if (subject && false) {
if (subject && (flags & CERT_TYPE_TPM2_F)) {
err = gnutls_x509_crt_set_dn(crt, subject, &error);
CHECK_GNUTLS_ERROR(err,
"Could not set DN on CRT: %s\n"
@ -1172,7 +1225,7 @@ if (_err != GNUTLS_E_SUCCESS) { \
}
/* 3.5.8 Certificate Policies -- skip since not mandated */
/* 3.5.9 Subject Alternative Names -- missing code */
/* 3.5.9 Subject Alternative Names */
switch (certtype) {
case CERT_TYPE_EK:
err = create_tpm_manufacturer_info(tpm_manufacturer, tpm_model,
@ -1183,15 +1236,29 @@ if (_err != GNUTLS_E_SUCCESS) { \
}
break;
case CERT_TYPE_PLATFORM:
err = create_tpm_and_platform_manuf_info(tpm_manufacturer, tpm_model,
tpm_version,
platf_manufacturer,
platf_model, platf_version,
if (flags & CERT_TYPE_TPM2_F) {
err = create_platf_manufacturer_info(platf_manufacturer,
platf_model,
platf_version,
&datum);
if (err) {
fprintf(stderr, "Could not create TPM and platform manufacturer "
"info");
goto cleanup;
if (err) {
fprintf(stderr, "Could not create platform manufacturer "
"info");
goto cleanup;
}
} else {
err = create_tpm_and_platform_manuf_info(tpm_manufacturer,
tpm_model,
tpm_version,
platf_manufacturer,
platf_model,
platf_version,
&datum);
if (err) {
fprintf(stderr, "Could not create TPM and platform "
"manufacturer info");
goto cleanup;
}
}
break;
case CERT_TYPE_AIK:
@ -1268,7 +1335,15 @@ if (_err != GNUTLS_E_SUCCESS) { \
switch (certtype) {
case CERT_TYPE_EK:
case CERT_TYPE_PLATFORM:
key_usage = GNUTLS_KEY_KEY_ENCIPHERMENT;
if (flags & CERT_TYPE_TPM2_F) {
if (flags & ALLOW_SIGNING_F) {
key_usage = GNUTLS_KEY_DIGITAL_SIGNATURE;
} else {
key_usage = GNUTLS_KEY_KEY_ENCIPHERMENT;
}
} else {
key_usage = GNUTLS_KEY_KEY_ENCIPHERMENT;
}
break;
case CERT_TYPE_AIK:
key_usage = GNUTLS_KEY_DIGITAL_SIGNATURE;
@ -1333,7 +1408,7 @@ if (_err != GNUTLS_E_SUCCESS) { \
gnutls_strerror(err))
/* sign cert */
err = gnutls_x509_crt_sign2(crt, sigcert, sigkey, GNUTLS_DIG_SHA1, 0);
err = gnutls_x509_crt_sign2(crt, sigcert, sigkey, hashAlgo, 0);
CHECK_GNUTLS_ERROR(err, "Could not sign the CRT: %s\n",
gnutls_strerror(err))

View File

@ -57,7 +57,8 @@ endif
if WITH_GNUTLS
TESTS += \
test_swtpm_cert
test_swtpm_cert \
test_tpm2_swtpm_cert
if WITH_SWTPM_SETUP
TESTS += \
test_swtpm_setup_create_cert

137
tests/test_tpm2_swtpm_cert Executable file
View File

@ -0,0 +1,137 @@
#!/bin/bash
# For the license, see the LICENSE file in the root directory.
DIR=$(dirname "$0")
ROOT=${DIR}/..
SWTPM_CERT=${ROOT}/src/swtpm_cert/swtpm_cert
cert=$(mktemp)
trap "cleanup" SIGTERM EXIT
function cleanup()
{
rm -f ${cert}
}
function check_cert_size()
{
local cert="$1"
local exp="$2"
# Unfortunately different GnuTLS versions may create certs of different
# sizes; deactivate this test for now
return
local size=$(stat -c%s ${cert} 2>/dev/null)
if [ $size -ne $exp ]; then
echo "Warning: Certificate file has unexpected size."
echo " Expected: $exp; found: $size"
fi
}
${SWTPM_CERT} \
--tpm2 \
--allow-signing \
--signkey ${DIR}/data/signkey.pem \
--issuercert ${DIR}/data/issuercert.pem \
--out-cert ${cert} \
--modulus 'b9dda830729de58f9f5bed2b3b9394ad4ec5afb9c390b89a3337250cbc575cfc8f31f7ffd3f05f4155076f7d1605381cd281b7f147b801154e4f89ee529fe36eae50f79561850e5b63037edaacbb390ea3fcd037e674fb179e3c5afe31214d78a756ca44cc6cf25421b51420ede548310c92b08a513ccc62fd0ef45dcf6546f6e865be6a661d045d1c47b60b428d11dc97cb9f35ee7c385bb20320934b015f8014e8fb19851c2af307e1e64648c142175e40b60615dc494fdb09ea5d5a6f3273b65a241e3cf30cc449b9fb3f900d1ed4be967b32b16f95a1d732dbfa143eaa1c2017556117f70faee5d77f836705d05405361ad5871a32161fa5a1234cfab497' \
--days 3650 \
--pem \
--tpm-manufacturer IBM --tpm-model swtpm-libtpms --tpm-version 1.2 \
--tpm-spec-family 2.0 --tpm-spec-revision 146 --tpm-spec-level 0
if [ $? -ne 0 ]; then
echo "Error: ${SWTPM_CERT} returned error code."
exit 1
fi
#expecting size to be constant
check_cert_size "${cert}" 1224
# truncate result file
echo -n > ${cert}
echo "Test 1: OK"
${SWTPM_CERT} \
--tpm2 \
--signkey ${DIR}/data/signkey.pem \
--issuercert ${DIR}/data/issuercert.pem \
--out-cert ${cert} \
--modulus 'b9dda830729de58f9f5bed2b3b9394ad4ec5afb9c390b89a3337250cbc575cfc8f31f7ffd3f05f4155076f7d1605381cd281b7f147b801154e4f89ee529fe36eae50f79561850e5b63037edaacbb390ea3fcd037e674fb179e3c5afe31214d78a756ca44cc6cf25421b51420ede548310c92b08a513ccc62fd0ef45dcf6546f6e865be6a661d045d1c47b60b428d11dc97cb9f35ee7c385bb20320934b015f8014e8fb19851c2af307e1e64648c142175e40b60615dc494fdb09ea5d5a6f3273b65a241e3cf30cc449b9fb3f900d1ed4be967b32b16f95a1d732dbfa143eaa1c2017556117f70faee5d77f836705d05405361ad5871a32161fa5a1234cfab497' \
--days 3650 \
--subject "OU=foo,L=NewYork,ST=NY,C=US" \
--pem \
--tpm-manufacturer IBM --tpm-model swtpm-libtpms --tpm-version 1.2 \
--tpm-spec-family 2.0 --tpm-spec-revision 146 --tpm-spec-level 0
if [ $? -ne 0 ]; then
echo "Error: ${SWTPM_CERT} returned error code."
exit 1
fi
#expecting size to be constant
check_cert_size "${cert}" 1302
# truncate result file
echo -n > ${cert}
echo "Test 2: OK"
${SWTPM_CERT} \
--tpm2 \
--signkey ${DIR}/data/signkey.pem \
--issuercert ${DIR}/data/issuercert.pem \
--out-cert ${cert} \
--pubkey ${DIR}/data/pubek.pem \
--days 3650 \
--subject "OU=foo,L=NewYork,ST=NY,C=US" \
--pem \
--tpm-manufacturer IBM --tpm-model swtpm-libtpms --tpm-version 1.2 \
--tpm-spec-family 2.0 --tpm-spec-revision 146 --tpm-spec-level 0
if [ $? -ne 0 ]; then
echo "Error: ${SWTPM_CERT} returned error code."
exit 1
fi
#expecting size to be constant
check_cert_size "${cert}" 1367
# truncate result file
#certtool --certificate-info --infile ${cert}
echo -n > ${cert}
echo "Test 3: OK"
###################### Platform Certificate #####################
${SWTPM_CERT} \
--tpm2 \
--type platform \
--signkey ${DIR}/data/signkey.pem \
--issuercert ${DIR}/data/issuercert.pem \
--pubkey ${DIR}/data/pubek.pem \
--out-cert ${cert} \
--days 3650 \
--subject "OU=foo,L=NewYork,ST=NY,C=US" \
--pem \
--tpm-manufacturer IBM --tpm-model swtpm-libtpms --tpm-version 1.2 \
--platform-manufacturer Fedora \
--platform-model QEMU \
--platform-version 2.1
if [ $? -ne 0 ]; then
echo "Error: ${SWTPM_CERT} returned error code."
exit 1
fi
#expecting size to be constant
check_cert_size "${cert}" 1411
# truncate result file
#certtool --certificate-info --infile ${cert}
echo -n > ${cert}
echo "Test 4: OK"