I do not have any code to verify Quote. In practice there are a number
of issues relating to verify Quote operations that go beyond just the
cryptographic signature verification. I wrote somethiing on the
Trousers Wiki several years ago, but it went away. Here is what I
wrote:

There are several aspects to Tspi_TPM_Quote() that are a bit tricky.

The Quote operation issues a signature over one or more PCRs as well
as some external data.  The signature must be issued by a TPM key of
type Signing, Identity, or Legacy.  It is anticipated that Quotes will
often be issued by Identity keys because these are the ones that can
be most directly and simply proven to be TPM keys.  Only Quotes by TPM
keys can be trusted to correctly sign current PCR values.  Identity
keys can be proven to be TPM keys by offering an AIK Credential.
Alternatively, an Identity Key proven by an AIK Credential can then
certify a Signing or Legacy key as being a TPM key, using
TSPI_Key_CertifyKey().

In order to prove to the verifier that the Quoted PCR values are up to
date, the Quote signature covers some external data, at least part of
which is intended to be provided by the verifier.  In the simplest
case, the verifier provides the extra data as a 20-byte nonce, and
this gets signed in the Quote operation along with the PCRs.  By
picking a new random 20-byte nonce each time before receiving a Quote
from a TPM platform, the verifier is assured that the Quote operation
is "fresh" and is not a replay of an earlier Quote, possibly even from
another platform.

Another option is for the signer to combine the verifier-supplied
nonce with some additional data that the signer wants to sign, to hash
that combination, and then to use the result of that hash as the
external data in the Quote operation.  In the latter case, the signer
must supply the additional signed data to the verifier so he can
verify the Quote operation.

The signer should construct a TSS_HPCRS object before calling
Tspi_TPM_Quote() by calling Tspi_PcrComposite_SelectPcrIndex() once
per PCR that should be signed, setting each PCR index sequentially
into the TSS_HPCRS object.  Then, after the Quote, the caller can use
Tspi_PcrComposite_GetPcrValue() for each signed PCR to get the value
that was actually signed in the Quote operation.  It will not
necessarily work to read the PCRs directly with Tspi_TPM_PcrRead()
either just before or just after the Quote, as theoretically some
other process could have run and changed those values before or after
the Quote is done.  Only Tspi_PcrComposite_GetPcrValue() is guaranteed
to return the values that were Quoted and which will hash correctly
(once the formatting is guessed) to match the compositeHash field.

Tspi_TPM_Quote() takes a TSS_VALIDATION structure which has one input
buffer and two output buffers.  The input buffer is rgbExternalData
and is the external data described above, which should be 20 bytes
long.  The output buffers are rgbData and rgbValidationData.  rgbData
is a TCPA_QUOTE_INFO struct which gets signed.  rgbValidationData is
the RSA signature over the rgbData buffer.  In addition, as just
mentioned, the TSS_HPCRS object gets updated on return to hold the PCR
values that were signed.

Verifying a Quote operation requires several pieces of information,
much of which is beyond what is returned by the Tspi_TPM_Quote() call.
 Here is what is needed:

   * A list of the PCRs which are Quoted.
   * The PCR values for each PCR in the list.
   * The external data, along with any signer-supplied data that may
