xref: /aosp_15_r20/external/ms-tpm-20-ref/TPMCmd/tpm/src/support/PropertyCap.c (revision 5c591343844d1f9da7da26467c4bf7efc8a7a413)
1 /* Microsoft Reference Implementation for TPM 2.0
2  *
3  *  The copyright in this software is being made available under the BSD License,
4  *  included below. This software may be subject to other third party and
5  *  contributor rights, including patent rights, and no such rights are granted
6  *  under this license.
7  *
8  *  Copyright (c) Microsoft Corporation
9  *
10  *  All rights reserved.
11  *
12  *  BSD License
13  *
14  *  Redistribution and use in source and binary forms, with or without modification,
15  *  are permitted provided that the following conditions are met:
16  *
17  *  Redistributions of source code must retain the above copyright notice, this list
18  *  of conditions and the following disclaimer.
19  *
20  *  Redistributions in binary form must reproduce the above copyright notice, this
21  *  list of conditions and the following disclaimer in the documentation and/or
22  *  other materials provided with the distribution.
23  *
24  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
25  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27  *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
28  *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29  *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
31  *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 //** Description
36 // This file contains the functions that are used for accessing the
37 // TPM_CAP_TPM_PROPERTY values.
38 
39 //** Includes
40 
41 #include "Tpm.h"
42 
43 //** Functions
44 
45 //*** TPMPropertyIsDefined()
46 // This function accepts a property selection and, if so, sets 'value'
47 // to the value of the property.
48 //
49 // All the fixed values are vendor dependent or determined by a
50 // platform-specific specification. The values in the table below
51 // are examples and should be changed by the vendor.
52 //  Return Type: BOOL
53 //      TRUE(1)         referenced property exists and 'value' set
54 //      FALSE(0)        referenced property does not exist
55 static BOOL
TPMPropertyIsDefined(TPM_PT property,UINT32 * value)56 TPMPropertyIsDefined(
57     TPM_PT           property,      // IN: property
58     UINT32          *value          // OUT: property value
59     )
60 {
61     switch(property)
62     {
63         case TPM_PT_FAMILY_INDICATOR:
64             // from the title page of the specification
65             // For this specification, the value is "2.0".
66             *value = TPM_SPEC_FAMILY;
67             break;
68         case TPM_PT_LEVEL:
69             // from the title page of the specification
70             *value = TPM_SPEC_LEVEL;
71             break;
72         case TPM_PT_REVISION:
73             // from the title page of the specification
74             *value = TPM_SPEC_VERSION;
75             break;
76         case TPM_PT_DAY_OF_YEAR:
77             // computed from the date value on the title page of the specification
78             *value = TPM_SPEC_DAY_OF_YEAR;
79             break;
80         case TPM_PT_YEAR:
81             // from the title page of the specification
82             *value = TPM_SPEC_YEAR;
83             break;
84         case TPM_PT_MANUFACTURER:
85             // vendor ID unique to each TPM manufacturer
86             *value = BYTE_ARRAY_TO_UINT32(MANUFACTURER);
87             break;
88         case TPM_PT_VENDOR_STRING_1:
89             // first four characters of the vendor ID string
90             *value = BYTE_ARRAY_TO_UINT32(VENDOR_STRING_1);
91             break;
92         case TPM_PT_VENDOR_STRING_2:
93             // second four characters of the vendor ID string
94 #ifdef VENDOR_STRING_2
95             *value = BYTE_ARRAY_TO_UINT32(VENDOR_STRING_2);
96 #else
97             *value = 0;
98 #endif
99             break;
100         case TPM_PT_VENDOR_STRING_3:
101             // third four characters of the vendor ID string
102 #ifdef VENDOR_STRING_3
103             *value = BYTE_ARRAY_TO_UINT32(VENDOR_STRING_3);
104 #else
105             *value = 0;
106 #endif
107             break;
108         case TPM_PT_VENDOR_STRING_4:
109             // fourth four characters of the vendor ID string
110 #ifdef VENDOR_STRING_4
111             *value = BYTE_ARRAY_TO_UINT32(VENDOR_STRING_4);
112 #else
113             *value = 0;
114 #endif
115             break;
116         case TPM_PT_VENDOR_TPM_TYPE:
117             // vendor-defined value indicating the TPM model
118             *value = 1;
119             break;
120         case TPM_PT_FIRMWARE_VERSION_1:
121             // more significant 32-bits of a vendor-specific value
122             *value = gp.firmwareV1;
123             break;
124         case TPM_PT_FIRMWARE_VERSION_2:
125             // less significant 32-bits of a vendor-specific value
126             *value = gp.firmwareV2;
127             break;
128         case TPM_PT_INPUT_BUFFER:
129             // maximum size of TPM2B_MAX_BUFFER
130             *value = MAX_DIGEST_BUFFER;
131             break;
132         case TPM_PT_HR_TRANSIENT_MIN:
133             // minimum number of transient objects that can be held in TPM
134             // RAM
135             *value = MAX_LOADED_OBJECTS;
136             break;
137         case TPM_PT_HR_PERSISTENT_MIN:
138             // minimum number of persistent objects that can be held in
139             // TPM NV memory
140             // In this implementation, there is no minimum number of
141             // persistent objects.
142             *value = MIN_EVICT_OBJECTS;
143             break;
144         case TPM_PT_HR_LOADED_MIN:
145             // minimum number of authorization sessions that can be held in
146             // TPM RAM
147             *value = MAX_LOADED_SESSIONS;
148             break;
149         case TPM_PT_ACTIVE_SESSIONS_MAX:
150             // number of authorization sessions that may be active at a time
151             *value = MAX_ACTIVE_SESSIONS;
152             break;
153         case TPM_PT_PCR_COUNT:
154             // number of PCR implemented
155             *value = IMPLEMENTATION_PCR;
156             break;
157         case TPM_PT_PCR_SELECT_MIN:
158             // minimum number of bytes in a TPMS_PCR_SELECT.sizeOfSelect
159             *value = PCR_SELECT_MIN;
160             break;
161         case TPM_PT_CONTEXT_GAP_MAX:
162             // maximum allowed difference (unsigned) between the contextID
163             // values of two saved session contexts
164             *value = ((UINT32)1 << (sizeof(CONTEXT_SLOT) * 8)) - 1;
165             break;
166         case TPM_PT_NV_COUNTERS_MAX:
167             // maximum number of NV indexes that are allowed to have the
168             // TPMA_NV_COUNTER attribute SET
169             // In this implementation, there is no limitation on the number
170             // of counters, except for the size of the NV Index memory.
171             *value = 0;
172             break;
173         case TPM_PT_NV_INDEX_MAX:
174             // maximum size of an NV index data area
175             *value = MAX_NV_INDEX_SIZE;
176             break;
177         case TPM_PT_MEMORY:
178             // a TPMA_MEMORY indicating the memory management method for the TPM
179         {
180             union
181             {
182                 TPMA_MEMORY     att;
183                 UINT32          u32;
184             } attributes = { TPMA_ZERO_INITIALIZER() };
185             SET_ATTRIBUTE(attributes.att, TPMA_MEMORY, sharedNV);
186             SET_ATTRIBUTE(attributes.att, TPMA_MEMORY, objectCopiedToRam);
187 
188             // Note: For a LSb0 machine, the bits in a bit field are in the correct
189             // order even if the machine is MSB0. For a MSb0 machine, a TPMA will
190             // be an integer manipulated by masking (USE_BIT_FIELD_STRUCTURES will
191             // be NO) so the bits are manipulate correctly.
192             *value = attributes.u32;
193             break;
194         }
195         case TPM_PT_CLOCK_UPDATE:
196             // interval, in seconds, between updates to the copy of
197             // TPMS_TIME_INFO .clock in NV
198             *value = (1 << NV_CLOCK_UPDATE_INTERVAL);
199             break;
200         case TPM_PT_CONTEXT_HASH:
201             // algorithm used for the integrity hash on saved contexts and
202             // for digesting the fuData of TPM2_FirmwareRead()
203             *value = CONTEXT_INTEGRITY_HASH_ALG;
204             break;
205         case TPM_PT_CONTEXT_SYM:
206             // algorithm used for encryption of saved contexts
207             *value = CONTEXT_ENCRYPT_ALG;
208             break;
209         case TPM_PT_CONTEXT_SYM_SIZE:
210             // size of the key used for encryption of saved contexts
211             *value = CONTEXT_ENCRYPT_KEY_BITS;
212             break;
213         case TPM_PT_ORDERLY_COUNT:
214             // maximum difference between the volatile and non-volatile
215             // versions of TPMA_NV_COUNTER that have TPMA_NV_ORDERLY SET
216             *value = MAX_ORDERLY_COUNT;
217             break;
218         case TPM_PT_MAX_COMMAND_SIZE:
219             // maximum value for 'commandSize'
220             *value = MAX_COMMAND_SIZE;
221             break;
222         case TPM_PT_MAX_RESPONSE_SIZE:
223             // maximum value for 'responseSize'
224             *value = MAX_RESPONSE_SIZE;
225             break;
226         case TPM_PT_MAX_DIGEST:
227             // maximum size of a digest that can be produced by the TPM
228             *value = sizeof(TPMU_HA);
229             break;
230         case TPM_PT_MAX_OBJECT_CONTEXT:
231 // Header has 'sequence', 'handle' and 'hierarchy'
232 #define SIZE_OF_CONTEXT_HEADER                  \
233     sizeof(UINT64) + sizeof(TPMI_DH_CONTEXT) + sizeof(TPMI_RH_HIERARCHY)
234 #define SIZE_OF_CONTEXT_INTEGRITY (sizeof(UINT16) + CONTEXT_INTEGRITY_HASH_SIZE)
235 #define SIZE_OF_FINGERPRINT     sizeof(UINT64)
236 #define SIZE_OF_CONTEXT_BLOB_OVERHEAD                \
237         (sizeof(UINT16)  + SIZE_OF_CONTEXT_INTEGRITY + SIZE_OF_FINGERPRINT)
238 #define SIZE_OF_CONTEXT_OVERHEAD                    \
239         (SIZE_OF_CONTEXT_HEADER + SIZE_OF_CONTEXT_BLOB_OVERHEAD)
240 #if 0
241             // maximum size of a TPMS_CONTEXT that will be returned by
242             // TPM2_ContextSave for object context
243             *value = 0;
244             // adding sequence, saved handle and hierarchy
245             *value += sizeof(UINT64) + sizeof(TPMI_DH_CONTEXT) +
246                 sizeof(TPMI_RH_HIERARCHY);
247             // add size field in TPM2B_CONTEXT
248             *value += sizeof(UINT16);
249             // add integrity hash size
250             *value += sizeof(UINT16) +
251                 CryptHashGetDigestSize(CONTEXT_INTEGRITY_HASH_ALG);
252             // Add fingerprint size, which is the same as sequence size
253             *value += sizeof(UINT64);
254             // Add OBJECT structure size
255             *value += sizeof(OBJECT);
256 #else
257             // the maximum size of a TPMS_CONTEXT that will be returned by
258             // TPM2_ContextSave for object context
259             *value = SIZE_OF_CONTEXT_OVERHEAD + sizeof(OBJECT);
260 #endif
261             break;
262         case TPM_PT_MAX_SESSION_CONTEXT:
263 #if 0
264 
265             // the maximum size of a TPMS_CONTEXT that will be returned by
266             // TPM2_ContextSave for object context
267             *value = 0;
268             // adding sequence, saved handle and hierarchy
269             *value += sizeof(UINT64) + sizeof(TPMI_DH_CONTEXT) +
270                 sizeof(TPMI_RH_HIERARCHY);
271             // Add size field in TPM2B_CONTEXT
272             *value += sizeof(UINT16);
273 // Add integrity hash size
274             *value += sizeof(UINT16) +
275                 CryptHashGetDigestSize(CONTEXT_INTEGRITY_HASH_ALG);
276       // Add fingerprint size, which is the same as sequence size
277             *value += sizeof(UINT64);
278             // Add SESSION structure size
279             *value += sizeof(SESSION);
280 #else
281              // the maximum size of a TPMS_CONTEXT that will be returned by
282             // TPM2_ContextSave for object context
283             *value = SIZE_OF_CONTEXT_OVERHEAD + sizeof(SESSION);
284 #endif
285             break;
286         case TPM_PT_PS_FAMILY_INDICATOR:
287             // platform specific values for the TPM_PT_PS parameters from
288             // the relevant platform-specific specification
289             // In this reference implementation, all of these values are 0.
290             *value = PLATFORM_FAMILY;
291             break;
292         case TPM_PT_PS_LEVEL:
293             // level of the platform-specific specification
294             *value = PLATFORM_LEVEL;
295             break;
296         case TPM_PT_PS_REVISION:
297             // specification Revision times 100 for the platform-specific
298             // specification
299             *value = PLATFORM_VERSION;
300             break;
301         case TPM_PT_PS_DAY_OF_YEAR:
302             // platform-specific specification day of year using TCG calendar
303             *value = PLATFORM_DAY_OF_YEAR;
304             break;
305         case TPM_PT_PS_YEAR:
306             // platform-specific specification year using the CE
307             *value = PLATFORM_YEAR;
308             break;
309         case TPM_PT_SPLIT_MAX:
310             // number of split signing operations supported by the TPM
311             *value = 0;
312 #if ALG_ECC
313             *value = sizeof(gr.commitArray) * 8;
314 #endif
315             break;
316         case TPM_PT_TOTAL_COMMANDS:
317             // total number of commands implemented in the TPM
318             // Since the reference implementation does not have any
319             // vendor-defined commands, this will be the same as the
320             // number of library commands.
321         {
322 #if COMPRESSED_LISTS
323             (*value) = COMMAND_COUNT;
324 #else
325             COMMAND_INDEX       commandIndex;
326             *value = 0;
327 
328             // scan all implemented commands
329             for(commandIndex = GetClosestCommandIndex(0);
330             commandIndex != UNIMPLEMENTED_COMMAND_INDEX;
331                 commandIndex = GetNextCommandIndex(commandIndex))
332             {
333                 (*value)++;     // count of all implemented
334             }
335 #endif
336             break;
337         }
338         case TPM_PT_LIBRARY_COMMANDS:
339             // number of commands from the TPM library that are implemented
340         {
341 #if COMPRESSED_LISTS
342             *value = LIBRARY_COMMAND_ARRAY_SIZE;
343 #else
344             COMMAND_INDEX       commandIndex;
345             *value = 0;
346 
347             // scan all implemented commands
348             for(commandIndex = GetClosestCommandIndex(0);
349             commandIndex < LIBRARY_COMMAND_ARRAY_SIZE;
350                 commandIndex = GetNextCommandIndex(commandIndex))
351             {
352                 (*value)++;
353             }
354 #endif
355             break;
356         }
357         case TPM_PT_VENDOR_COMMANDS:
358             // number of vendor commands that are implemented
359             *value = VENDOR_COMMAND_ARRAY_SIZE;
360             break;
361         case TPM_PT_NV_BUFFER_MAX:
362             // Maximum data size in an NV write command
363             *value = MAX_NV_BUFFER_SIZE;
364             break;
365         case TPM_PT_MODES:
366 #if FIPS_COMPLIANT
367             *value = 1;
368 #else
369             *value = 0;
370 #endif
371             break;
372         case TPM_PT_MAX_CAP_BUFFER:
373             *value = MAX_CAP_BUFFER;
374             break;
375 
376         // Start of variable commands
377         case TPM_PT_PERMANENT:
378             // TPMA_PERMANENT
379         {
380             union {
381                 TPMA_PERMANENT      attr;
382                 UINT32              u32;
383             } flags = { TPMA_ZERO_INITIALIZER() };
384             if(gp.ownerAuth.t.size != 0)
385                 SET_ATTRIBUTE(flags.attr, TPMA_PERMANENT, ownerAuthSet);
386             if(gp.endorsementAuth.t.size != 0)
387                 SET_ATTRIBUTE(flags.attr, TPMA_PERMANENT, endorsementAuthSet);
388             if(gp.lockoutAuth.t.size != 0)
389                 SET_ATTRIBUTE(flags.attr, TPMA_PERMANENT, lockoutAuthSet);
390             if(gp.disableClear)
391                 SET_ATTRIBUTE(flags.attr, TPMA_PERMANENT, disableClear);
392             if(gp.failedTries >= gp.maxTries)
393                 SET_ATTRIBUTE(flags.attr, TPMA_PERMANENT, inLockout);
394             // In this implementation, EPS is always generated by TPM
395             SET_ATTRIBUTE(flags.attr, TPMA_PERMANENT, tpmGeneratedEPS);
396 
397             // Note: For a LSb0 machine, the bits in a bit field are in the correct
398             // order even if the machine is MSB0. For a MSb0 machine, a TPMA will
399             // be an integer manipulated by masking (USE_BIT_FIELD_STRUCTURES will
400             // be NO) so the bits are manipulate correctly.
401             *value = flags.u32;
402             break;
403         }
404         case TPM_PT_STARTUP_CLEAR:
405             // TPMA_STARTUP_CLEAR
406         {
407             union {
408                 TPMA_STARTUP_CLEAR  attr;
409                 UINT32              u32;
410             } flags = { TPMA_ZERO_INITIALIZER() };
411 //
412             if(g_phEnable)
413                 SET_ATTRIBUTE(flags.attr, TPMA_STARTUP_CLEAR, phEnable);
414             if(gc.shEnable)
415                 SET_ATTRIBUTE(flags.attr, TPMA_STARTUP_CLEAR, shEnable);
416             if(gc.ehEnable)
417                 SET_ATTRIBUTE(flags.attr, TPMA_STARTUP_CLEAR, ehEnable);
418             if(gc.phEnableNV)
419                 SET_ATTRIBUTE(flags.attr, TPMA_STARTUP_CLEAR, phEnableNV);
420             if(g_prevOrderlyState != SU_NONE_VALUE)
421                 SET_ATTRIBUTE(flags.attr, TPMA_STARTUP_CLEAR, orderly);
422 
423             // Note: For a LSb0 machine, the bits in a bit field are in the correct
424             // order even if the machine is MSB0. For a MSb0 machine, a TPMA will
425             // be an integer manipulated by masking (USE_BIT_FIELD_STRUCTURES will
426             // be NO) so the bits are manipulate correctly.
427             *value = flags.u32;
428             break;
429         }
430         case TPM_PT_HR_NV_INDEX:
431             // number of NV indexes currently defined
432             *value = NvCapGetIndexNumber();
433             break;
434         case TPM_PT_HR_LOADED:
435             // number of authorization sessions currently loaded into TPM
436             // RAM
437             *value = SessionCapGetLoadedNumber();
438             break;
439         case TPM_PT_HR_LOADED_AVAIL:
440             // number of additional authorization sessions, of any type,
441             // that could be loaded into TPM RAM
442             *value = SessionCapGetLoadedAvail();
443             break;
444         case TPM_PT_HR_ACTIVE:
445             // number of active authorization sessions currently being
446             // tracked by the TPM
447             *value = SessionCapGetActiveNumber();
448             break;
449         case TPM_PT_HR_ACTIVE_AVAIL:
450             // number of additional authorization sessions, of any type,
451             // that could be created
452             *value = SessionCapGetActiveAvail();
453             break;
454         case TPM_PT_HR_TRANSIENT_AVAIL:
455             // estimate of the number of additional transient objects that
456             // could be loaded into TPM RAM
457             *value = ObjectCapGetTransientAvail();
458             break;
459         case TPM_PT_HR_PERSISTENT:
460             // number of persistent objects currently loaded into TPM
461             // NV memory
462             *value = NvCapGetPersistentNumber();
463             break;
464         case TPM_PT_HR_PERSISTENT_AVAIL:
465             // number of additional persistent objects that could be loaded
466             // into NV memory
467             *value = NvCapGetPersistentAvail();
468             break;
469         case TPM_PT_NV_COUNTERS:
470             // number of defined NV indexes that have NV TPMA_NV_COUNTER
471             // attribute SET
472             *value = NvCapGetCounterNumber();
473             break;
474         case TPM_PT_NV_COUNTERS_AVAIL:
475             // number of additional NV indexes that can be defined with their
476             // TPMA_NV_COUNTER attribute SET
477             *value = NvCapGetCounterAvail();
478             break;
479         case TPM_PT_ALGORITHM_SET:
480             // region code for the TPM
481             *value = gp.algorithmSet;
482             break;
483         case TPM_PT_LOADED_CURVES:
484 #if ALG_ECC
485         // number of loaded ECC curves
486             *value = ECC_CURVE_COUNT;
487 #else // ALG_ECC
488             *value = 0;
489 #endif // ALG_ECC
490             break;
491         case TPM_PT_LOCKOUT_COUNTER:
492             // current value of the lockout counter
493             *value = gp.failedTries;
494             break;
495         case TPM_PT_MAX_AUTH_FAIL:
496             // number of authorization failures before DA lockout is invoked
497             *value = gp.maxTries;
498             break;
499         case TPM_PT_LOCKOUT_INTERVAL:
500             // number of seconds before the value reported by
501             // TPM_PT_LOCKOUT_COUNTER is decremented
502             *value = gp.recoveryTime;
503             break;
504         case TPM_PT_LOCKOUT_RECOVERY:
505             // number of seconds after a lockoutAuth failure before use of
506             // lockoutAuth may be attempted again
507             *value = gp.lockoutRecovery;
508             break;
509         case TPM_PT_NV_WRITE_RECOVERY:
510             // number of milliseconds before the TPM will accept another command
511             // that will modify NV.
512             // This should make a call to the platform code that is doing rate
513             // limiting of NV. Rate limiting is not implemented in the reference
514             // code so no call is made.
515             *value = 0;
516             break;
517         case TPM_PT_AUDIT_COUNTER_0:
518             // high-order 32 bits of the command audit counter
519             *value = (UINT32)(gp.auditCounter >> 32);
520             break;
521         case TPM_PT_AUDIT_COUNTER_1:
522             // low-order 32 bits of the command audit counter
523             *value = (UINT32)(gp.auditCounter);
524             break;
525         default:
526             // property is not defined
527             return FALSE;
528             break;
529     }
530     return TRUE;
531 }
532 
533 //*** TPMCapGetProperties()
534 // This function is used to get the TPM_PT values. The search of properties will
535 // start at 'property' and continue until 'propertyList' has as many values as
536 // will fit, or the last property has been reported, or the list has as many
537 // values as requested in 'count'.
538 //  Return Type: TPMI_YES_NO
539 //  YES        more properties are available
540 //  NO         no more properties to be reported
541 TPMI_YES_NO
TPMCapGetProperties(TPM_PT property,UINT32 count,TPML_TAGGED_TPM_PROPERTY * propertyList)542 TPMCapGetProperties(
543     TPM_PT                       property,      // IN: the starting TPM property
544     UINT32                       count,         // IN: maximum number of returned
545                                                 //     properties
546     TPML_TAGGED_TPM_PROPERTY    *propertyList   // OUT: property list
547     )
548 {
549     TPMI_YES_NO     more = NO;
550     UINT32          i;
551     UINT32          nextGroup;
552 
553     // initialize output property list
554     propertyList->count = 0;
555 
556     // maximum count of properties we may return is MAX_PCR_PROPERTIES
557     if(count > MAX_TPM_PROPERTIES) count = MAX_TPM_PROPERTIES;
558 
559     // if property is less than PT_FIXED, start from PT_FIXED
560     if(property < PT_FIXED)
561         property = PT_FIXED;
562     // There is only the fixed and variable groups with the variable group coming
563     // last
564     if(property >= (PT_VAR + PT_GROUP))
565         return more;
566 
567     // Don't read past the end of the selected group
568     nextGroup = ((property / PT_GROUP) * PT_GROUP) + PT_GROUP;
569 
570     // Scan through the TPM properties of the requested group.
571     for(i = property; i < nextGroup; i++)
572     {
573         UINT32          value;
574         // if we have hit the end of the group, quit
575         if(i != property && ((i % PT_GROUP) == 0))
576             break;
577         if(TPMPropertyIsDefined((TPM_PT)i, &value))
578         {
579             if(propertyList->count < count)
580             {
581                 // If the list is not full, add this property
582                 propertyList->tpmProperty[propertyList->count].property =
583                     (TPM_PT)i;
584                 propertyList->tpmProperty[propertyList->count].value = value;
585                 propertyList->count++;
586             }
587             else
588             {
589                 // If the return list is full but there are more properties
590                 // available, set the indication and exit the loop.
591                 more = YES;
592                 break;
593             }
594         }
595     }
596     return more;
597 }