/// <summary>
/// This sample demonstrates a policy containing ALL policy commands.
/// It also demonstrates serialization of the policy, and the use
/// of callbacks to satisfy the conditions in a policy (e.g. knowledge
/// of a private key, or the NV-index associated with a name.
/// </summary>
/// <param name="tpm">Reference to the TPM used.</param>
static void SamplePolicySerializationAndCallbacks(Tpm2 tpm)
{
Console.WriteLine("Policy sample that serializes all policy commands.");
//
// Check if policy commands are implemented by TPM. This list
// could include all the other used commands as well.
// This check here makes sense for policy commands, because
// usually a policy has to be executed in full. If a command
// out of the chain of policy commands is not implemented in the
// TPM, the policy cannot be satisfied.
//
var usedCommands = new[] {
TpmCc.PolicyPhysicalPresence,
TpmCc.PolicySigned,
TpmCc.PolicySecret,
TpmCc.PolicyPCR,
TpmCc.PolicyLocality,
TpmCc.PolicyNV,
TpmCc.PolicyCounterTimer,
TpmCc.PolicyCommandCode,
TpmCc.PolicyPassword,
TpmCc.PolicyAuthorize,
TpmCc.PolicyPhysicalPresence,
TpmCc.PolicyCpHash,
TpmCc.PolicyTicket,
TpmCc.PolicyNameHash,
TpmCc.PolicyCpHash,
TpmCc.PolicyDuplicationSelect,
TpmCc.PolicyAuthValue,
TpmCc.PolicyNvWritten
};
foreach (var commandCode in usedCommands)
{
if (!tpm.Helpers.IsImplemented(commandCode))
{
Console.WriteLine("Cancel Policy serialization and callback sample, because command {0} is not implemented by TPM.", commandCode);
return;
}
}
//
// AuthValue encapsulates an authorization value: essentially a byte-array.
// OwnerAuth is the owner authorization value of the TPM-under-test. We
// assume that it (and other) auths are set to the default (null) value.
// If running on a real TPM, which has been provisioned by Windows, this
// value will be different. An administrator can retrieve the owner
// authorization value from the registry.
//
var ownerAuth = new AuthValue();
var pInit = new PolicyTree(TpmAlgId.Sha256);
var p = new PolicyTree(TpmAlgId.Sha256);
//
// In the first part of this sample we establish keys, NV-slots,
// etc. that will be used in the policy.
//
//
// create a new RSA software signing key. We will use this for both
// TpmPolicySigned AND TpmPolicyAuthorize.
//
var signKeyPublicTemplate = new TpmPublic(TpmAlgId.Sha256,
ObjectAttr.Sign | ObjectAttr.Restricted | ObjectAttr.FixedTPM,
new byte[0],
new RsaParms(new SymDefObject(),
new SchemeRsassa(TpmAlgId.Sha256),
2048, 0),
new Tpm2bPublicKeyRsa());
_publicSigningKey = new AsymCryptoSystem(signKeyPublicTemplate);
//
// Get an authorization ticket for TpmPolicyAuthorize. We will authorize
// a policy-digest consisting of policyPhysPresense.
//
var tempPolicy = new PolicyTree(TpmAlgId.Sha256);
tempPolicy.Create(
new PolicyAce[]
{
new TpmPolicyPhysicalPresence(),
"leaf"
});
TpmHash initPolicyHash = tempPolicy.GetPolicyDigest();
var policyAuthRef = new byte[0];
byte[] dataToSign = Globs.Concatenate(initPolicyHash.HashData, policyAuthRef);
byte[] aHash = CryptoLib.HashData(TpmAlgId.Sha256,
Globs.Concatenate(initPolicyHash.HashData, policyAuthRef));
//
// Sign the simple policy just containing PolicyPhysPres so that
// we can change it to a new value with PolicyAuthorize.
//
ISignatureUnion policyAuthSig = _publicSigningKey.Sign(dataToSign);
//
// Get a ticket verifying the signature.
//
TpmHandle verifierHandle = tpm.LoadExternal(null, _publicSigningKey.GetPublicParms(), TpmHandle.RhOwner);
tpm.VerifySignature(verifierHandle, aHash, policyAuthSig);
tpm.FlushContext(verifierHandle);
//
// Get the value of PCR[1]
//
var pcrs = new uint[] { 1 };
var sel = new PcrSelection(TpmAlgId.Sha, pcrs);
PcrSelection[] selOut;
Tpm2bDigest[] pcrValues;
tpm.PcrRead(new[] { sel }, out selOut, out pcrValues);
//
// Save the current PCR values in a convenient data structure
//
var expectedPcrVals = new PcrValueCollection(selOut, pcrValues);
//
// Set up an NV slot
//
TpmHandle nvHandle = TpmHandle.NV(3001);
//
// Clean anything that might have been there before
//
tpm[ownerAuth]._AllowErrors().NvUndefineSpace(TpmHandle.RhOwner, nvHandle);
AuthValue nvAuth = AuthValue.FromRandom(8);
tpm[ownerAuth].NvDefineSpace(TpmHandle.RhOwner, nvAuth, new NvPublic(nvHandle, TpmAlgId.Sha1,
NvAttr.TpmaNvAuthread | NvAttr.TpmaNvAuthwrite, new byte[0], 32));
//
// write some data
//
var nvData = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 };
tpm[nvAuth].NvWrite(nvHandle, nvHandle, nvData, 0);
byte[] nvName;
tpm.NvReadPublic(nvHandle, out nvName);
//
// Install evaluation callback
// Note: generally the callback will check that the parameters are
// actions that it is willing to authorize. Those checks are omitted here.
//
p.SetNvCallback((PolicyTree policyTree,
TpmPolicyNV ace,
out SessionBase authorizingSession,
out TpmHandle authorizedEntityHandle,
out TpmHandle nvHandleIs) =>
{
authorizedEntityHandle = nvHandle;
nvHandleIs = nvHandle;
authorizingSession = nvAuth;
});
//
// counter-timer: The policy will check that the reset-count
// is the current value.
//
int start, end;
TimeInfo now = tpm.ReadClock();
Marshaller.GetFragmentInfo(now, "resetCount", out start, out end);
byte[] operandB = Marshaller.GetTpmRepresentation(now.clockInfo.resetCount);
//
// Get a cpHash for the command we want to execute
//
var cpHash = new TpmHash(TpmAlgId.Sha256);
tpm._GetCpHash(cpHash).HierarchyChangeAuth(TpmHandle.RhOwner, ownerAuth);
p.SetSignerCallback(SignerCallback);
//
// PolicySecret tests knowledge of ownerAuth. Note that the callback
// will generally check that it is prepared to authorize what it is
// being asked to authorize. Those checks are omitted here (we just
// provide a PWAP session containing ownerAuth.
//
p.SetPolicySecretCallback((PolicyTree policyTree,
TpmPolicySecret ace,
out SessionBase authorizingSession,
out TpmHandle authorizedEntityHandle,
out bool flushAuthEntity) =>
{
authorizingSession = ownerAuth;
authorizedEntityHandle = TpmHandle.RhOwner;
flushAuthEntity = false;
});
//
// If the policy contains a TpmPolicyAction then print out the
// action string on the console.
//
p.SetPolicyActionCallback((PolicyTree policy, TpmPolicyAction ace)
=> Console.WriteLine(ace.Action));
var policyRef = new byte[] { 1, 2, 3, 4 };
//
// Ticket expiration times have to be negative.
// Positive expiration times do not generate a ticket.
//
_expectedExpirationTime = -60;
//
// A normalized policy is an array of policy-chains written as
// arrays. Here "most" of the policy-ACEs are in the first chain, but some
// ACEs cannot co-exist, and some need a ticket from a prior evaluation.
//
pInit.CreateNormalizedPolicy(
new[]
{
new PolicyAce[]
{
new TpmPolicySigned(_publicSigningKey.GetPublicParms().GetName(),
// Newly created PubKey
true, // Nonce in signed data
_expectedExpirationTime, // expirationTime
new byte[0], // cpHash
policyRef) // policyRef
{NodeId = "Signing Key 1"}, // Distinguishing name
//
// Include owner-auth
//
new TpmPolicySecret(TpmHandle.RhOwner.GetName(), true,
new byte[0], new byte[] {1, 2, 3}, 0),
//
// Include PCR-values read earlier
//
new TpmPolicyPcr(expectedPcrVals),
//
// Command must be issued at locality two
//
new TpmPolicyLocality(LocalityAttr.TpmLocTwo),
//
// NV-data we set earlier must be present
//
new TpmPolicyNV(nvName, nvData, 0, Eo.Eq),
//
// This is a "dummy ACE" that is not executed on the TPM but
// a callback will be invoked at when the policy is executed.
// One use case for this is to increment a counter between two
// PolicyNV counter-checks.
//
new TpmPolicyAction("Output of TpmPolicyAction when executed."),
//
// Boot-count must be what we read earlier
//
new TpmPolicyCounterTimer(operandB, (ushort) start, Eo.Eq),
//
// Only authorize HierarchyChangeAuth
//
new TpmPolicyCommand(TpmCc.HierarchyChangeAuth),
//
// Include password
//
new TpmPolicyPassword(),
//
// Authorize a change from PolicyPP (last ACE below)
//
new TpmPolicyAuthorize(initPolicyHash.HashData,
policyAuthRef,
_publicSigningKey.GetPublicParms(),
TpmAlgId.Sha256,
policyAuthSig),
//
// Demand that the command be executed with PP asserted
//
new TpmPolicyPhysicalPresence(),
//
// Name for this branch
//
"branch_1"
},
new PolicyAce[]
{
//
// Bind to command/parameters
//
new TpmPolicyCpHash(cpHash),
//
// Name for this branch
//
"branch_2"
},
new PolicyAce[]
{
new TpmPolicyTicket(_publicSigningKey.GetPublicParms(),
policyRef,
TpmSt.AuthSigned)
//
// Distinguishing name for this node
//
{NodeId = "PolicyTicket"},
//
// Name for this branch
//
"branch_3"
},
//
// TODO: These ACEs are not evaluated yet in this sample
//
new PolicyAce[]
{
new TpmPolicyNameHash(),
new TpmPolicyCpHash(cpHash),
new TpmPolicyDuplicationSelect(new byte[0], new byte[0], true),
new TpmPolicyAuthValue(), // Include entity authValue in HMAC
new TpmPolicyNvWritten(),
"branch_4"
}
}
);
TpmHash policyHash = pInit.GetPolicyDigest();
//
// Check that we can serialize and deserialize the policy
//
const string fileName = @".\test1.xml";
pInit.SerializeToFile("Sample Policy",PolicySerializationFormat.Xml, fileName);
p.DeserializeFromFile(PolicySerializationFormat.Xml, fileName);
//
// And check that the policy hash is the same
//
TpmHash deserializedHash = p.GetPolicyDigest();
if (policyHash != deserializedHash)
{
throw new Exception("Serialization error");
}
//
// Execute the policy on the TPM. Start with "branch_1".
//
AuthSession s0 = tpm.StartAuthSessionEx(TpmSe.Policy, TpmAlgId.Sha256);
s0.RunPolicy(tpm, p, "branch_1");
//
// Check that the executed policy has the correct digest
//
byte[] actualPolicyDigest = tpm.PolicyGetDigest(s0.Handle);
if (policyHash != actualPolicyDigest)
{
throw new Exception("Policy Evaluation error");
}
//
// Set a command to use the policy
//
tpm[ownerAuth].SetPrimaryPolicy(TpmHandle.RhOwner, policyHash.HashData, TpmAlgId.Sha256);
//
// And then execute the command
//
tpm._AssertPhysicalPresence(true);
tpm._SetLocality(LocalityAttr.TpmLocTwo);
tpm[s0].HierarchyChangeAuth(TpmHandle.RhOwner, ownerAuth);
tpm._SetLocality(LocalityAttr.TpmLocZero);
tpm._AssertPhysicalPresence(false);
tpm.FlushContext(s0.Handle);
//
// Next, "branch_2".
//
s0 = tpm.StartAuthSessionEx(TpmSe.Policy, TpmAlgId.Sha256);
s0.RunPolicy(tpm, p, "branch_2");
tpm[s0].HierarchyChangeAuth(TpmHandle.RhOwner, ownerAuth);
tpm.FlushContext(s0.Handle);
//
// Now "branch_3" - ticket. Copy parms out of the ticket/ACE returned
// from TpmPolicySinged above.
//
var sigAce = p.GetAce<TpmPolicySigned>("Signing Key 1");
TkAuth signedTicket = p.GetTicket("Signing Key 1");
var tickAce = p.GetAce<TpmPolicyTicket>("PolicyTicket");
tickAce.CpHash = sigAce.CpHash;
tickAce.PolicyRef = sigAce.PolicyRef;
tickAce.ExpirationTime = sigAce.GetTimeout();
tickAce.SetTicket(signedTicket);
s0 = tpm.StartAuthSessionEx(TpmSe.Policy, TpmAlgId.Sha256);
s0.RunPolicy(tpm, p, "branch_3");
tpm[s0].HierarchyChangeAuth(TpmHandle.RhOwner, ownerAuth);
tpm.FlushContext(s0.Handle);
Console.WriteLine("Finished SamplePolicySerializationAndCallbacks.");
}