1 /* Software-based Trusted Platform Module (TPM) Emulator
2  * Copyright (C) 2004-2010 Mario Strasser <mast@gmx.net>
3  *
4  * This module is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published
6  * by the Free Software Foundation; either version 2 of the License,
7  * or (at your option) any later version.
8  *
9  * This module is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * $Id: tpm_integrity.c 474 2011-12-20 10:27:45Z mast $
15  */
16 
17 #include "tpm_emulator.h"
18 #include "tpm_commands.h"
19 #include "tpm_data.h"
20 #include "crypto/sha1.h"
21 #include "crypto/rsa.h"
22 #include "tpm_handles.h"
23 #include "tpm_marshalling.h"
24 
25 /*
26  * Integrity Collection and Reporting ([TPM_Part3], Section 16)
27  * This section deals with what commands have direct access to the PCR.
28  */
29 
30 #define PCR_ATTRIB     tpmData.permanent.data.pcrAttrib
31 #define PCR_VALUE      tpmData.permanent.data.pcrValue
32 #define LOCALITY       tpmData.stany.flags.localityModifier
33 
TPM_Extend(TPM_PCRINDEX pcrNum,TPM_DIGEST * inDigest,TPM_PCRVALUE * outDigest)34 TPM_RESULT TPM_Extend(TPM_PCRINDEX pcrNum, TPM_DIGEST *inDigest,
35                       TPM_PCRVALUE *outDigest)
36 {
37   tpm_sha1_ctx_t ctx;
38 
39   info("TPM_Extend()");
40   if (pcrNum >= TPM_NUM_PCR) return TPM_BADINDEX;
41   if (!(PCR_ATTRIB[pcrNum].pcrExtendLocal & (1 << LOCALITY))) return TPM_BAD_LOCALITY;
42   /* compute new PCR value as SHA-1(old PCR value || inDigest) */
43   tpm_sha1_init(&ctx);
44   tpm_sha1_update(&ctx, PCR_VALUE[pcrNum].digest, sizeof(PCR_VALUE[pcrNum].digest));
45   tpm_sha1_update(&ctx, inDigest->digest, sizeof(inDigest->digest));
46   tpm_sha1_final(&ctx, PCR_VALUE[pcrNum].digest);
47   /* set output digest */
48   if (tpmData.permanent.flags.disable) {
49     memset(outDigest->digest, 0, sizeof(*outDigest->digest));
50   } else {
51     memcpy(outDigest, &PCR_VALUE[pcrNum], sizeof(TPM_PCRVALUE));
52   }
53   return TPM_SUCCESS;
54 }
55 
TPM_PCRRead(TPM_PCRINDEX pcrIndex,TPM_PCRVALUE * outDigest)56 TPM_RESULT TPM_PCRRead(TPM_PCRINDEX pcrIndex, TPM_PCRVALUE *outDigest)
57 {
58   info("TPM_PCRRead()");
59   if (pcrIndex >= TPM_NUM_PCR) return TPM_BADINDEX;
60   memcpy(outDigest, &PCR_VALUE[pcrIndex], sizeof(TPM_PCRVALUE));
61   return TPM_SUCCESS;
62 }
63 
TPM_Quote(TPM_KEY_HANDLE keyHandle,TPM_NONCE * extrnalData,TPM_PCR_SELECTION * targetPCR,TPM_AUTH * auth1,TPM_PCR_COMPOSITE * pcrData,UINT32 * sigSize,BYTE ** sig)64 TPM_RESULT TPM_Quote(TPM_KEY_HANDLE keyHandle, TPM_NONCE *extrnalData,
65                      TPM_PCR_SELECTION *targetPCR, TPM_AUTH *auth1,
66                      TPM_PCR_COMPOSITE *pcrData,
67                      UINT32 *sigSize, BYTE **sig)
68 {
69   TPM_RESULT res;
70   TPM_KEY_DATA *key;
71   TPM_COMPOSITE_HASH hash;
72   BYTE buf[48];
73   info("TPM_Quote()");
74   /* get key */
75   key = tpm_get_key(keyHandle);
76   if (key == NULL) return TPM_INVALID_KEYHANDLE;
77   /* verify authorization */
78   if (auth1->authHandle != TPM_INVALID_HANDLE
79       || key->authDataUsage != TPM_AUTH_NEVER) {
80     res = tpm_verify_auth(auth1, key->usageAuth, keyHandle);
81     if (res != TPM_SUCCESS) return res;
82   }
83   if (key->sigScheme != TPM_SS_RSASSAPKCS1v15_SHA1)
84     return TPM_INAPPROPRIATE_SIG;
85   if (key->keyUsage != TPM_KEY_SIGNING && key->keyUsage != TPM_KEY_LEGACY
86       && key->keyUsage != TPM_KEY_IDENTITY)
87     return TPM_INVALID_KEYUSAGE;
88   /* compute composite hash */
89   res = tpm_compute_pcr_digest(targetPCR, &hash, pcrData);
90   if (res != TPM_SUCCESS) return res;
91   /* setup quote info and sign it */
92   memcpy(&buf[ 0], "\x01\x01\x00\x00QUOT", 8);
93   memcpy(&buf[ 8], hash.digest, 20);
94   memcpy(&buf[28], extrnalData->nonce, 20);
95   *sigSize = key->key.size >> 3;
96   *sig = tpm_malloc(*sigSize);
97   if (*sig == NULL) return TPM_FAIL;
98   if (tpm_rsa_sign(&key->key, RSA_SSA_PKCS1_SHA1, buf, 48, *sig)) {
99     tpm_free(*sig);
100     return TPM_FAIL;
101   }
102   return TPM_SUCCESS;
103 }
104 
TPM_PCR_Reset(TPM_PCR_SELECTION * pcrSelection)105 TPM_RESULT TPM_PCR_Reset(TPM_PCR_SELECTION *pcrSelection)
106 {
107   int i;
108   info("TPM_PCR_Reset()");
109   if ((pcrSelection->sizeOfSelect * 8) > TPM_NUM_PCR)
110     return TPM_INVALID_PCR_INFO;
111   /* this command must be atomic, thus we first verify that all
112      registers are resetable ... */
113   for (i = 0; i < pcrSelection->sizeOfSelect * 8; i++) {
114     /* is PCR number i selected ? */
115     if (pcrSelection->pcrSelect[i >> 3] & (1 << (i & 7))) {
116       if (!PCR_ATTRIB[i].pcrReset) return TPM_NOTRESETABLE;
117       if (!(PCR_ATTRIB[i].pcrResetLocal & (1 << LOCALITY))) return TPM_NOTLOCAL;
118     }
119   }
120   /* ... then we reset all registers at once */
121   for (i = 0; i < pcrSelection->sizeOfSelect * 8; i++) {
122     /* is PCR number i selected ? */
123     if (pcrSelection->pcrSelect[i >> 3] & (1 << (i & 7))) {
124       memset(PCR_VALUE[i].digest, 0, sizeof(PCR_VALUE[i].digest));
125     }
126   }
127   return TPM_SUCCESS;
128 }
129 
tpm_compute_pcr_digest(TPM_PCR_SELECTION * pcrSelection,TPM_COMPOSITE_HASH * digest,TPM_PCR_COMPOSITE * composite)130 TPM_RESULT tpm_compute_pcr_digest(TPM_PCR_SELECTION *pcrSelection,
131                                   TPM_COMPOSITE_HASH *digest,
132                                   TPM_PCR_COMPOSITE *composite)
133 {
134   int i,j;
135   TPM_PCR_COMPOSITE comp;
136   tpm_sha1_ctx_t ctx;
137   UINT32 len;
138   BYTE *buf, *ptr;
139   info("tpm_compute_pcr_digest()");
140   /* create PCR composite */
141   if ((pcrSelection->sizeOfSelect * 8) > TPM_NUM_PCR
142       || pcrSelection->sizeOfSelect == 0) return TPM_INVALID_PCR_INFO;
143   for (i = 0, j = 0; i < pcrSelection->sizeOfSelect * 8; i++) {
144     /* is PCR number i selected ? */
145     if (pcrSelection->pcrSelect[i >> 3] & (1 << (i & 7))) {
146       memcpy(&comp.pcrValue[j++], &PCR_VALUE[i], sizeof(TPM_PCRVALUE));
147     }
148   }
149   memcpy(&comp.select, pcrSelection, sizeof(TPM_PCR_SELECTION));
150   comp.valueSize = j * sizeof(TPM_PCRVALUE);
151   debug("comp.valueSize = %d", comp.valueSize);
152   if (comp.valueSize > 0) {
153     /* marshal composite and compute hash */
154     len = sizeof_TPM_PCR_COMPOSITE(comp);
155     buf = ptr = tpm_malloc(len);
156     if (buf == NULL
157         || tpm_marshal_TPM_PCR_COMPOSITE(&ptr, &len, &comp)) {
158        tpm_free(buf);
159        return TPM_FAIL;
160     }
161     tpm_sha1_init(&ctx);
162     tpm_sha1_update(&ctx, buf, sizeof_TPM_PCR_COMPOSITE(comp));
163     tpm_sha1_final(&ctx, digest->digest);
164     tpm_free(buf);
165   } else {
166     memset(digest, 0, sizeof(TPM_COMPOSITE_HASH));
167   }
168   /* copy composite if requested */
169   if (composite != NULL)
170     memcpy(composite, &comp, sizeof(TPM_PCR_COMPOSITE));
171   return TPM_SUCCESS;
172 }
173 
tpm_verify_pcr(TPM_KEY_DATA * key,BOOL atrelease,BOOL atcreation)174 TPM_RESULT tpm_verify_pcr(TPM_KEY_DATA *key, BOOL atrelease, BOOL atcreation)
175 {
176   TPM_RESULT res;
177   TPM_COMPOSITE_HASH digest;
178   info("tpm_verify_pcr()");
179   if (atrelease) {
180     res = tpm_compute_pcr_digest(&key->pcrInfo.releasePCRSelection,
181                                  &digest, NULL);
182     if (res != TPM_SUCCESS) return res;
183     if (memcmp(&digest, &key->pcrInfo.digestAtRelease,
184         sizeof(TPM_COMPOSITE_HASH))) return TPM_WRONGPCRVAL;
185     if (key->pcrInfo.tag == TPM_TAG_PCR_INFO_LONG
186         && !(key->pcrInfo.localityAtRelease
187              & (1 << tpmData.stany.flags.localityModifier)))
188       return TPM_BAD_LOCALITY;
189   }
190   if (atcreation) {
191     res = tpm_compute_pcr_digest(&key->pcrInfo.creationPCRSelection,
192                                  &digest, NULL);
193     if (res != TPM_SUCCESS) return res;
194     if (memcmp(&digest, &key->pcrInfo.digestAtCreation,
195         sizeof(TPM_COMPOSITE_HASH))) return TPM_WRONGPCRVAL;
196     if (key->pcrInfo.tag == TPM_TAG_PCR_INFO_LONG
197         && !(key->pcrInfo.localityAtCreation
198              & (1 << tpmData.stany.flags.localityModifier)))
199       return TPM_BAD_LOCALITY;
200   }
201   return TPM_SUCCESS;
202 }
203 
TPM_Quote2(TPM_KEY_HANDLE keyHandle,TPM_NONCE * externalData,TPM_PCR_SELECTION * targetPCR,BOOL addVersion,TPM_AUTH * auth1,TPM_PCR_INFO_SHORT * pcrData,UINT32 * versionInfoSize,TPM_CAP_VERSION_INFO * versionInfo,UINT32 * sigSize,BYTE ** sig)204 TPM_RESULT TPM_Quote2(TPM_KEY_HANDLE keyHandle, TPM_NONCE *externalData,
205                       TPM_PCR_SELECTION *targetPCR, BOOL addVersion,
206                       TPM_AUTH *auth1, TPM_PCR_INFO_SHORT *pcrData,
207                       UINT32 *versionInfoSize,
208                       TPM_CAP_VERSION_INFO *versionInfo,
209                       UINT32 *sigSize, BYTE **sig)
210 {
211   TPM_RESULT res;
212   TPM_KEY_DATA *key;
213   TPM_COMPOSITE_HASH H1;
214   TPM_QUOTE_INFO2 Q1;
215   tpm_sha1_ctx_t ctx;
216   TPM_DIGEST digest;
217   UINT32 respSize, len, size;
218   BYTE *resp, *ptr, *buf;
219 
220   info("TPM_Quote2()");
221   /* get key by keyHandle*/
222   key = tpm_get_key(keyHandle);
223   if (key == NULL) return TPM_INVALID_KEYHANDLE;
224   /* 1. The TPM MUST validate the AuthData to use the key pointed
225    *    to by keyhandle */
226   if (auth1->authHandle != TPM_INVALID_HANDLE
227       || key->authDataUsage != TPM_AUTH_NEVER) {
228     res = tpm_verify_auth(auth1, key->usageAuth, keyHandle);
229     if (res != TPM_SUCCESS) return res;
230   }
231   /* 2. Validate that keyHandle->sigScheme is TPM_SS_RSASSAPKCS1v15_SHA1 or
232         TPM_SS_RSASSAPKCS1v15_INFO, if not return TPM_INAPPROPRIATE_SIG. */
233   if ((key->sigScheme != TPM_SS_RSASSAPKCS1v15_SHA1) &&
234        (key->sigScheme != TPM_SS_RSASSAPKCS1v15_INFO))
235     return TPM_INAPPROPRIATE_SIG;
236   /* 3. Validate that keyHandle->keyUsage is TPM_KEY_SIGNING, TPM_KEY_IDENTITY,
237         or TPM_KEY_LEGACY, if not return TPM_INVALID_KEYUSAGE */
238   if ((key->keyUsage != TPM_KEY_SIGNING) && (key->keyUsage != TPM_KEY_LEGACY)
239       && (key->keyUsage != TPM_KEY_IDENTITY))
240     return TPM_INVALID_KEYUSAGE;
241   /* 4. Validate targetPCR is a valid TPM_PCR_SELECTION structure,
242    *    on errors return TPM_INVALID_PCR_INFO */
243   if (targetPCR->sizeOfSelect > sizeof(targetPCR->pcrSelect))
244     return TPM_INVALID_PCR_INFO;
245   /* 5. Create H1 a SHA-1 hash of a TPM_PCR_COMPOSITE using the
246    *    TPM_STCLEAR_DATA->PCR[] indicated by targetPCR->pcrSelect */
247   res = tpm_compute_pcr_digest(targetPCR, &H1, NULL);
248   if (res != TPM_SUCCESS) return res;
249   /* 6. Create S1 a TPM_PCR_INFO_SHORT */
250     /* a. Set S1->pcrSelection to targetPCR */
251     pcrData->pcrSelection.sizeOfSelect = targetPCR->sizeOfSelect;
252     memcpy(pcrData->pcrSelection.pcrSelect, targetPCR->pcrSelect, targetPCR->sizeOfSelect);
253     /* b. Set S1->localityAtRelease to TPM_STANY_DATA -> localityModifier */
254     pcrData->localityAtRelease = 1 << tpmData.stany.flags.localityModifier;
255     /* c. Set S1->digestAtRelease to H1 */
256     memcpy(&pcrData->digestAtRelease, &H1, sizeof(TPM_COMPOSITE_HASH));
257   /* 7. Create Q1 a TPM_QUOTE_INFO2 structure */
258   Q1.tag = TPM_TAG_QUOTE_INFO2;
259     /* a. Set Q1->fixed to "QUT2" */
260     Q1.fixed[0] = 'Q', Q1.fixed[1] = 'U', Q1.fixed[2] = 'T', Q1.fixed[3] = '2';
261     /* b. Set Q1->infoShort to S1 */
262     Q1.infoShort.pcrSelection.sizeOfSelect = pcrData->pcrSelection.sizeOfSelect;
263     memcpy(Q1.infoShort.pcrSelection.pcrSelect,
264       pcrData->pcrSelection.pcrSelect, pcrData->pcrSelection.sizeOfSelect);
265     Q1.infoShort.localityAtRelease = pcrData->localityAtRelease;
266     memcpy(Q1.infoShort.digestAtRelease.digest,
267       pcrData->digestAtRelease.digest, sizeof(TPM_COMPOSITE_HASH));
268     /* c. Set Q1->externalData to externalData */
269     memcpy(&Q1.externalData, externalData, sizeof(TPM_NONCE));
270   size = len = sizeof_TPM_QUOTE_INFO2(Q1);
271   buf = ptr = tpm_malloc(size);
272   if (buf == NULL) return TPM_NOSPACE;
273   if (tpm_marshal_TPM_QUOTE_INFO2(&ptr, &len, &Q1) || (len != 0)) {
274     debug("TPM_Quote2(): tpm_marshal_TPM_QUOTE_INFO2() failed.");
275     tpm_free(buf);
276     return TPM_FAIL;
277   }
278   /* 8. If addVersion is TRUE */
279   if (addVersion == TRUE) {
280     debug("TPM_Quote2(): addVersion == TRUE");
281     /* a. Concatenate to Q1 a TPM_CAP_VERSION_INFO structure */
282     res = TPM_GetCapability(TPM_CAP_VERSION_VAL, 0, NULL, &respSize, &resp);
283     if (res != TPM_SUCCESS) {
284       debug("TPM_Quote2(): cap_version_val() failed.");
285       tpm_free(buf);
286       return TPM_FAIL;
287     }
288     /* b. Set the output parameters for versionInfo */
289     ptr = resp;
290     len = respSize;
291     if (tpm_unmarshal_TPM_CAP_VERSION_INFO(&ptr, &len, versionInfo) ||
292       (len != 0)) {
293         debug("TPM_Quote2(): tpm_unmarshal_TPM_CAP_VERSION_INFO() failed.");
294         tpm_free(buf);
295         return TPM_FAIL;
296     }
297     *versionInfoSize = respSize;
298    } else { /* 9. Else */
299     debug("TPM_Quote2(): addVersion == FALSE");
300     /* a. Set versionInfoSize to 0 */
301     *versionInfoSize = 0;
302     /* b. Return no bytes in versionInfo */
303   }
304   /* 10. Sign a SHA-1 hash of Q1 using keyHandle as the signature key */
305   tpm_sha1_init(&ctx);
306   tpm_sha1_update(&ctx, buf, size);
307   tpm_free(buf);
308   if (addVersion == TRUE) {
309     tpm_sha1_update(&ctx, resp, respSize);
310     tpm_free(resp);
311   }
312   tpm_sha1_final(&ctx, digest.digest);
313   /* 11. Return the signature in sig */
314   return tpm_sign(key, auth1, FALSE, digest.digest, sizeof(TPM_DIGEST), sig, sigSize);
315 }
316