/// <summary>
/// This sample illustrates the creation and use of an RSA signing key to
/// "quote" PCR state
/// </summary>
/// <param name="tpm">Reference to the TPM object.</param>
static void QuotePcrs(Tpm2 tpm)
{
//
// First use a library routine to create an RSA/AES primary storage key
// with null user-auth.
//
TpmPublic rsaPrimaryPublic;
var primaryAuth = new byte[0];
TpmHandle primHandle = CreateRsaPrimaryStorageKey(tpm, primaryAuth, out rsaPrimaryPublic);
//
// Template for a signing key. We will make the key restricted so that we
// can quote with it too.
//
var signKeyPubTemplate = new TpmPublic(TpmAlgId.Sha1,
ObjectAttr.Sign | ObjectAttr.Restricted | // A "quoting" key
ObjectAttr.FixedParent | ObjectAttr.FixedTPM | // Non-duplicable
ObjectAttr.UserWithAuth | // Authorize with auth-data
ObjectAttr.SensitiveDataOrigin, // TPM will create a new key
new byte[0],
new RsaParms(new SymDefObject(), new SchemeRsassa(TpmAlgId.Sha1), 2048, 0),
new Tpm2bPublicKeyRsa());
//
// Auth-data for new key
//
var userAuth = new byte[] { 1, 2, 3, 4 };
var sensCreate = new SensitiveCreate(userAuth, new byte[0]);
//
// Creation data (not used in this sample)
//
CreationData childCreationData;
TkCreation creationTicket;
byte[] creationHash;
//
// Create the key
//
TpmPublic keyPub;
TpmPrivate keyPriv = tpm[primaryAuth].Create(primHandle, // Child of primary key created above
sensCreate, // Auth-data
signKeyPubTemplate, // Template created above
new byte[0], // Other parms are not used here
new PcrSelection[0],
out keyPub,
out childCreationData, out creationHash, out creationTicket);
Console.WriteLine("New public key\n" + keyPub.ToString());
//
// Load the key as a child of the primary that it
// was created under.
//
TpmHandle signHandle = tpm[primaryAuth].Load(primHandle, keyPriv, keyPub);
//
// Note that Load returns the "name" of the key and this is automatically
// associated with the handle.
//
Console.WriteLine("Name of key:" + BitConverter.ToString(signHandle.Name));
//
// Aome data to quote
//
TpmHash hashToSign = TpmHash.FromData(TpmAlgId.Sha1, new byte[] { 4, 3, 2, 1 });
//
// PCRs to quote. SHA-1 bank, PCR-indices 1, 2, and 3
//
var pcrsToQuote = new PcrSelection[]
{
new PcrSelection(TpmAlgId.Sha, new uint[] { 1, 2, 3 })
};
//
// Ask the TPM to quote the PCR (and the nonce). The TPM
// returns the quote-signature and the data that was signed
//
ISignatureUnion quoteSig;
Attest quotedInfo = tpm[userAuth].Quote(signHandle,
hashToSign.HashData,
new SchemeRsassa(TpmAlgId.Sha1),
pcrsToQuote,
out quoteSig);
//
// Print out what was quoted
//
var info = (QuoteInfo)quotedInfo.attested;
Console.WriteLine("PCRs that were quoted: " +
info.pcrSelect[0].ToString() +
"\nHash of PCR-array: " +
BitConverter.ToString(info.pcrDigest));
//
// Read the PCR to check the quoted value
//
PcrSelection[] outSelection;
Tpm2bDigest[] outValues;
tpm.PcrRead(new PcrSelection[] {
new PcrSelection(TpmAlgId.Sha, new uint[] { 1, 2, 3 })
},
out outSelection,
out outValues);
//
// Use the Tpm2Lib library to validate the quote against the
// values just read.
//
bool quoteOk = keyPub.VerifyQuote(TpmAlgId.Sha1,
outSelection,
outValues,
hashToSign.HashData,
quotedInfo,
quoteSig);
if (!quoteOk)
{
throw new Exception("Quote did not validate");
}
Console.WriteLine("Quote correctly validated.");
//
// Test other uses of the signing key. A restricted key can only
// sign data that the TPM knows does not start with a magic
// number (that identifies TPM internal data). So this does not
// work
//
var nullProof = new TkHashcheck(TpmHandle.RhNull, new byte[0]);
tpm[userAuth]._ExpectError(TpmRc.Ticket).Sign(signHandle,
hashToSign.HashData,
new SchemeRsassa(TpmAlgId.Sha1),
nullProof);
//
// But if we ask the TPM to hash the same data and then sign it
// then the TPM can be sure that the data is safe, so it will
// sign it.
//
TkHashcheck safeHashTicket;
TpmHandle hashHandle = tpm.HashSequenceStart(_nullAuth, TpmAlgId.Sha1);
//
// The ticket is only generated if the data is "safe."
//
tpm[_nullAuth].SequenceComplete(hashHandle,
new byte[] { 4, 3, 2, 1 },
TpmHandle.RhOwner,
out safeHashTicket);
//
// This will now work because the ticket proves to the
// TPM that the data that it is about to sign does not
// start with TPM_GENERATED
//
ISignatureUnion sig = tpm[userAuth].Sign(signHandle,
hashToSign.HashData,
new SchemeRsassa(TpmAlgId.Sha1),
safeHashTicket);
//
// And we can verify the signature
//
bool sigOk = keyPub.VerifySignatureOverData(new byte[] { 4, 3, 2, 1 }, sig);
if (!sigOk)
{
throw new Exception("Signature did not verify");
}
Console.WriteLine("Signature verified.");
//
// Clean up
//
tpm.FlushContext(primHandle);
tpm.FlushContext(signHandle);
}