This is a TPM utility and some scripts I wrote while experimenting with my TPM and trying to get a TPM quote operation to work. The tool is both a C++ wrapper around TrouSerS (the TpmSession class) and a command line utility that combines some of the operations also provided by tpm-tools and tpm-quote-tools.
This utility is more verbose and user friendly (I think) and also supports password authentication more correctly than a number of the official tools. Also these tools can help you to convert TMP public keys into OpenSSL PEM or DER format.
This is a simple autotool based project. Prerequisites are:
- a C++ compiler
- openssl-devel
- trousers-devel (libtspci)
- tclap command line parsing library
This software is in a prototypical state to guide as documentation and an example but not for production use.
Remote attestation means that the TPM is testifying a certain state of PCRS at a certain time to another (remote) host. To do this a public/private RSA key pair is used for signing and verifying a message generated by the TPM.
Since only the TPM itself knows the private key used in the process it also a way to prove the identity of the host.
In TPM spec jargon this operation is called a TPM quote.
The TPM needs to be correctly initialized and ownership needs to have been
taken via tpm-tools commands like tpm_takeownership
. I've only tested this
with passphrases set for TPM and SRK (both to the same) not with the "well
known secret". Implementing the latter in the tool should be no big deal,
however.
We need to create a key in the TPM that can be used for signing during the
quote operation in the remote attestation process. The quote operation is the
act of signing a set of PCRs internally by the TPM. The tpm_util
can create
a key like this:
tpm_util --create-key --key-id <ID> -t signing
The <ID>
can be any unique number for identifying the key in subsequent
operations. There is also a special key type "identity" which is supposedly
present exactly for the remote attestation feature. However it fails in my
case with "Invalid key usage". Upstream
discussion about
this lead to nothing.
Using a signing type key works too, however.
Using the signing key we just created we can now ask for a quote operation to be performed. Like this:
tpm_util --get-quote -u <ID> --pcrs 1-8
Here you need to specify the same key ID as during the creation step, of course. The PCR selection tells the TPM which PCRS should be hashed for this quote operation.
The output on stdout then presents us with the hexadecimal encoded PCR
composite hash and the RSA 2048 bit signature, performed with the newly
created signing key. The signature is for the PCR composite hash data. Both
pieces of data are also written to files in the current directory in
quote_hash.txt
and quote_signature.txt
.
The quote operation also utilizes a nonce which is necessary to prevent
replay attacks and should be a random value of high quality that doesn't
repeat. Currently this nonce value is fixed within tpm_util
and needs to be
replaced by a command line parameter for real life operation.
The PCR composite hash data stored in quote_hash.txt
consists of some binary
data like:
- the TPM version numbers
- the hash of the contents of the selected PCRS
- the nonce used as input of the quote operation
Extracting this information is not easily possible. The nonce makes up the last 20 bytes of the data. For verification you should perform a quote operation during provisioning, when all PCRS have the desired values and use a well defined nonce (like all 00 or all FF). This initial quote data should be safely stored on the remote end that whishes to perform remote attestation later on.
Then when you actually want to do remote attestation you can send a high quality nonce to the machine and there the quote operation can be carried out. Once you get the quote data back you can insert the actual nonce in the original quote data you obtained during provisioning and thus compare if the values of TPM version and selected PCRS are still the same and the nonce matches. Then you can verify the signature. Or the other way around.
The signature verification can be done using any capable RSA library that can
read the public key format. To fetch the public signing key from the tpm the
tpm_util
can be used:
tpm_util --fetch-key -u <id>
This will write various key blobs and an OpenSSL public key file to the current working directory. The key blobs can only be understood by the TrouSerS stack / the TPM itself. It's a format specific to the TPM. It is specified in the specs but difficult to parse and other tools like OpenSSL can't parse it (except the openssl-tpm-engine).
The OpenSSL public PEM key can be used by OpenSSL and many other tools,
however. The other key blobs can be used to load the key into the same or some
other TPM again, under some UUID. For this tpm_util -r <file> -u <id>
can be
used.
To verify a quote the accompanying script verify_quote.py
can be used at the
moment:
verify_quote.py --key openssl.<ID>.public.pem --message quote_hash.txt --signature quote_signature.txt
This does only check the signature, however, not the correct PCR values (because there's no reference).
For a real life implementation the provisioning process and a network protocol
of some sort needs to be devised. The tcsd
also supports listening on
public network interfaces but somehow I doubt doing this would be a good
(safe) idea. A dedicated, small network channel only for doing the attestation
related communication would be my recommendation.
The way TrouSerS / TPM 1.2 and the tpm-quote-tools are designed it seems they want to perform the signature verification on the other end also via some TPM. I don't think this is what we want ... verifying on the host in software would be more flexible when dealing with a large number of remote peers.
My TPM for some reasons enters a dictionary attack protection mode, even if
the password is always entered correctly, after a couple of times of entering
the passphrase. You can get out of this mode by using the command
tpm_resetdalock
from tpm-tools.
For not having to enter the passphrases countless of times you can also use
the environment variables SRK_PASS
and TPM_PASS
. the tpm_util
will use
them automatically instead of querying the passphrase interactively.