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
