From 35e976de681806331e7b38fc40176809227a6d63 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 29 Oct 2018 22:02:06 -0400 Subject: [PATCH] samples: Extend script to create a CA using a TPM 2.0 for signing Extend the script that creates a CA that uses a TPM 2.0 for signing. For this we have to create tokens using the TPM 2.0 pkcs11 module's tpm2_ptool and can then use the p11tool for creating keys. Signed-off-by: Stefan Berger --- man/man8/swtpm-create-tpmca.8 | 43 +++++++- man/man8/swtpm-create-tpmca.pod | 39 ++++++- samples/swtpm-create-tpmca | 181 ++++++++++++++++++++++++-------- samples/swtpm-localca | 20 ++-- 4 files changed, 228 insertions(+), 55 deletions(-) diff --git a/man/man8/swtpm-create-tpmca.8 b/man/man8/swtpm-create-tpmca.8 index 26b6dbbac..aee87c6d3 100644 --- a/man/man8/swtpm-create-tpmca.8 +++ b/man/man8/swtpm-create-tpmca.8 @@ -133,7 +133,7 @@ .\" ======================================================================== .\" .IX Title "swtpm-create-tpmca 8" -.TH swtpm-create-tpmca 8 "2018-10-17" "swtpm" "" +.TH swtpm-create-tpmca 8 "2018-10-30" "swtpm" "" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -209,6 +209,14 @@ The hostname where tcsd is running on. The default hostname is 'localhost'. .IX Item "-tss-tcsd-port" The \s-1TCP\s0 port on which tcsd is listening for messages. The default port is 30003. +.IP "\fB\-\-tpm2\fR" 4 +.IX Item "--tpm2" +The \s-1TPM\s0 to use for signing the certificates is a \s-1TPM 2\s0 and Intel's \s-1TSS 2.0\s0 +stack must be running (tpm2\-abrmd) along with the \s-1TPM 2.0 PKCS11\s0 module. +The \s-1TPM 2.0 PKCS11\s0 module must have been initialized using the tpm2_ptool. +.Sp +The environment variables \s-1GNUTLS_PIN\s0 and \s-1GNUTLS_SO_PIN\s0 should be set to hold +the PINs. If they are not set the default \s-1PIN\s0's 'swtpm\-tpmca' will be used. .IP "\fB\-help, \-h, \-?\fR" 4 .IX Item "-help, -h, -?" Display the help screen and exit. @@ -219,6 +227,9 @@ into /var/lib/swtpm\-localca and the swtpm-localca configuration to /etc/swtpm\-localca.conf. It can then be used for signing certificates of newly created \fBswtpm\fR TPMs. .PP +If the host's \s-1TPM\s0 is a \s-1TPM 1.2,\s0 we need to start the tcsd first and can +then create the \s-1TPM\s0 key and \s-1TPM CA\s0 certificate: +.PP .Vb 10 \& #> sudo systemctl start tcsd \& #> sudo /usr/share/swtpm/swtpm\-create\-tpmca \e @@ -237,10 +248,38 @@ newly created \fBswtpm\fR TPMs. \& signingkey_password = password \& parentkey_password = password .Ve +.PP +Alternatively, if the host's \s-1TPM\s0 is a \s-1TPM 2\s0 and Intel's \s-1TPM 2\s0 stack is +installed, we need to start tpm2\-abrmd first and can then create the \s-1TPM\s0 key +and \s-1TPM CA\s0 certificate: +.PP +.Vb 11 +\& #> sudo systemctl start tpm2\-abrmd +\& #> sudo GNUTLS_PIN=mypin GNUTLS_SO_PIN=mysopin /usr/share/swtpm/swtpm\-create\-tpmca \e +\& \-\-dir /var/lib/swtpm\-localca \e +\& \-\-overwrite \e +\& \-\-outfile /etc/swtpm\-localca.conf \e +\& \-\-group tss \e +\& \-\-tpm2 +\& statedir = /var/lib/swtpm\-localca +\& signingkey = pkcs11:model=TPM2%20PKCS%2311\e;manufacturer=Intel\e;serial=0000000000000000\e;token=swtpm\-tpmca\e;id=%31\e;object=swtpm\-tpmca\-key\e;type=private +\& issuercert = /var/lib/swtpm\-localca/swtpm\-localca\-tpmca\-cert.pem +\& certserial = /var/lib/swtpm\-localca/certserial +.Ve +.PP +To test either one of the above \s-1TPM\s0 CAs, run the following command: +.PP +.Vb 5 +\& #> /usr/share/swtpm/swtpm\-localca \e +\& \-\-type ek \-\-ek x=11,y=13 \e +\& \-\-dir /tmp \-\-vmid test \-\-tpm2 \e +\& \-\-tpm\-spec\-family 2.0 \-\-tpm\-spec\-revision 146 \-\-tpm\-spec\-level 00 \e +\& \-\-tpm\-model swtpm \-\-tpm\-version 20170101 \-\-tpm\-manufacturer IBM +.Ve .SH "KNOWN ISSUES" .IX Header "KNOWN ISSUES" The interaction of GnuTLS certtool with the \s-1TPM TCSD\s0 daemon may cause so -many \s-1TPM\s0 (key) authentication failures, that the \s-1TPM\s0 refuses to accept any +many \s-1TPM\s0 (key) authentication failures that the \s-1TPM\s0 refuses to accept any more authenticated commands until the \s-1TPM\s0's owner sends it the TPM_ORD_ResetLockValue command. The reason for this is that certtool first tries to use 20 zero bytes for the \s-1SRK\s0 password and only then prompts for diff --git a/man/man8/swtpm-create-tpmca.pod b/man/man8/swtpm-create-tpmca.pod index 3ee59f08e..86383a1a0 100644 --- a/man/man8/swtpm-create-tpmca.pod +++ b/man/man8/swtpm-create-tpmca.pod @@ -85,6 +85,15 @@ The hostname where tcsd is running on. The default hostname is 'localhost'. The TCP port on which tcsd is listening for messages. The default port is 30003. +=item B<--tpm2> + +The TPM to use for signing the certificates is a TPM 2 and Intel's TSS 2.0 +stack must be running (tpm2-abrmd) along with the TPM 2.0 PKCS11 module. +The TPM 2.0 PKCS11 module must have been initialized using the tpm2_ptool. + +The environment variables GNUTLS_PIN and GNUTLS_SO_PIN should be set to hold +the PINs. If they are not set the default PIN's 'swtpm-tpmca' will be used. + =item B<-help, -h, -?> Display the help screen and exit. @@ -98,6 +107,9 @@ into /var/lib/swtpm-localca and the swtpm-localca configuration to /etc/swtpm-localca.conf. It can then be used for signing certificates of newly created B TPMs. +If the host's TPM is a TPM 1.2, we need to start the tcsd first and can +then create the TPM key and TPM CA certificate: + #> sudo systemctl start tcsd #> sudo /usr/share/swtpm/swtpm-create-tpmca \ --dir /var/lib/swtpm-localca \ @@ -115,10 +127,35 @@ newly created B TPMs. signingkey_password = password parentkey_password = password + +Alternatively, if the host's TPM is a TPM 2 and Intel's TPM 2 stack is +installed, we need to start tpm2-abrmd first and can then create the TPM key +and TPM CA certificate: + + #> sudo systemctl start tpm2-abrmd + #> sudo GNUTLS_PIN=mypin GNUTLS_SO_PIN=mysopin /usr/share/swtpm/swtpm-create-tpmca \ + --dir /var/lib/swtpm-localca \ + --overwrite \ + --outfile /etc/swtpm-localca.conf \ + --group tss \ + --tpm2 + statedir = /var/lib/swtpm-localca + signingkey = pkcs11:model=TPM2%20PKCS%2311\;manufacturer=Intel\;serial=0000000000000000\;token=swtpm-tpmca\;id=%31\;object=swtpm-tpmca-key\;type=private + issuercert = /var/lib/swtpm-localca/swtpm-localca-tpmca-cert.pem + certserial = /var/lib/swtpm-localca/certserial + +To test either one of the above TPM CAs, run the following command: + + #> /usr/share/swtpm/swtpm-localca \ + --type ek --ek x=11,y=13 \ + --dir /tmp --vmid test --tpm2 \ + --tpm-spec-family 2.0 --tpm-spec-revision 146 --tpm-spec-level 00 \ + --tpm-model swtpm --tpm-version 20170101 --tpm-manufacturer IBM + =head1 KNOWN ISSUES The interaction of GnuTLS certtool with the TPM TCSD daemon may cause so -many TPM (key) authentication failures, that the TPM refuses to accept any +many TPM (key) authentication failures that the TPM refuses to accept any more authenticated commands until the TPM's owner sends it the TPM_ORD_ResetLockValue command. The reason for this is that certtool first tries to use 20 zero bytes for the SRK password and only then prompts for diff --git a/samples/swtpm-create-tpmca b/samples/swtpm-create-tpmca index b1e7e858e..09ee2d265 100755 --- a/samples/swtpm-create-tpmca +++ b/samples/swtpm-create-tpmca @@ -4,6 +4,7 @@ FLAG_OVERWRITE=1 FLAG_REGISTER_KEY=2 FLAG_TPMTOOL_SUPPORTS_SRK_WELL_KNOWN=4 FLAG_SRK_WELL_KNOWN=8 +FLAG_TPM2=16 TSS_TCSD_HOSTNAME_DEFAULT=localhost TSS_TCSD_PORT_DEFAULT=30003 @@ -39,6 +40,14 @@ function get_filesize() fi } +# Create a config value by escaping the proper characters +# +# @param 1: The string to escape +function escape_pkcs11_url() +{ + echo "$1" | sed 's/;/\\;/g' +} + # Use expect for automating the interaction with the tpmtool # # @param 1...: parameters to pass to tpmtool command line @@ -86,7 +95,7 @@ create_localca_cert() { local tpmpubkey=${dir}/swtpm-localca-tpmca-pubkey.pem local tpmca=${dir}/swtpm-localca-tpmca-cert.pem local template=${dir}/template - local tpmkeyurl params + local tpmkeyurl local msg output if ! [ -r "${cakey}" ] || ! [ -r ${cacert} ]; then @@ -131,52 +140,124 @@ create_localca_cert() { rm -f ${tpmkey} ${tpmpubkey} ${tpmca} - if [ $((flags & FLAG_SRK_WELL_KNOWN)) -ne 0 ]; then - unset GNUTLS_PIN - params="--srk-well-known" - else - export GNUTLS_PIN=${TPM_SRK_PASSWORD} - fi - - if [ $((flags & FLAG_REGISTER_KEY)) -ne 0 ]; then - msg="$(run_tpmtool --generate-rsa --signing --register ${params})" - if [ $? -ne 0 ]; then - logerr "Could not generate registered signing key with tpmtool" - logerr "${msg}" - return 1 + if [ $((flags & FLAG_TPM2)) -ne 0 ]; then + local tokenurl tpmkeyurl + local token="swtpm-tpmca" + local label="swtpm-tpmca" + local keylabel="swtpm-tpmca-key" + local userpin=${GNUTLS_PIN:-swtpm-tpmca} + local sopin=${GNUTLS_SO_PIN:-swtpm-tpmca} + + export GNUTLS_PIN=${userpin} + + tokenurl=$(p11tool --list-tokens 2>&1 | \ + grep "token=${token}" | \ + sed -n "s/.*URL: //p") + if [ -z "${tokenurl}" ]; then + msg=$(tpm2_ptool.py addtoken \ + --pid 1 \ + --sopin=${sopin} \ + --userpin=${userpin} \ + --label=${label} 2>&1) + if [ $? -ne 0 ]; then + logerr "Error: Could not create pkcs11 token" + logerr "${msg}" + return 1 + fi + tokenurl=$(p11tool --list-tokens 2>&1 | \ + grep ";token=${token}" | \ + sed -n "s/.*URL: //p") + if [ -z "${tokenurl}" ]; then + logerr "Error: Could not get token URL for token '${token}'" + logerr "${msg}" + fi fi - tpmkeyurl=$(echo "${msg}" | sed -n 's/\(tpmkey:uuid=[^;]*\);.*/\1/p') + + tpmkeyurl=$(p11tool --list-keys ${tokenurl} 2>&1 | \ + grep ";object=${keylabel}" | \ + sed -n "s/.*URL: //p") if [ -z "${tpmkeyurl}" ]; then - logerr "Could not parse tpmkey URL" + msg=$(tpm2_ptool.py addkey \ + --label=${label} \ + --userpin=${userpin} \ + --algorithm=rsa2048 \ + --key-label=${keylabel} \ + --id 1 2>&1) + if [ $? -ne 0 ]; then + logerr "Error: Could not create create key under pkcs11 token ${token}" + logerr "${msg}" + return 1 + fi + tpmkeyurl=$(p11tool --list-keys ${tokenurl} 2>&1 | \ + grep ";object=${keylabel}" | \ + sed -n "s/.*URL: //p") + if [ -z "${tpmkeyurl}" ]; then + logerr "Error: Could not get TPM key URL for ${tokenurl}" + logerr "${msg}" + return 1 + fi + fi + rm -f "${tpmpubkey}" + + msg=$(p11tool --export-pubkey ${tpmkeyurl} --login --outfile ${tpmpubkey} 2>&1) + if [ $? -ne 0 ] || \ + [ ! -r ${tpmpubkey} ] || [ $(get_filesize "${tpmpubkey}") -eq 0 ]; then + logerr "Error: Could not get TPM public key" logerr "${msg}" + rm -f "${tpmkey}" "${tpmpubkey}" return 1 fi else - rm -f "${tpmkey}" - msg="$(run_tpmtool --generate-rsa --signing --outfile "${tpmkey}" ${params})" - if [ $? -ne 0 ]; then - logerr "Could not create signing key with tpmtool" - logerr "${msg}" - rm -f "${tpmkey}" - return 1 + local params="" + + if [ $((flags & FLAG_SRK_WELL_KNOWN)) -ne 0 ]; then + unset GNUTLS_PIN + params="--srk-well-known" + else + export GNUTLS_PIN=${TPM_SRK_PASSWORD} fi - if [ ! -r "${tpmkey}" ] || [ $(get_filesize "${tpmkey}") -eq 0 ]; then - logerr "The TPM key file ${tpmkey} was not written properly" + + if [ $((flags & FLAG_REGISTER_KEY)) -ne 0 ]; then + msg="$(run_tpmtool --generate-rsa --signing --register ${params})" + if [ $? -ne 0 ]; then + logerr "Could not generate registered signing key with tpmtool" + logerr "${msg}" + return 1 + fi + tpmkeyurl=$(echo "${msg}" | sed -n 's/\(tpmkey:uuid=[^;]*\);.*/\1/p') + if [ -z "${tpmkeyurl}" ]; then + logerr "Could not parse tpmkey URL" + logerr "${msg}" + return 1 + fi + else rm -f "${tpmkey}" - return 1 + msg="$(run_tpmtool --generate-rsa --signing --outfile "${tpmkey}" ${params})" + if [ $? -ne 0 ]; then + logerr "Could not create signing key with tpmtool" + logerr "${msg}" + rm -f "${tpmkey}" + return 1 + fi + if [ ! -r "${tpmkey}" ] || [ $(get_filesize "${tpmkey}") -eq 0 ]; then + logerr "The TPM key file ${tpmkey} was not written properly" + logerr "${msg}" + rm -f "${tpmkey}" + return 1 + fi + chmod 640 ${tpmkey} + tpmkeyurl=tpmkey:file=${tpmkey} fi - chmod 640 ${tpmkey} - tpmkeyurl=tpmkey:file=${tpmkey} - fi - rm -f "${tpmpubkey}" - msg=$(run_tpmtool --pubkey=${tpmkeyurl} --outfile "${tpmpubkey}" ${params}) - if [ $? -ne 0 ] || \ - [ ! -r ${tpmpubkey} ] || [ $(get_filesize "${tpmpubkey}") -eq 0 ]; then - logerr "Error: Could not get TPM public key" - logerr "${msg}" - rm -f "${tpmkey}" "${tpmpubkey}" - return 1 + rm -f "${tpmpubkey}" + msg=$(run_tpmtool --pubkey=${tpmkeyurl} --outfile "${tpmpubkey}" ${params}) + if [ $? -ne 0 ] || \ + [ ! -r ${tpmpubkey} ] || [ $(get_filesize "${tpmpubkey}") -eq 0 ]; then + logerr "Error: Could not get TPM public key" + logerr "${msg}" + rm -f "${tpmkey}" "${tpmpubkey}" + return 1 + fi fi echo "cn=swtpm-localca" > ${template} @@ -202,12 +283,17 @@ create_localca_cert() { fi output="statedir = ${dir} -signingkey = ${tpmkeyurl} +signingkey = $(escape_pkcs11_url ${tpmkeyurl}) issuercert = ${tpmca} -certserial = ${dir}/certserial -TSS_TCSD_HOSTNAME = ${TSS_TCSD_HOSTNAME} -TSS_TCSD_PORT = ${TSS_TCSD_PORT}" +certserial = ${dir}/certserial" + if [ $((flags & FLAG_TPM2)) -eq 0 ]; then + output+="$(echo -e "\nTSS_TCSD_HOSTNAME = ${TSS_TCSD_HOSTNAME}")" + output+="$(echo -e "\nTSS_TCSD_PORT = ${TSS_TCSD_PORT}")" + else + output+="$(echo -e "\nGNUTLS_PIN = ${GNUTLS_PIN}")" + output+="$(echo -e "\nGNUTLS_SO_PIN = ${GNUTLS_SO_PIN}")" + fi if [ -n "${TPM_KEY_PASSWORD}" ]; then output+="$(echo -e "\nsigningkey_password = ${TPM_KEY_PASSWORD}")" fi @@ -264,7 +350,8 @@ The following options are supported: --overwrite Overwrite any data in an existing directory; tries to reuse a root CA if one is found there --register Create a registered TPM 1.2 key rather than a file that - contains the key + contains the key; this option has no effect if --tpm2 is + used --key-password s Password for the newly created TPM key; required if --register is not passed Note: use the same as the --srk-password (bug in certtool) @@ -280,6 +367,7 @@ The following options are supported: on; default is '${TSS_TCSD_HOSTNAME_DEFAULT}' --tss-tcsd-port p The TCP port on which tcsd is listening for connections; default is ${TSS_TCSD_PORT_DEFAULT} +--tpm2 Setup a CA that uses a TPM2 rather than a TPM 1.2 --help, -h, -? Display this help screen and exit @@ -353,6 +441,9 @@ main() { shift TSS_TCSD_PORT="$1" ;; + --tpm2) + flags=$((flags | FLAG_TPM2)) + ;; --help|-h|-?) usage "$0" "${flags}" exit 0 @@ -376,13 +467,15 @@ main() { return 1 fi - if [ -z "${TPM_SRK_PASSWORD}" ] && \ + if [ -z "${TPM_SRK_PASSWORD}" ] && [ $((flags & FLAG_TPM2)) -eq 0 ] && [ $((flags & FLAG_TPMTOOL_SUPPORTS_SRK_WELL_KNOWN)) -eq 0 ]; then logerr "SRK password must be provided" return 1 fi - if [ -z "${TPM_KEY_PASSWORD}" ] && [ $((flags & FLAG_REGISTER_KEY)) -eq 0 ]; then + if [ -z "${TPM_KEY_PASSWORD}" ] && \ + [ $((flags & FLAG_REGISTER_KEY)) -eq 0 ] && \ + [ $((flags & FLAG_TPM2)) -eq 0 ]; then logerr "Key password is required" return 1 fi diff --git a/samples/swtpm-localca b/samples/swtpm-localca index c31a1e8ef..2f186cb8e 100755 --- a/samples/swtpm-localca +++ b/samples/swtpm-localca @@ -559,7 +559,18 @@ main() { esac # TPM keys are GNUTLS URLs... - if ! [[ "$SIGNKEY" =~ ^tpmkey:(uuid|file)= ]]; then + if [[ "$SIGNKEY" =~ ^tpmkey:(uuid|file)= ]]; then + export TSS_TCSD_HOSTNAME=$(get_config_value "$LOCALCA_CONFIG" \ + "TSS_TCSD_HOSTNAME" "localhost") + export TSS_TCSD_PORT=$(get_config_value "$LOCALCA_CONFIG" \ + "TSS_TCSD_PORT" "30003") + logit "CA uses a GnuTLS TPM key; using TSS_TCSD_HOSTNAME=${TSS_TCSD_HOSTNAME}" \ + "TSS_TCSD_PORT=${TSS_TCSD_PORT}" + elif [[ "$SIGNKEY" =~ ^pkcs11: ]]; then + export GNUTLS_PIN=$(get_config_value "$LOCALCA_CONFIG" \ + "GNUTLS_PIN" "swtpm-tpmca") + logit "CA uses a GnuTLS PKCS#11 key; using GNUTLS_PIN" + else if [ ! -r "$SIGNKEY" ]; then if [ -f "$SIGNKEY" ]; then logerr "Signing key $SIGNKEY exists but cannot access" \ @@ -579,13 +590,6 @@ main() { logerr "Cannot access signing key ${SIGNKEY}." exit 1 fi - else - export TSS_TCSD_HOSTNAME=$(get_config_value "$LOCALCA_CONFIG" \ - "TSS_TCSD_HOSTNAME" "localhost") - export TSS_TCSD_PORT=$(get_config_value "$LOCALCA_CONFIG" \ - "TSS_TCSD_PORT" "30003") - logit "CA uses a GnuTLS TPM key; using TSS_TCSD_HOSTNAME=${TSS_TCSD_HOSTNAME}" \ - "TSS_TCSD_PORT=${TSS_TCSD_PORT}" fi if [ ! -r "$ISSUERCERT" ]; then