All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: [tpm2] Seal/Unseal NV Index to PCRs
@ 2018-07-18  9:26 Zhu, Bing
  0 siblings, 0 replies; 2+ messages in thread
From: Zhu, Bing @ 2018-07-18  9:26 UTC (permalink / raw)
  To: tpm2

[-- Attachment #1: Type: text/plain, Size: 2782 bytes --]

Typically, this is pretty complicated thing to do. See this blog https://mjg59.dreamwidth.org/48897.html


1.    If you do want a sample to seal an NV index value with set of PCRs, actually I had an example based on https://github.com/tpm2-software/tpm2-tss/tree/master/test you can take it as a reference.

2.    Then when system upgrades that cause the PCR values updated, I think you should use the API TPM2_PolicyAuthorize  to handle with that. Unfortunately, I don’t have sample code.

Here I copied text here for TPM2.0 specification. Not sure if you’re aware of this before.
“In the case of a BIOS update that changes PCR, the platform OEM could provide a signature for the PCR
values created by the new BIOS. Now, if the policy of the sealed data includes a TPM2_PolicyAuthorize()
from the OEM, then the BIOS can be updated and no recovery process would be needed to deal with the
new PCR values. That is, with either authorized set of PCR, DKEY and Dfinal will be the same, even though
DPCR.A and DPCR.B are different.

An additional way to indirectly modify a policy uses TPM2_PolicyAuthorizeNV(). This command specifies
an NV Index location of a policy that will be effective for an entity, the effective policy being policy digest
stored in the data at that NV index. When a policy is formed using TPM2_PolicyAuthorizeNV(), the NV
Index Name is specified. When the entity is to be authorized, the policy stored in the data of the named
index is satisfied, and then TPM2_PolicyAuthorizeNV() is executed. If the policy stored in that index
matches the policySession->policyDigest, then the policySession->policyDigest is replaced with results of
first setting the policySession->policyDigest to the Zero Digest, and then extending it with the command
code concatenated with the name of the NV index, which will contain the modified policy. The main
difference between this command and TPM2_PolicyAuthorize() is that multiple policies can be used to
satisfy TPM2_PolicyAuthorize(), as long as they are all signed with the appropriate key. Only the policy
that is currently stored in the NV index can satisfy TPM2_PolicyAuthorizeNV()”

Bing

From: tpm2 [mailto:tpm2-bounces(a)lists.01.org] On Behalf Of Arvind Kumar
Sent: Wednesday, July 18, 2018 6:03 AM
To: tpm2(a)lists.01.org
Subject: [tpm2] Seal/Unseal NV Index to PCRs

Hi,

I'm a beginner who needs to securely store disk partition encryption key (luks key) in TPM 2.0. I have been looking around for examples but most of them only cover theory. I need to figure out following - How to seal an NV index to set of PCRs where these PCRs will be updated on system upgrade later.

I need to understand the process with examples, any help is appreciated !


Thanks,
Arvind

[-- Attachment #2: attachment.html --]
[-- Type: text/html, Size: 11711 bytes --]

[-- Attachment #3: NV_PCR.c --]
[-- Type: text/plain, Size: 33354 bytes --]

/* SPDX-License-Identifier: BSD-2 */
/***********************************************************************;
 * Copyright (c) 2018, Intel Corporation
 * All rights reserved.
 ***********************************************************************;
 */

#define PCR_0   0
#define PCR_1   1
#define PCR_2   2
#define PCR_3   3
#define PCR_4   4
#define PCR_5   5
#define PCR_6   6
#define PCR_7   7
#define PCR_8   8
#define PCR_9   9
#define PCR_10  10
#define PCR_11  11
#define PCR_12  12
#define PCR_13  13
#define PCR_14  14
#define PCR_15  15
#define PCR_16  16
#define PCR_17  17
#define PCR_18  18



#define STRING_BYTES_ENDIAN_CONVERT(size) \
    UINT##size string_bytes_endian_convert_##size(UINT##size data) { \
    \
        UINT##size converted; \
        UINT8 *bytes = (UINT8 *)&data; \
        UINT8 *tmp = (UINT8 *)&converted; \
    \
        size_t i; \
        for(i=0; i < sizeof(UINT##size); i ++) { \
            tmp[i] = bytes[sizeof(UINT##size) - i - 1]; \
        } \
        \
        return converted; \
    }

//string_bytes_endian_convert_32()
STRING_BYTES_ENDIAN_CONVERT(32);

// list all NV index
void GetTpmNvList()
{
	TSS2_RC rval = TSS2_RC_SUCCESS;
	TPMS_CAPABILITY_DATA capabilityData = { 0};
	TPMI_YES_NO moreData = 0;
	TPM2B_NV_PUBLIC nvPublic;
	TPM2B_NAME nvName;

	
	DebugPrintf( NO_PREFIX, "\nList all NV Index: \n"); 	

	rval = Tss2_Sys_GetCapability( sysContext, 
									0,
									TPM_CAP_HANDLES, 
									string_bytes_endian_convert_32(TPM_HT_NV_INDEX),
									TPM_PT_NV_INDEX_MAX, 
									&moreData, 
									&capabilityData, 
									0 );

	for(UINT32 i = 0; i < capabilityData.data.handles.count; i ++){
		DebugPrintf( NO_PREFIX, "NV Index[%02d]: ", i); 	

		nvPublic.t.size = 0;
		INIT_SIMPLE_TPM2B_SIZE( nvName );
		
		// no auth is required to read public info of NV.
		rval = Tss2_Sys_NV_ReadPublic( sysContext, capabilityData.data.handles.handle[i], 0, &nvPublic, &nvName, 0 );	
		DebugPrintf( NO_PREFIX, "nvIndex = 0x%X, nameAlg = 0x%X, attr = 0x%X, authPolicy.size = 0x%x, dataSize = 0x%x\n", 
								nvPublic.t.nvPublic.nvIndex,
								nvPublic.t.nvPublic.nameAlg,
								nvPublic.t.nvPublic.attributes,
								nvPublic.t.nvPublic.authPolicy.t.size,
								nvPublic.t.nvPublic.dataSize);
		CheckPassed( rval );

	}

}

void TestPcrExtend()
{
    UINT32 rval;
    TPMS_AUTH_COMMAND sessionData;
    TSS2_SYS_CMD_AUTHS sessionsData;
    UINT16 i, digestSize;
    TPML_PCR_SELECTION  pcrSelection;
    UINT32 pcrUpdateCounterBeforeExtend;
    UINT32 pcrUpdateCounterAfterExtend;
    UINT8 pcrBeforeExtend[SHA256_DIGEST_SIZE];	
	UINT8 pcrAfterExtend[SHA256_DIGEST_SIZE];
    TPM2B_EVENT eventData;
    TPML_DIGEST pcrValues;
    TPML_DIGEST_VALUES digests;
    TPML_PCR_SELECTION pcrSelectionOut;

    TPMS_AUTH_COMMAND *sessionDataArray[1];

    sessionDataArray[0] = &sessionData;
    sessionsData.cmdAuths = &sessionDataArray[0];

    DebugPrintf( NO_PREFIX, "\nPCR_EXTEND, PCR_EVENT, PCR_ALLOCATE, and PCR_READ TESTS:\n" );

    // Init authHandle
    sessionData.sessionHandle = TPM_RS_PW;

    // Init nonce.
    sessionData.nonce.t.size = 0;

    // init hmac
    sessionData.hmac.t.size = 0;

    // Init session attributes
    *( (UINT8 *)((void *)&sessionData.sessionAttributes ) ) = 0;

    // Init digests
    digests.count = 1;
    digests.digests[0].hashAlg = TPM_ALG_SHA256;
    digestSize = GetDigestSize( digests.digests[0].hashAlg );

    for( i = 0; i < digestSize; i++ )
    {
        digests.digests[0].digest.sha1[i] = (UINT8)(i % 256);
    }

    pcrSelection.count = 1;
    pcrSelection.pcrSelections[0].hash = TPM_ALG_SHA256;
    pcrSelection.pcrSelections[0].sizeofSelect = 3;

    // Clear out PCR select bit field
    pcrSelection.pcrSelections[0].pcrSelect[0] = 0;
    pcrSelection.pcrSelections[0].pcrSelect[1] = 0;
    pcrSelection.pcrSelections[0].pcrSelect[2] = 0;

    // Now set the PCR you want to read
    SET_PCR_SELECT_BIT( pcrSelection.pcrSelections[0], PCR_0 );

    rval = Tss2_Sys_PCR_Read( sysContext, 0, &pcrSelection, &pcrUpdateCounterBeforeExtend, &pcrSelectionOut, &pcrValues, 0 );
    CheckPassed( rval );

    if( pcrValues.digests[0].t.size <= SHA256_DIGEST_SIZE &&
            pcrValues.digests[0].t.size <= sizeof( pcrValues.digests[0].t.buffer ) )
        memcpy( &( pcrBeforeExtend[0] ), &( pcrValues.digests[0].t.buffer[0] ), pcrValues.digests[0].t.size );

    sessionsData.cmdAuthsCount = 1;
    sessionsData.cmdAuths[0] = &sessionData;

    rval = Tss2_Sys_PCR_Extend( sysContext, PCR_0, &sessionsData, &digests, 0  );
    CheckPassed( rval );

    rval = Tss2_Sys_PCR_Read( sysContext, 0, &pcrSelection, &pcrUpdateCounterAfterExtend, &pcrSelectionOut, &pcrValues, 0 );
    CheckPassed( rval );

    memcpy( &( pcrAfterExtend[0] ), &( pcrValues.digests[0].t.buffer[0] ), pcrValues.digests[0].t.size );

    if( pcrUpdateCounterBeforeExtend == pcrUpdateCounterAfterExtend )
    {
        DebugPrintf( NO_PREFIX, "ERROR!! pcrUpdateCounter didn't change value\n" );
        Cleanup();
    }

    if( 0 == memcmp( &( pcrBeforeExtend[0] ), &( pcrAfterExtend[0] ), SHA256_DIGEST_SIZE ) )
    {
        DebugPrintf( NO_PREFIX, "ERROR!! PCR didn't change value\n" );
        Cleanup();
    }

    pcrSelection.pcrSelections[0].sizeofSelect = 4;

    rval = Tss2_Sys_PCR_Read( sysContext, 0, &pcrSelection, &pcrUpdateCounterAfterExtend, 0, 0, 0 );
    CheckFailed( rval, TPM_RC_1 + TPM_RC_P + TPM_RC_VALUE );

    eventData.t.size = 4;
    eventData.t.buffer[0] = 0;
    eventData.t.buffer[1] = 0xff;
    eventData.t.buffer[2] = 0x55;
    eventData.t.buffer[3] = 0xaa;

    rval = Tss2_Sys_PCR_Event( sysContext, PCR_18, &sessionsData, &eventData, &digests, 0  );
    CheckPassed( rval );
}

void GetTpmPCRs()
{
	TSS2_RC rval = TSS2_RC_SUCCESS;
	TPMS_CAPABILITY_DATA capabilityData = { 0};
	TPMI_YES_NO moreData = 0;
	UINT32 unused;
	TPML_PCR_SELECTION pcrSelectionOut;
	TPML_DIGEST pcrValues;

	rval = Tss2_Sys_GetCapability( sysContext, 
									0,
									TPM_CAP_PCRS, 
									0,
									1, 
									&moreData, 
									&capabilityData, 
									0 );
	CheckPassed( rval );

	for(UINT32 i = 0; i < capabilityData.data.assignedPCR.count; i ++){

		if (TPM_ALG_SHA256 != capabilityData.data.assignedPCR.pcrSelections[i].hash) continue;
		
		DebugPrintf( NO_PREFIX, "\nPCR:HASH bank (TPM_ALG_SHA256) = 0x%x, sizeofSelect = %d\n", 
					capabilityData.data.assignedPCR.pcrSelections[i].hash,
					capabilityData.data.assignedPCR.pcrSelections[i].sizeofSelect);
	}

	capabilityData.data.assignedPCR.count = 1;
	capabilityData.data.assignedPCR.pcrSelections[0].hash = TPM_ALG_SHA256;
	SET_PCR_SELECT_SIZE(capabilityData.data.assignedPCR.pcrSelections[0], 3);

		
	for(UINT32 pcr_id = 0; pcr_id < 24; pcr_id += 8) {
		CLEAR_PCR_SELECT_BITS(capabilityData.data.assignedPCR.pcrSelections[0]);
		capabilityData.data.assignedPCR.pcrSelections[0].pcrSelect[pcr_id / 8] = 0xff;


		
		rval = Tss2_Sys_PCR_Read( sysContext, 0, &(capabilityData.data.assignedPCR), &unused, &pcrSelectionOut, &pcrValues, 0 );


		
		for(UINT32 j = 0; j < pcrValues.count; j ++){
			DebugPrintf( NO_PREFIX, "PCR[%02d]: ", pcr_id + j);
			for(UINT32 l = 0; l < pcrValues.digests[j].t.size; l++){
				DebugPrintf( NO_PREFIX, " %02x", pcrValues.digests[j].t.buffer[l]);
			}	
			DebugPrintf( NO_PREFIX, "\n");
		}
		
		

	}

	DebugPrintf( NO_PREFIX, "\n");
}





#define NV_INDEX_OS_SEED           0x01500052
#define NV_INDEX_OS_SEED_0         0x01500053


#define NV_INDEX_OS_SEED_SIZE      32     // 32Bytes




// NOTE:  this policySession will be either a trial or real policy session
// depending on the value of the passed in trialSession parameter.
// TODO: associated with PCR0/2/4/6/8/10 
TPM_RC BuildSeedReadPolicy( TSS2_SYS_CONTEXT *sysContext, TPMI_SH_AUTH_SESSION *nvSessionHandle, TPM2B_DIGEST *policyDigest, bool trialSession )
{

    TPM2B_ENCRYPTED_SECRET  	encryptedSalt = { {0}, };
    TPMT_SYM_DEF 				symmetric = { .algorithm = TPM_ALG_NULL };
    TPM_RC 						rval;
    TPM2B_NONCE 				nonceCaller, nonceTpm;
	UINT32                      i;	    
    TPM2B_DIGEST 				pcrDigest;
    TPML_PCR_SELECTION 			pcrs;
    TPML_DIGEST 				pcrValues;
    UINT32 						pcrUpdateCounter;
	TPML_PCR_SELECTION 			pcrSelectionOut;

	// Init nonceNewer (TODO - use random)
	nonceCaller.t.size = GetDigestSize( TPM_ALG_SHA256 );
	for( i = 0; i < nonceCaller.t.size; i++ )
		nonceCaller.t.buffer[i] = 0xff - i;

	INIT_SIMPLE_TPM2B_SIZE(nonceTpm);
	
    // Start policy session, and return a session handle (nvSessionHandle)
    rval = Tss2_Sys_StartAuthSession(  sysContext, 
    								   TPM_RH_NULL, // tpmKey
    								   TPM_RH_NULL, // bind
    								   0,
    								   &nonceCaller,
    								   &encryptedSalt, 
    								   trialSession ? TPM_SE_TRIAL : TPM_SE_POLICY , 
    								   &symmetric, 
    								   TPM_ALG_SHA256,
    								   nvSessionHandle,
    								   &nonceTpm,
    								   0
    								   );
    if( rval != TPM_RC_SUCCESS )
        return rval;


	// Set PCRs that are associated with this policy
	// for now, only choose PCR#1 and PCR#3
	pcrs.count = 1;
	pcrs.pcrSelections[0].hash 		   = TPM_ALG_SHA256;
	pcrs.pcrSelections[0].sizeofSelect = 3;
	pcrs.pcrSelections[0].pcrSelect[0] = 0;
	pcrs.pcrSelections[0].pcrSelect[1] = 0;
	pcrs.pcrSelections[0].pcrSelect[2] = 0;
	SET_PCR_SELECT_BIT( pcrs.pcrSelections[0], PCR_0  );
	SET_PCR_SELECT_BIT( pcrs.pcrSelections[0], PCR_18 );


	//
	// Compute pcrDigest
	//
	// 1. Read PCRs (&pcrSelectionOut MUST NOT be NULL!!!!!)
	rval = Tss2_Sys_PCR_Read( sysContext, 0, &pcrs, &pcrUpdateCounter, &pcrSelectionOut, &pcrValues, 0 );
    CheckPassed( rval );

	// 2. Hash those PCRs together
    INIT_SIMPLE_TPM2B_SIZE( pcrDigest );
    rval = TpmHashSequence( TPM_ALG_SHA256, pcrValues.count, &pcrValues.digests[0], &pcrDigest );
    CheckPassed( rval );

	// 3. Apply selected PCRs' pcrDigest (as approvedPcrDigest) to policyDigest, 
	//    and setup connection between policy and selected PCRs.
    rval = Tss2_Sys_PolicyPCR( sysContext, *nvSessionHandle, 0, &pcrDigest, &pcrs, 0 );
    CheckPassed( rval );

    // Get policyDigest hash.
    INIT_SIMPLE_TPM2B_SIZE( *policyDigest );
    rval = Tss2_Sys_PolicyGetDigest( sysContext, *nvSessionHandle, 0, policyDigest, 0 );
    CheckPassed( rval );

    if( trialSession )
    {
        // Need to flush the session here for trial policy only
        rval = Tss2_Sys_FlushContext( sysContext, *nvSessionHandle );
        CheckPassed( rval );
    }

    return rval;
}




// 
// Name/ID: NV_INDEX_OS_SEED
// Description: fuse the TRK (SEED) to a NV index
// Size: 32B
// Policy: Read when PCRs match trusted boot and  OS, Write once for ever
// Input: the SEED of ( Root Key, TRK)
//
void TestFuseSeedReadPolicyWriteOnce(TPM2B_DIGEST *seed)
{
	UINT32 rval;
	TPM2B_NV_PUBLIC publicInfo;
	TPM2B_AUTH	nvAuth;
	TPMS_AUTH_COMMAND sessionData;
	TSS2_SYS_CMD_AUTHS sessionsData;
	int i;
	TPM2B_MAX_NV_BUFFER nvWriteData;
	TPM2B_MAX_NV_BUFFER nvReadData;
	TPMI_SH_AUTH_SESSION nvSessionHandle = 0;
	bool                 indexExist = false;

	TPM2B_DIGEST policyDigest = { { sizeof (TPM2B_DIGEST) - 2, } };

	TPMS_AUTH_COMMAND *sessionDataArray[1];

	sessionDataArray[0] = &sessionData;
	sessionsData.cmdAuths = &sessionDataArray[0];

	DebugPrintf( NO_PREFIX, "\nStart to do one-time fuse for  SEED:\n" );

	// auth value (random? -  doesn't matter!!!)
	nvAuth.t.size = 0;

	publicInfo.t.size = sizeof( TPMI_RH_NV_INDEX ) +
						sizeof( TPMI_ALG_HASH ) + 
						sizeof( TPMA_NV ) + 
						sizeof( UINT16) +
						sizeof( UINT16 );
	publicInfo.t.nvPublic.nvIndex = NV_INDEX_OS_SEED;
	publicInfo.t.nvPublic.nameAlg = TPM_ALG_SHA256;

	// First zero out attributes.
	*(UINT32 *)&( publicInfo.t.nvPublic.attributes ) = 0;

	// Now set the attributes.
	publicInfo.t.nvPublic.attributes.TPM_NT                 = TPM_NT_ORDINARY;
	publicInfo.t.nvPublic.attributes.TPMA_NV_POLICYREAD     = 1;
	publicInfo.t.nvPublic.attributes.TPMA_NV_PPWRITE        = 1;
	publicInfo.t.nvPublic.attributes.TPMA_NV_WRITEDEFINE    = 1; //TPM2_NV_WriteLock() may be used to prevent 
	                                                             //further writes to this location (unless deleted)
	publicInfo.t.nvPublic.attributes.TPMA_NV_WRITEALL       = 1;
	publicInfo.t.nvPublic.attributes.TPMA_NV_PLATFORMCREATE = 1; 

	// We need to set a policy (never satisfied) to prevent this NV index deletion.
	publicInfo.t.nvPublic.attributes.TPMA_NV_POLICY_DELETE = 1; 


	// start to build the policy digest with trial session
	rval = BuildSeedReadPolicy(sysContext, &nvSessionHandle, &policyDigest, true);
	CheckPassed( rval );

	nvSessionHandle = 0;
	
	// enable policy for this index now
	CopySizedByteBuffer( &(publicInfo.t.nvPublic.authPolicy.b), &(policyDigest.b) );

	// SHA256
	publicInfo.t.nvPublic.dataSize = NV_INDEX_OS_SEED_SIZE; 


	InitNullSession(&sessionData);

	sessionsData.cmdAuthsCount = 1;
	sessionsData.cmdAuths[0] = &sessionData;

	// make sure the data size is the same with publicInfo.t.nvPublic.dataSize
	// and the seed.t.size < MAX_NV_INDEX_SIZE
	if(seed->t.size >= MAX_NV_INDEX_SIZE || 
	   seed->t.size != NV_INDEX_OS_SEED_SIZE ||
	   seed->t.size != publicInfo.t.nvPublic.dataSize)
		CheckPassed(-1);

	
	nvWriteData.t.size = seed->t.size;
	for( i = 0; i < nvWriteData.t.size; i++ ){
		nvWriteData.t.buffer[i] = seed->t.buffer[i];
	}


	// just create a NV index
	rval = Tss2_Sys_NV_DefineSpace( sysContext, TPM_RH_PLATFORM, &sessionsData, &nvAuth, &publicInfo, 0 );
	if(rval == TPM_RC_NV_DEFINED){
		DebugPrintf( NO_PREFIX, "Already defined, attemp to do read test \n" );

		indexExist = true;
		goto verify;
	}
	
	CheckPassed(rval);	

	rval = Tss2_Sys_NV_Write( sysContext, TPM_RH_PLATFORM, NV_INDEX_OS_SEED, &sessionsData, &nvWriteData, 0, 0 );
	CheckPassed( rval );

	for( i = 0; i < nvWriteData.t.size; i++ )
		DebugPrintf( NO_PREFIX, "%02X ", nvWriteData.t.buffer[i] );
    DebugPrintf( NO_PREFIX, "\n");	
	
	// Lock the write to prevent further write access
	rval = Tss2_Sys_NV_WriteLock( sysContext, TPM_RH_PLATFORM, NV_INDEX_OS_SEED, &sessionsData, 0 );
	CheckPassed( rval );

verify:
	
	// after locked, the write will fail (since both TPMA_NV_WRITEDEFINE and TPMA_NV_WRITE_STCLEAR is set)
	rval = Tss2_Sys_NV_Write( sysContext, TPM_RH_PLATFORM, NV_INDEX_OS_SEED, &sessionsData, &nvWriteData, 0, 0);
	CheckFailed( rval, TPM_RC_NV_LOCKED );

	// read to verify (test 2 times)
	for( i = 0; i < 2; i ++){
		// start to build the policy digest with real session
		rval = BuildSeedReadPolicy(sysContext, &nvSessionHandle, &policyDigest, false);
		CheckPassed( rval );

		// apply policy session
		sessionData.sessionHandle     = nvSessionHandle;
		sessionData.hmac.t.size       = 0;
		sessionData.nonce.t.size      = 0;
		*( (UINT8 *)((void *)&( sessionData.sessionAttributes ) ) ) = 0;
	    sessionData.sessionAttributes.continueSession = 1;
		
		// read to verify
		INIT_SIMPLE_TPM2B_SIZE( nvReadData );
		rval = Tss2_Sys_NV_Read( sysContext, NV_INDEX_OS_SEED, NV_INDEX_OS_SEED, &sessionsData, NV_INDEX_OS_SEED_SIZE, 0, &nvReadData, 0 );
		CheckPassed( rval );
		if( indexExist == false){
			rval = CompareSizedByteBuffer(&(seed->b), &(nvReadData.b));
			CheckPassed( rval );
		}


		rval = Tss2_Sys_FlushContext( sysContext, nvSessionHandle );
		CheckPassed( rval );


		InitNullSession(&sessionData);
		// read to verify
		INIT_SIMPLE_TPM2B_SIZE( nvReadData );
		rval = Tss2_Sys_NV_Read( sysContext, TPM_RH_PLATFORM, NV_INDEX_OS_SEED, &sessionsData, NV_INDEX_OS_SEED_SIZE, 0, &nvReadData, 0 );
		CheckFailed( rval, TPM_RC_NV_AUTHORIZATION );

		rval = Tss2_Sys_NV_Read( sysContext, NV_INDEX_OS_SEED, NV_INDEX_OS_SEED, &sessionsData, NV_INDEX_OS_SEED_SIZE, 0, &nvReadData, 0 );
		CheckFailed( rval, TPM_RC_AUTH_UNAVAILABLE );
	}
}


// 
// Name/ID: NV_INDEX_OS_SEED
// Description: fuse the TRK (SEED) to a NV index
// Size: 32B
// Policy: Read once per boot, Write once for ever
// Input: the SEED of  ( Root Key, TRK)
//
void TestFuseSeedReadOnceWriteOnce(TPM2B_DIGEST *seed)
{
	UINT32 rval;
	TPM2B_NV_PUBLIC publicInfo;
	TPM2B_AUTH	nvAuth;
	TPMS_AUTH_COMMAND sessionData;
	TSS2_SYS_CMD_AUTHS sessionsData;
	int i;
	TPM2B_MAX_NV_BUFFER nvWriteData;
	TPM2B_MAX_NV_BUFFER nvReadData;
	bool                 indexExist = false;

	TPMS_AUTH_COMMAND *sessionDataArray[1];

	sessionDataArray[0] = &sessionData;
	sessionsData.cmdAuths = &sessionDataArray[0];

	DebugPrintf( NO_PREFIX, "\nStart to do one-time fuse for  SEED -#0:\n" );

	// auth value (random? -  doesn't matter!!!)
	nvAuth.t.size = 0;

	publicInfo.t.size = sizeof( TPMI_RH_NV_INDEX ) +
						sizeof( TPMI_ALG_HASH ) + 
						sizeof( TPMA_NV ) + 
						sizeof( UINT16) +
						sizeof( UINT16 );
	publicInfo.t.nvPublic.nvIndex = NV_INDEX_OS_SEED_0;
	publicInfo.t.nvPublic.nameAlg = TPM_ALG_SHA256;

	// First zero out attributes.
	*(UINT32 *)&( publicInfo.t.nvPublic.attributes ) = 0;

	// Now set the attributes.
	publicInfo.t.nvPublic.attributes.TPM_NT                 = TPM_NT_ORDINARY;
	publicInfo.t.nvPublic.attributes.TPMA_NV_PPREAD     	= 1;
	publicInfo.t.nvPublic.attributes.TPMA_NV_PPWRITE        = 1;

	// TPM2_NV_WriteLock() may be used to prevent further writes to this location (unless deleted)
	publicInfo.t.nvPublic.attributes.TPMA_NV_WRITEDEFINE    = 1; 

	publicInfo.t.nvPublic.attributes.TPMA_NV_WRITEALL       = 1;
	publicInfo.t.nvPublic.attributes.TPMA_NV_PLATFORMCREATE = 1; 

	// TPM2_NV_ReadLock() may be used to SET TPMA_NV_READLOCKED for this Index.
	publicInfo.t.nvPublic.attributes.TPMA_NV_READ_STCLEAR   = 1;

	// We need to set a policy (never satisfied) to prevent this NV index deletion.
	// WARNING - must clear this on development stage.
	publicInfo.t.nvPublic.attributes.TPMA_NV_POLICY_DELETE = 1; 
	
	//  policy for this index now (Empty Policy)
	publicInfo.t.nvPublic.authPolicy.t.size = 0;

	// SHA256 size
	publicInfo.t.nvPublic.dataSize = NV_INDEX_OS_SEED_SIZE; 


	InitNullSession(&sessionData);

	sessionsData.cmdAuthsCount = 1;
	sessionsData.cmdAuths[0] = &sessionData;

	// make sure the data size is the same with publicInfo.t.nvPublic.dataSize
	// and the seed.t.size < MAX_NV_INDEX_SIZE
	if(seed->t.size >= MAX_NV_INDEX_SIZE || 
	   seed->t.size != NV_INDEX_OS_SEED_SIZE ||
	   seed->t.size != publicInfo.t.nvPublic.dataSize)
		CheckPassed(-1);

	
	nvWriteData.t.size = seed->t.size;
	for( i = 0; i < nvWriteData.t.size; i++ ){
		nvWriteData.t.buffer[i] = seed->t.buffer[i];
	}

	// just create a NV index
	rval = Tss2_Sys_NV_DefineSpace( sysContext, TPM_RH_PLATFORM, &sessionsData, &nvAuth, &publicInfo, 0 );
	if(rval == TPM_RC_NV_DEFINED){
		DebugPrintf( NO_PREFIX, "Already defined, attemp to do read/write test \n" );

		indexExist = true;
		goto verify;
	}
	
	CheckPassed(rval);	

	rval = Tss2_Sys_NV_Write( sysContext, TPM_RH_PLATFORM, NV_INDEX_OS_SEED_0, &sessionsData, &nvWriteData, 0, 0 );
	CheckPassed( rval );

	for( i = 0; i < nvWriteData.t.size; i++ )
		DebugPrintf( NO_PREFIX, "%02X ", nvWriteData.t.buffer[i] );
    DebugPrintf( NO_PREFIX, "\n");	
	
	// Lock the write to prevent further write access
	rval = Tss2_Sys_NV_WriteLock( sysContext, TPM_RH_PLATFORM, NV_INDEX_OS_SEED_0, &sessionsData, 0 );
	CheckPassed( rval );

verify:
	
	// test after being locked, write will fail (since TPMA_NV_WRITEDEFINE is set)
	rval = Tss2_Sys_NV_Write( sysContext, TPM_RH_PLATFORM, NV_INDEX_OS_SEED_0, &sessionsData, &nvWriteData, 0, 0);
	CheckFailed( rval, TPM_RC_NV_LOCKED );

	// read to verify (test 2 times)
	for( i = 0; i < 2; i ++){
		// read to verify
		INIT_SIMPLE_TPM2B_SIZE( nvReadData );
		rval = Tss2_Sys_NV_Read( sysContext, TPM_RH_PLATFORM, NV_INDEX_OS_SEED_0, &sessionsData, NV_INDEX_OS_SEED_SIZE, 0, &nvReadData, 0 );
		CheckPassed( rval );
		if( indexExist == false){
			rval = CompareSizedByteBuffer(&(seed->b), &(nvReadData.b));
			CheckPassed( rval );
		}

		rval = Tss2_Sys_NV_Read( sysContext, NV_INDEX_OS_SEED_0, NV_INDEX_OS_SEED_0, &sessionsData, NV_INDEX_OS_SEED_SIZE, 0, &nvReadData, 0 );
		CheckFailed( rval, TPM_RC_AUTH_UNAVAILABLE );
	}

	// now it is time for locking it to prevent read any more (unless restart)
	rval = Tss2_Sys_NV_ReadLock(sysContext,TPM_RH_PLATFORM, NV_INDEX_OS_SEED_0, &sessionsData, 0 );
	CheckPassed( rval );

	// read to verify after being locked.
	INIT_SIMPLE_TPM2B_SIZE( nvReadData );
	rval = Tss2_Sys_NV_Read( sysContext, TPM_RH_PLATFORM, NV_INDEX_OS_SEED_0, &sessionsData, NV_INDEX_OS_SEED_SIZE, 0, &nvReadData, 0 );
	CheckFailed( rval, TPM_RC_NV_LOCKED );

	// also locked!
	rval = Tss2_Sys_NV_Read( sysContext, NV_INDEX_OS_SEED_0, NV_INDEX_OS_SEED_0, &sessionsData, NV_INDEX_OS_SEED_SIZE, 0, &nvReadData, 0 );
	CheckFailed( rval, TPM_RC_NV_LOCKED );


    // simulation: do a TPM reset and Startup (to re-enable platform hierarchy)
    rval = Tss2_Sys_Shutdown( sysContext, 0, TPM_SU_STATE, 0 );
    CheckPassed( rval );
    rval = Tss2_Sys_Shutdown( sysContext, 0, TPM_SU_CLEAR, 0 );
    CheckPassed( rval );
    rval = TpmReset();
    CheckPassed(rval);
    rval = Tss2_Sys_Startup ( sysContext, TPM_SU_CLEAR );
    CheckPassed( rval );

	// now we can read it again
	INIT_SIMPLE_TPM2B_SIZE( nvReadData );
	rval = Tss2_Sys_NV_Read( sysContext, TPM_RH_PLATFORM, NV_INDEX_OS_SEED_0, &sessionsData, NV_INDEX_OS_SEED_SIZE, 0, &nvReadData, 0 );
	CheckPassed( rval );
	if( indexExist == false){
		rval = CompareSizedByteBuffer(&(seed->b), &(nvReadData.b));
		CheckPassed( rval );
	}

	// write will fail (since TPMA_NV_WRITEDEFINE is set)
	rval = Tss2_Sys_NV_Write( sysContext, TPM_RH_PLATFORM, NV_INDEX_OS_SEED_0, &sessionsData, &nvWriteData, 0, 0);
	CheckFailed( rval, TPM_RC_NV_LOCKED );

	
	// lock it again to prevent reading any more (unless restart)
	rval = Tss2_Sys_NV_ReadLock(sysContext,TPM_RH_PLATFORM, NV_INDEX_OS_SEED_0, &sessionsData, 0 );
	CheckPassed( rval );

	// read to verify after being locked.
	INIT_SIMPLE_TPM2B_SIZE( nvReadData );
	rval = Tss2_Sys_NV_Read( sysContext, TPM_RH_PLATFORM, NV_INDEX_OS_SEED_0, &sessionsData, NV_INDEX_OS_SEED_SIZE, 0, &nvReadData, 0 );
	CheckFailed( rval, TPM_RC_NV_LOCKED );

	rval = Tss2_Sys_NV_Read( sysContext, NV_INDEX_OS_SEED_0, NV_INDEX_OS_SEED_0, &sessionsData, NV_INDEX_OS_SEED_SIZE, 0, &nvReadData, 0 );
	CheckFailed( rval, TPM_RC_NV_LOCKED );

}

// 
// Name/ID: NV_INDEX_OS_SEED_OWNER
// Description: fuse the TRK (SEED) to a NV index
// Size: 32B
// Policy: Read when PCRs match trusted boot and  OS, Write once for ever
// Input: the SEED of  ( Root Key, TRK)
//
void TestFuseSeedReadPolicyWritePolicyOwner(TPM2B_DIGEST *seed)
{
	UINT32 rval;
    
    TPMI_RH_PROVISION	authHandle = TPM_RH_OWNER;
    TPMI_RH_NV_INDEX    nvIndex = NV_INDEX_OS_SEED_OWNER;

//    bool writen = FALSE;

	TPM2B_NV_PUBLIC publicInfo;
	TPM2B_AUTH	nvAuth;
	TPMS_AUTH_COMMAND sessionData;
	TSS2_SYS_CMD_AUTHS sessionsData;
	int i;
	TPM2B_MAX_NV_BUFFER nvWriteData;
	TPM2B_MAX_NV_BUFFER nvReadData;
	TPMI_SH_AUTH_SESSION nvSessionHandle = 0;
	bool                 indexExist = false;

	TPM2B_DIGEST policyDigest = { { sizeof (TPM2B_DIGEST) - 2, } };


    TSS2_SYS_RSP_AUTHS sessionsDataOut = { 1, };
    TPMS_AUTH_RESPONSE sessionDataOut;
    TPMS_AUTH_RESPONSE *sessionDataOutArray[1];

	TPMS_AUTH_COMMAND *sessionDataArray[1];

    sessionDataOutArray[0] = &sessionDataOut;
    sessionsDataOut.rspAuths = &sessionDataOutArray[0];

	sessionDataArray[0] = &sessionData;
	sessionsData.cmdAuths = &sessionDataArray[0];

	DebugPrintf( NO_PREFIX, "\nStart to do one-time fuse for  SEED:\n" );

	// auth value (random? -  doesn't matter!!!)
	nvAuth.t.size = 0;

	publicInfo.t.size = sizeof( TPMI_RH_NV_INDEX ) +
						sizeof( TPMI_ALG_HASH ) + 
						sizeof( TPMA_NV ) + 
						sizeof( UINT16) +
						sizeof( UINT16 );
	publicInfo.t.nvPublic.nvIndex = nvIndex;
	publicInfo.t.nvPublic.nameAlg = TPM_ALG_SHA256;

	// First zero out attributes.
	*(UINT32 *)&( publicInfo.t.nvPublic.attributes ) = 0;

	// Now set the attributes.
	publicInfo.t.nvPublic.attributes.TPM_NT                 = TPM_NT_ORDINARY;
	publicInfo.t.nvPublic.attributes.TPMA_NV_POLICYREAD     = 1;
	publicInfo.t.nvPublic.attributes.TPMA_NV_POLICYWRITE    = 1;
//	publicInfo.t.nvPublic.attributes.TPMA_NV_OWNERWRITE    = 1;
	publicInfo.t.nvPublic.attributes.TPMA_NV_WRITEDEFINE    = 1; //TPM2_NV_WriteLock() may be used to prevent 
	                                                             //further writes to this location (unless deleted)

    publicInfo.t.nvPublic.attributes.TPMA_NV_READ_STCLEAR    = 1; //TPM2_NV_ReadLock() may be used to prevent 
                                                                 //further read to this location (unless reboot)

	publicInfo.t.nvPublic.attributes.TPMA_NV_WRITEALL       = 1;
//	publicInfo.t.nvPublic.attributes.TPMA_NV_PLATFORMCREATE = 1; 

	// We need to set a policy (never satisfied) to prevent this NV index deletion.
//	publicInfo.t.nvPublic.attributes.TPMA_NV_POLICY_DELETE = 1; 


	// start to build the policy digest with trial session
	rval = BuildSeedReadPolicy(sysContext, &nvSessionHandle, &policyDigest, true);
	CheckPassed( rval );

	nvSessionHandle = 0;

	// enable policy for this index now
	CopySizedByteBuffer( &(publicInfo.t.nvPublic.authPolicy.b), &(policyDigest.b) );

	// SHA256
	publicInfo.t.nvPublic.dataSize = NV_INDEX_OS_SEED_SIZE; 


	InitNullSession(&sessionData);

	sessionsData.cmdAuthsCount = 1;
	sessionsData.cmdAuths[0] = &sessionData;

	// make sure the data size is the same with publicInfo.t.nvPublic.dataSize
	// and the seed.t.size < MAX_NV_INDEX_SIZE
	if(seed->t.size >= MAX_NV_INDEX_SIZE || 
	   seed->t.size != NV_INDEX_OS_SEED_SIZE ||
	   seed->t.size != publicInfo.t.nvPublic.dataSize)
		CheckPassed(-1);

	
	nvWriteData.t.size = seed->t.size;
	for( i = 0; i < nvWriteData.t.size; i++ ){
		nvWriteData.t.buffer[i] = seed->t.buffer[i];
	}

	rval = Tss2_Sys_NV_UndefineSpace( sysContext, authHandle, nvIndex, &sessionsData, 0);
//	CheckPassed(rval);	

	// just create a NV index
	rval = Tss2_Sys_NV_DefineSpace( sysContext, authHandle, &sessionsData, &nvAuth, &publicInfo, 0 );
	if(rval == TPM_RC_NV_DEFINED){
		DebugPrintf( NO_PREFIX, "Already defined, attemp to do read test \n" );

		indexExist = true;
		goto verify;
	}
	
	CheckPassed(rval);	

verify:
	
		// start to build the policy digest with real session

        InitNullSession(&sessionData);
        
        sessionsData.cmdAuthsCount = 1;
        sessionsData.cmdAuths[0] = &sessionData;
        nvSessionHandle = 0;

        // test policy write and write lock
            rval = BuildSeedReadPolicy(sysContext, &nvSessionHandle, &policyDigest, false);
            CheckPassed( rval );
            
            // apply policy session
            sessionData.sessionHandle     = nvSessionHandle;
            sessionData.hmac.t.size       = 0;
            sessionData.nonce.t.size      = 0;
            *( (UINT8 *)((void *)&( sessionData.sessionAttributes ) ) ) = 0;
            sessionData.sessionAttributes.continueSession = 1;
            
            rval = Tss2_Sys_NV_Write( sysContext, nvIndex, nvIndex, &sessionsData, &nvWriteData, 0, &sessionsDataOut  );
            CheckPassed( rval );
            
            rval = BuildSeedReadPolicy(sysContext, &nvSessionHandle, &policyDigest, false);
            CheckPassed( rval );
            
            // apply policy session
            sessionData.sessionHandle     = nvSessionHandle;
            sessionData.hmac.t.size       = 0;
            sessionData.nonce.t.size      = 0;
            *( (UINT8 *)((void *)&( sessionData.sessionAttributes ) ) ) = 0;
            sessionData.sessionAttributes.continueSession = 1;
            
            rval = Tss2_Sys_NV_WriteLock( sysContext, nvIndex, nvIndex, &sessionsData, 0 );
            CheckPassed( rval );

    		rval = Tss2_Sys_FlushContext( sysContext, nvSessionHandle );
    		CheckPassed( rval );

            InitNullSession(&sessionData);
            
            sessionsData.cmdAuthsCount = 1;
            sessionsData.cmdAuths[0] = &sessionData;
            nvSessionHandle = 0;
        
            rval = BuildSeedReadPolicy(sysContext, &nvSessionHandle, &policyDigest, false);
            CheckPassed( rval );
            
            // apply policy session
            sessionData.sessionHandle     = nvSessionHandle;
            sessionData.hmac.t.size       = 0;
            sessionData.nonce.t.size      = 0;
            *( (UINT8 *)((void *)&( sessionData.sessionAttributes ) ) ) = 0;
            sessionData.sessionAttributes.continueSession = 1;
            
            rval = Tss2_Sys_NV_Write( sysContext, nvIndex, nvIndex, &sessionsData, &nvWriteData, 0, &sessionsDataOut  );
            CheckFailed( rval, TPM_RC_NV_LOCKED );

        // test policy read 
		rval = Tss2_Sys_FlushContext( sysContext, nvSessionHandle );
		CheckPassed( rval );

        InitNullSession(&sessionData);
        
        sessionsData.cmdAuthsCount = 1;
        sessionsData.cmdAuths[0] = &sessionData;
        nvSessionHandle = 0;

		rval = BuildSeedReadPolicy(sysContext, &nvSessionHandle, &policyDigest, false);
		CheckPassed( rval );

		// apply policy session
		sessionData.sessionHandle     = nvSessionHandle;
		sessionData.hmac.t.size       = 0;
		sessionData.nonce.t.size      = 0;
		*( (UINT8 *)((void *)&( sessionData.sessionAttributes ) ) ) = 0;
	    sessionData.sessionAttributes.continueSession = 1;

		// read to verify
		INIT_SIMPLE_TPM2B_SIZE( nvReadData );
		rval = Tss2_Sys_NV_Read( sysContext, nvIndex, nvIndex, &sessionsData, NV_INDEX_OS_SEED_SIZE, 0, &nvReadData, 0 );
		CheckPassed( rval );
		if( indexExist == false){
			rval = CompareSizedByteBuffer(&(seed->b), &(nvReadData.b));
			CheckPassed( rval );
		}

		rval = Tss2_Sys_FlushContext( sysContext, nvSessionHandle );
		CheckPassed( rval );

		InitNullSession(&sessionData);
		// read to verify
		INIT_SIMPLE_TPM2B_SIZE( nvReadData );
		rval = Tss2_Sys_NV_Read( sysContext, authHandle, nvIndex, &sessionsData, NV_INDEX_OS_SEED_SIZE, 0, &nvReadData, 0 );
		CheckFailed( rval, TPM_RC_NV_AUTHORIZATION );

		rval = Tss2_Sys_NV_Read( sysContext, nvIndex, nvIndex, &sessionsData, NV_INDEX_OS_SEED_SIZE, 0, &nvReadData, 0 );
		CheckFailed( rval, TPM_RC_AUTH_UNAVAILABLE );

        // test read lock 
        sessionsData.cmdAuthsCount = 1;
        sessionsData.cmdAuths[0] = &sessionData;
        nvSessionHandle = 0;

        rval = BuildSeedReadPolicy(sysContext, &nvSessionHandle, &policyDigest, false);
		CheckPassed( rval );

		// apply policy session
		sessionData.sessionHandle     = nvSessionHandle;
		sessionData.hmac.t.size       = 0;
		sessionData.nonce.t.size      = 0;
		*( (UINT8 *)((void *)&( sessionData.sessionAttributes ) ) ) = 0;
	    sessionData.sessionAttributes.continueSession = 1;

		// read to verify
		INIT_SIMPLE_TPM2B_SIZE( nvReadData );
		rval = Tss2_Sys_NV_ReadLock( sysContext, nvIndex, nvIndex, &sessionsData, 0 );
		CheckPassed( rval );


		rval = Tss2_Sys_FlushContext( sysContext, nvSessionHandle );
		CheckPassed( rval );

        InitNullSession(&sessionData);
        
        sessionsData.cmdAuthsCount = 1;
        sessionsData.cmdAuths[0] = &sessionData;
        nvSessionHandle = 0;


        rval = BuildSeedReadPolicy(sysContext, &nvSessionHandle, &policyDigest, false);
		CheckPassed( rval );

		// apply policy session
		sessionData.sessionHandle     = nvSessionHandle;
		sessionData.hmac.t.size       = 0;
		sessionData.nonce.t.size      = 0;
		*( (UINT8 *)((void *)&( sessionData.sessionAttributes ) ) ) = 0;
	    sessionData.sessionAttributes.continueSession = 1;

		// read to verify
		INIT_SIMPLE_TPM2B_SIZE( nvReadData );
		rval = Tss2_Sys_NV_Read( sysContext, nvIndex, nvIndex, &sessionsData, NV_INDEX_OS_SEED_SIZE, 0, &nvReadData, 0 );
		CheckFailed( rval, TPM_RC_NV_LOCKED );
}

void TestFuseSeed()
{	
	TPM2B_DIGEST randomSeed = { { sizeof (TPM2B_DIGEST) - 2, } };
	UINT32 rval;

	// generate a random value (as SEED secret) on create
	rval = Tss2_Sys_GetRandom( sysContext, 0, NV_INDEX_OS_SEED_SIZE, &randomSeed, 0);
	CheckPassed( rval );

	// read once (per boot)	
	// @0 - read once (per boot), write once for all
	TestFuseSeedReadOnceWriteOnce(&randomSeed);

	// read with policy	
	// @1 - read with PCR_0 & PCR_18 match, write once
	TestFuseSeedReadPolicyWriteOnce(&randomSeed);
	
	// read and write with policy under owner hierarchy	
	// @1 - read and write with PCR_0 & PCR_18 match, enable read/write lock
	TestFuseSeedReadPolicyWritePolicyOwner(&randomSeed);
}


^ permalink raw reply	[flat|nested] 2+ messages in thread

* [tpm2] Seal/Unseal NV Index to PCRs
@ 2018-07-17 22:02 Arvind Kumar
  0 siblings, 0 replies; 2+ messages in thread
From: Arvind Kumar @ 2018-07-17 22:02 UTC (permalink / raw)
  To: tpm2

[-- Attachment #1: Type: text/plain, Size: 412 bytes --]

Hi,

I'm a beginner who needs to securely store disk partition encryption key
(luks key) in TPM 2.0. I have been looking around for examples but most of
them only cover theory. I need to figure out following - How to seal an NV
index to set of PCRs where these PCRs will be updated on system upgrade
later.

I need to understand the process with examples, any help is appreciated !


Thanks,
Arvind

[-- Attachment #2: attachment.html --]
[-- Type: text/html, Size: 520 bytes --]

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2018-07-18  9:26 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-07-18  9:26 [tpm2] Seal/Unseal NV Index to PCRs Zhu, Bing
  -- strict thread matches above, loose matches on Subject: below --
2018-07-17 22:02 Arvind Kumar

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.