/// <summary>
/// A "normalized" policy is one transformed into disjunctive normal form,
/// in which a collection of policy "AND chains" is combined with PolicyOR
/// before submission to the TPM.
/// Callers must provide an-array-of-arrays of TpmPolicyACEs. The arrays may NOT
/// contain PolicyOr (these will be added automatically), but each array MUST be
/// terminated with a unique string identifier encoded in a TpmPolicyChainId.
/// </summary>
/// <param name="policy"></param>
public void CreateNormalizedPolicy(PolicyAce[][] policy)
{
// To validate that the input does not have any repeated branchIds or ACEs
var branchIdDict = new Dictionary <string, string>();
var aces = new HashSet <object>();
int numBranches = 0;
bool unnamedBranches = false;
// The following code validates and transforms the array-of-arrays into a linked
// list + OR nodes tree. First collect lists of chains in the chains collection.
var chains = new List <PolicyAce>();
foreach (PolicyAce[] chain in policy)
{
numBranches++;
PolicyAce leaf = null;
PolicyAce previousAce = null;
PolicyAce root = null;
// Turn the array into a doubly-linked list
foreach (PolicyAce ace in chain)
{
// Repeats are illegal
if (aces.Contains(ace))
{
Globs.Throw <ArgumentException>("CreateNormalizedPolicy: " +
"Repeated ACE in policy");
}
// Already associated with a session is illegal
if (ace.AssociatedPolicy != null)
{
Globs.Throw <ArgumentException>("CreateNormalizedPolicy: " +
"ACE is already associated with a policy");
}
ace.AssociatedPolicy = this;
aces.Add(ace);
// OR is illegal in normal form (these are added automatically
// at the root to union the arrays that are input to this function).
if (ace is TpmPolicyOr)
{
Globs.Throw <ArgumentException>("CreateNormalizedPolicy: " +
"Normalized form cannot contain TpmPolicyOr");
}
if (previousAce != null)
{
previousAce.NextAce = ace;
}
ace.PreviousAce = previousAce;
previousAce = ace;
// Is the branchId valid?
string branchId = ace.BranchID;
if (!String.IsNullOrEmpty(branchId))
{
if (branchIdDict.ContainsKey(branchId))
{
Globs.Throw <ArgumentException>("CreateNormalizedPolicy: " +
"Repeated branch-identifier " + branchId);
}
branchIdDict.Add(branchId, "");
}
if (root == null)
{
root = ace;
}
leaf = ace;
}
// Does the leaf have a branch ID?
if (leaf != null && String.IsNullOrEmpty(leaf.BranchID))
{
unnamedBranches = true;
}
// Else we have a good chain starting at root
chains.Add(root);
}
if (unnamedBranches && numBranches != 1)
{
throw new ArgumentException("Policy-chain leaf does not have a branch identifier");
}
// We now have a list of chains in chains.
int numChains = chains.Count;
// A single chain (no ORs)
if (numChains == 1)
{
PolicyRoot = chains[0];
return;
}
// Each TPM_or can take up to 8 inputs. We will add OR-aces to the root
// to capture all chains. The algorithm is that we create an OR and keep
// adding chains in the input order until full (if it is the last chain)
// or one-less than full. Then create a new OR, attach it to the last OR
// and keep filling as before.
var theRoot = new TpmPolicyOr();
TpmPolicyOr currentOrAce = theRoot;
for (int j = 0; j < numChains; j++)
{
bool lastChain = (j == numChains - 1);
if ((currentOrAce.PolicyBranches.Count < 7) || lastChain)
{
currentOrAce.AddPolicyBranch(chains[j]);
}
else
{
// We have overflowed the TpmPolicyOr so add a new child-OR
// attached to the previous as the final (8th) branch.
var nextOr = new TpmPolicyOr();
currentOrAce.AddPolicyBranch(nextOr);
currentOrAce = nextOr;
currentOrAce.AddPolicyBranch(chains[j]);
}
}
// All input chains are connected up to one or more ORs at the root so we are done.
PolicyRoot = theRoot;
}