have been hashed to create this value.
   * The RSA modulus of the signing public key.
   * The RSA signature over the Quoted data (from Tspi_TPM_Quote()'s
rgbValidationData return).
   * The TCPA_QUOTE_INFO struct which gets signed (from
Tspi_TPM_Quote()'s rgbData return).
   * Optionally, any certificates and signatures necessary to prove
that the signing key is a TPM key.

To verify the Quote operation the following steps should be performed.
 The order doesn't matter much, all tests should be done.

   * Validate any certificates or signatures offered by the TPM
platform to establish that the key issuing the Quote signature is a
valid TPM key.

   * Verify the signature using an RSA library.  We have the RSA
modulus of the signing key; TCG exponents are always 0x10001 (decimal
65537).  SHA-1 hash the rgbData buffer from the TSS_VALIDATION
structure and compare it with the value signed in the
rgbValidationData buffer from that same struct (which will use PKCS-1
v1.5 padding).  They should match and the signature should be a valid
RSA signature by that key.

   * Verify that the fixed field of TCPA_QUOTE_INFO (which is the
rgbData buffer from the TSS_VALIDATION struct) holds the four
characters 'QUOT'.

   * Verify that the externalData field of TCPA_QUOTE_INFO holds the
expected external data, and if this external data is the hash of some
other data that was to be signed along with the nonce, verify that
hash.

   * Verify that the version.major and version.minor fields of
TCPA_QUOTE_INFO are 1 and 1.  This should be true for both 1.1 and 1.2
TPMs.  (It's not 100% clear what all the different 1.1 TPMs put in
this field.)

   * Verify that the compositeHash field of TCPA_QUOTE_INFO matches
the hash of the PCR values.  This requires formatting the PCR data as
a serialized TCPA_PCR_COMPOSITE structure and hashing it.
Unfortunately there is an ambiguity in this step regarding the value
in the select.sizeOfSelect field, which tells how many bytes long is
the bit field that indicates which PCRs are being signed.  It's
supposed to be as long as the maximum number of PCRs supported on the
signing system, but the verifier doesn't necessarily know that value.
Probably the best policy is for the signer to report the PCR
information to the verifier in the form of a properly formatted,
serialized TCPA_PCR_COMPOSITE structure.  This will have two bytes
which indicate the length of the bit field; the bit field itself; four
bytes to indicate the length of the PCR data (which will equal 20
times the number of PCRs being signed); and then the PCR data, 20
bytes per PCR.  This buffer should be hashed and the hash compared to
the compositeHash field of TCPA_QUOTE_INFO.  That confirms that the
PCR data is what has been signed.

This last step is further complicated by the fact that not even the
signer may know precisely how the TCPA_PCR_COMPOSITE structure was
formatted when the TPM signed it.  Ironically, the low-level
TPM_Quote() operation does return this struct in precisely the correct
format, but the TSS does not return this data and the detailed
formatting is lost.  Probably the signer should query the
TSS_TPMCAP_PROP_PCR property of the TPM to find the number of PCRs
supported, and upon return from Tspi_TPM_Quote() try formatting a
TCPA_PCR_COMPOSITE using this value, then hash that and compare with
the compositeHash field of the TCPA_QUOTE_INFO.  If they don't match,
the signer can try different lengths of the bit field until it finds
one that matches.  Then it can report this properly formatted
structure to the verifier.

A final note: it's rather odd that Tspi_TPM_Quote() returns the
TCPA_QUOTE_INFO at all, since essentially all the data in it is known
from other sources.  The external data has to be known independently;
the 'QUOT' field is known; the compositeHash field which holds a hash
of the PCRs has to be calculated independently as described above;
only the version field is slightly ambiguous.  Ideally a fixed value
would be used there and indeed the 1.2 TPM spec says that it should be
1.1.0.0.  With that convention, TCPA_QUOTE_INFO becomes completely
redundant and can be reconstructed by the verifier once provided with
the other data that he needs anyway.  Then he can verify the RSA
signature over his constructed TCPA_QUOTE_INFO and get just as much
security as in the present design.

Both the choice to return the TCPA_QUOTE_INFO and the loss of the
TCPA_PCR_COMPOSITE formatting are due to the design of the TSS rather
than the TPM.  The TPM does not return TCPA_QUOTE_INFO and does return
the TCPA_PCR_COMPOSITE formatting.  It's too bad that the TSS did not
stick a little closer to the TPM functionality in this case.
(Actually this raises the question of how the TSS knows what version
field to return in TCPA_QUOTE_INFO!  Different TPMs might do slightly
different things there.)

Also note that the TSS 1.2 function Tspi_TPM_Quote2() addresses most
of these problems and will require much less guesswork in creating a
reliably verifiable Quote operation.

------------------------------------------------------------------------------
Join us December 9, 2009 for the Red Hat Virtual Experience,
a free event focused on virtualization and cloud computing. 
Attend in-depth sessions from your desk. Your couch. Anywhere.
http://p.sf.net/sfu/redhat-sfdev2dev
_______________________________________________
TrouSerS-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/trousers-users

Reply via email to