コード例 #1
0
ファイル: SlotManager.cs プロジェクト: alex1818/TSS.MSR
        /// <summary>
        /// Updates TBS context database for commands that either fill or empty slots.
        /// </summary>
        /// <param name="caller"></param>
        /// <param name="command"></param>
        /// <param name="responseHandles"></param>
        /// <param name="inputObjects"></param>
        private void ProcessUpdatedTpmState(TbsContext caller, CommandInfo command, TpmHandle[] responseHandles, ObjectContext[] inputObjects)
        {
            switch (command.CommandCode)
            {
            // Commands that fill a slot (apart from contextLoad, which is more complex)
            case TpmCc.Load:
            case TpmCc.LoadExternal:
            case TpmCc.CreatePrimary:
            case TpmCc.HmacStart:
            case TpmCc.HashSequenceStart:
            case TpmCc.StartAuthSession:
                var t = new TpmHandle(responseHandles[0].handle);
                // ReSharper disable once UnusedVariable
                ObjectContext context2 = ContextManager.CreateObjectContext(caller, t);
                break;

            case TpmCc.ContextLoad:
            case TpmCc.ContextSave:
                throw new Exception("should not be here");

            case TpmCc.FlushContext:
            case TpmCc.SequenceComplete:
                ContextManager.Remove(inputObjects[0]);
                break;

            case TpmCc.EventSequenceComplete:
                ContextManager.Remove(inputObjects[1]);
                break;
            }
        }
コード例 #2
0
ファイル: SlotManager.cs プロジェクト: alex1818/TSS.MSR
 /// <summary>
 /// Removes all TPM objects referenced by this context and then removes the context from the TBS database.
 /// </summary>
 /// <param name="c"></param>
 public void DisposeContext(TbsContext c)
 {
     lock (this)
     {
         foreach (ObjectContext o in ContextManager.ObjectContexts)
         {
             if (o.Owner != c)
             {
                 continue;
             }
             if (o.Loaded)
             {
                 Tpm.FlushContext(o.TheTpmHandle);
                 o.Loaded = false;
             }
             else
             {
                 if (o.TheSlotType == SlotType.SessionSlot)
                 {
                     // TODO: Need to flush saved sessions?
                     // Tpm.FlushContext(o.TheTpmHandle);
                     Tpm.FlushContext(o.Context.savedHandle);
                 }
             }
         }
         ContextManager.RemoveAll(c);
     }
 }
コード例 #3
0
ファイル: SlotManager.cs プロジェクト: pdb0102/TSS.MSR
        /// <summary>
        /// Look up TBS ObjectContext records given the handles in the inHandles input parms.
        /// </summary>
        /// <param name="caller"></param>
        /// <param name="inHandles"></param>
        /// <returns></returns>
        private ObjectContext[] GetReferencedObjects(TbsContext caller, TpmHandle[] inHandles)
        {
            var neededContexts = new ObjectContext[inHandles.Length];

            for (int j = 0; j < inHandles.Length; j++)
            {
                neededContexts[j] = ContextManager.GetContext(caller, inHandles[j]);
                if (neededContexts[j] == null)
                {
                    return(null);
                }
            }
            return(neededContexts);
        }
コード例 #4
0
ファイル: SlotManager.cs プロジェクト: pdb0102/TSS.MSR
        /// <summary>
        /// Get the TBS ObjectContext given SessionIn objects collected from the inputs stream.
        /// </summary>
        /// <param name="caller"></param>
        /// <param name="inSessions"></param>
        /// <returns></returns>
        private ObjectContext[] GetSessions(TbsContext caller, SessionIn[] inSessions)
        {
            var contexts = new ObjectContext[inSessions.Length];

            for (int j = 0; j < inSessions.Length; j++)
            {
                contexts[j] = ContextManager.GetContext(caller, inSessions[j].handle);
                if (contexts[j] == null)
                {
                    return(null);
                }
            }
            return(contexts);
        }
コード例 #5
0
ファイル: SlotManager.cs プロジェクト: pdb0102/TSS.MSR
 public TbsContext CreateTbsContext()
 {
     lock (this)
     {
         TbsContext newContext = null;
         try
         {
             newContext = new TbsContext(this);
             return(newContext);
         }
         catch (Exception)
         {
             if (newContext != null)
             {
                 newContext.Dispose();
             }
             return(null);
         }
     }
 }
コード例 #6
0
ファイル: SlotManager.cs プロジェクト: Microsoft/TSS.MSR
        public TbsContext CreateTbsContext()
        {
            lock (this)
            {
                TbsContext newContext = null;
                try
                {
                    newContext = new TbsContext(this);
                    return newContext;
                }
                catch (Exception)
                {
                    if (newContext != null)
                    {
                        newContext.Dispose();
                    }
                    return null;
                }

            }
        }
コード例 #7
0
ファイル: SlotManager.cs プロジェクト: pdb0102/TSS.MSR
 /// <summary>
 /// Removes all TPM objects referenced by this context and then removes
 /// the context from the TBS database.
 /// </summary>
 /// <param name="c"></param>
 public void DisposeContext(TbsContext c)
 {
     lock (this)
     {
         foreach (ObjectContext o in ContextManager.ObjectContexts)
         {
             if (o.Owner != c)
             {
                 continue;
             }
             if (o.Loaded)
             {
                 Tpm._AllowErrors()
                 .FlushContext(o.TheTpmHandle);
                 if (!Tpm._LastCommandSucceeded())
                 {
                     Debug.WriteLine("TRM failed to flush a handle: {0:X8}", o.TheTpmHandle);
                 }
                 o.Loaded = false;
             }
         }
         ContextManager.RemoveAll(c);
     }
 }
コード例 #8
0
ファイル: SlotManager.cs プロジェクト: pdb0102/TSS.MSR
 void ProcessTpmClear(TbsContext caller, Tbs.SlotType excluded = Tbs.SlotType.NoSlot)
 {
     ContextManager.ObjectContexts.RemoveAll(ctx => ctx.Owner == caller &&
                                             ctx.TheSlotType != excluded);
     Debug.Assert(excluded != Tbs.SlotType.NoSlot || ContextManager.ObjectContexts.Count == 0);
 }
コード例 #9
0
ファイル: SlotManager.cs プロジェクト: pdb0102/TSS.MSR
        /// <summary>
        /// Dispatch a command to the underlying TPM. This method implements all
        /// significant functionality. It examines the command stream and performs
        /// (approximately) the following actions:
        /// 1) If the command references a handle (session or transient object), then
        ///     TBS makes sure that the entity  is loaded. If it is, then the handle is
        ///     "translated" to the underlying TPM handle. If it is not, then TBS checks
        ///     to see if it has a saved context for the entity, and if so, loads it.
        /// 2) If the command will fill a slot, then TBS ensures that a slot is available.
        ///     It does this by ContextSaving the LRU entity of the proper type (that is
        ///     not used in this command).
        /// </summary>
        /// <param name="caller"></param>
        /// <param name="active"></param>
        /// <param name="inBuf"></param>
        /// <param name="outBuf"></param>
        /// <exception cref="Exception"></exception>
        internal void DispatchCommand(TbsContext caller, CommandModifier active, byte[] inBuf, out byte[] outBuf)
        {
            lock (this)
            {
                CommandNumber++;
                // ReSharper disable once CompareOfFloatsByEqualityOperator
                if (StateSaveProbability != 0.0)
                {
                    // S3 debug support
                    DebugStateSave();
                    LastStateSaveCommandNumber = CommandNumber;
                }

                CommandHeader commandHeader;
                TpmHandle[]   inHandles;
                SessionIn[]   inSessions;
                byte[]        commandParmsNoHandles;
                bool          legalCommand = CommandProcessor.CrackCommand(inBuf,
                                                                           out commandHeader, out inHandles, out inSessions, out commandParmsNoHandles);

                if (!legalCommand)
                {
                    // Is a diagnostics command.  Pass through to TPM (a real RM would refuse).
                    TpmDevice.DispatchCommand(active, inBuf, out outBuf);
                    return;
                }

                TpmCc cc = commandHeader.CommandCode;

                // Lookup command
                CommandInfo command = Tpm2.CommandInfoFromCommandCode(cc);
                if (command == null)
                {
                    throw new Exception("Unrecognized command");
                }

                if (cc == TpmCc.ContextLoad || cc == TpmCc.ContextSave)
                {
                    Debug.WriteLine("ContextLoad and ContextSave are not supported in this build");
                    outBuf = Marshaller.GetTpmRepresentation(new Object[] {
                        TpmSt.NoSessions,
                        (uint)10,
                        TpmRc.NotUsed
                    });
                }

                // Look up referenced objects and sessions
                ObjectContext[] neededObjects  = GetReferencedObjects(caller, inHandles);
                ObjectContext[] neededSessions = GetSessions(caller, inSessions);
                ObjectContext[] neededEntities =
                    neededObjects != null
                            ? neededSessions != null
                                ? neededObjects.Concat(neededSessions).ToArray()
                                : neededObjects
                            : neededSessions;

#if false
                // LibTester may intentionally use invalid handles, therefore it always
                // work in the passthru mode (all correctness checks by TSS infra suppressed)
                if (!Tpm2._TssBehavior.Passthrough &&
                    (neededObjects == null || neededSessions == null))
#endif
                if (neededObjects == null || neededSessions == null)
                {
                    // One or more of the handles was not registered for the context
                    byte[] ret = FormatError(TpmRc.Handle);
                    outBuf = ret;
                    return;
                }

                // Load referenced objects and sessions (free slots if needed)
                // It's important to load all object and session handles in a single call
                // to LoadEntities(), as for some commands (e.g. GetSessionAuditDigest)
                // the objects array may contain session handles. In this case the session
                // handles loaded by the invocation of LoadEntities for neededObjects
                // may be evicted again during the subsequent call for neededSessions.
                var expectedResponses = Tpm._GetExpectedResponses();
                if (!LoadEntities(neededEntities))
                {
                    throw new Exception("Failed to make space for objects or sessions");
                }
                else
                {
                    // At this point everything referenced should be loaded, and
                    // there will be a free slot if needed so we can translate
                    // the input handles to the underlying handles
                    ReplaceHandlesIn(inHandles, inSessions, neededObjects, neededSessions);
                }

                // Re-create the command using translated object and session handles
                byte[] commandBuf = CommandProcessor.CreateCommand(commandHeader.CommandCode,
                                                                   inHandles, inSessions, commandParmsNoHandles);
                if (!Tpm2._TssBehavior.Passthrough)
                {
                    Debug.Assert(commandBuf.Length == inBuf.Length);
                }

                byte[] responseBuf;

                // TODO: Virtualize TPM2_GetCapability() for handle enumeration.

                //
                // Execute command on underlying TPM device.
                // If we get an ObjectMemory or SessionMemory error we try to make more space and try again
                // Note: If the TPM device throws an error above we let it propagate out.  There should be no side
                // effects on TPM state that the TBS cares about.
                //
                ulong firstCtxSeqNum = 0;
                while (true)
                {
                    Tpm._ExpectResponses(expectedResponses);
                    TpmDevice.DispatchCommand(active, commandBuf, out responseBuf);

                    TpmRc res = GetResultCode(responseBuf);
                    if (res == TpmRc.Success ||
                        expectedResponses != null && expectedResponses.Contains(res))
                    {
                        break;
                    }

                    if (res == TpmRc.ContextGap)
                    {
                        ulong seqNum = ShortenSessionContextGap(firstCtxSeqNum);
                        if (seqNum == 0)
                        {
                            break;  // Failed to handle CONTEXT_GAP error
                        }
                        if (firstCtxSeqNum == 0)
                        {
                            firstCtxSeqNum = seqNum;
                        }

                        //if (firstCtxSeqNum != 0)
                        //    Console.WriteLine("DispatchCommand: CONTEXT_GAP handled");
                        continue;
                    }

                    var slotType = SlotType.NoSlot;
                    if (res == TpmRc.ObjectHandles || res == TpmRc.ObjectMemory)
                    {
                        slotType = SlotType.ObjectSlot;
                    }
                    else if (res == TpmRc.SessionHandles || res == TpmRc.SessionMemory)
                    {
                        slotType = SlotType.SessionSlot;
                    }
                    else
                    {
                        // Command failure not related to resources
                        break;
                    }
                    if (!MakeSpace(slotType, neededEntities))
                    {
                        // Failed to make an object slot in the TPM
                        responseBuf = TpmErrorHelpers.BuildErrorResponseBuffer(TpmRc.Memory);
                        break;
                    }
                }

                // Parse the response from the TPM
                TpmSt        responseTag;
                uint         responseParamSize;
                TpmRc        resultCode;
                TpmHandle[]  responseHandles;
                SessionOut[] responseSessions;
                byte[]       responseParmsNoHandles, responseParmsWithHandles;

                CommandProcessor.SplitResponse(responseBuf,
                                               command.HandleCountOut,
                                               out responseTag,
                                               out responseParamSize,
                                               out resultCode,
                                               out responseHandles,
                                               out responseSessions,
                                               out responseParmsNoHandles,
                                               out responseParmsWithHandles);

                // In case of an error there is no impact on the loaded sessions, but
                // we update the LRU values because the user will likely try again.
                if (resultCode != TpmRc.Success)
                {
                    outBuf = responseBuf;
                    UpdateLastUseCount(new[] { neededObjects, neededSessions });
                    return;
                }

                // Update TBS database with any newly created TPM objects
                ProcessUpdatedTpmState(caller, command, responseHandles, neededObjects);

                // And if there were any newly created objects use the new DB entries
                // to translate the handles
                ReplaceHandlesOut(responseHandles);
                outBuf = CommandProcessor.CreateResponse(resultCode, responseHandles,
                                                         responseSessions, responseParmsNoHandles);

                Debug.Assert(outBuf.Length == responseBuf.Length);
            } // lock(this)
        }
コード例 #10
0
ファイル: SlotManager.cs プロジェクト: alex1818/TSS.MSR
        /// <summary>
        /// Dispatch a command to the underlying TPM. This method implements all significant functionality.
        /// DispatchCommand examines the command stream and performs (approximately) the following functions
        /// 1) If the command references a handle (session or transient object) then TBS makes sure that the entity
        ///     is loaded. If it is, then the handle is "translated" to the underlying TPM handle. If it is not, then
        ///     TBS checks to see if it has a saved context for the entity, and if so loads it.
        /// 2) If the command will fill a slot, then TBS ensures that a slot is available. It does this by ContextSaving
        ///     the LRU entity of the proper type (that is not used in this command).
        /// </summary>
        /// <param name="caller"></param>
        /// <param name="active"></param>
        /// <param name="inBuf"></param>
        /// <param name="outBuf"></param>
        /// <exception cref="Exception"></exception>
        internal void DispatchCommand(TbsContext caller, CommandModifier active, byte[] inBuf, out byte[] outBuf)
        {
            lock (this)
            {
                CommandNumber++;
                // ReSharper disable once CompareOfFloatsByEqualityOperator
                if (StateSaveProbability != 0.0)
                {
                    // S3 debug support
                    DebugStateSave();
                    LastStateSaveCommandNumber = CommandNumber;
                }

                CommandHeader commandHeader;
                TpmHandle[]   inHandles;
                SessionIn[]   inSessions;
                byte[]        commandParmsNoHandles;
                bool          legalCommand = CommandProcessor.CrackCommand(inBuf, out commandHeader, out inHandles, out inSessions, out commandParmsNoHandles);

                if (!legalCommand)
                {
                    // Is a diagnostics command.  Pass through to TPM (a real RM would refuse).
                    TpmDevice.DispatchCommand(active, inBuf, out outBuf);
                    return;
                }

                TpmCc commandCode = commandHeader.CommandCode;

                // Lookup command
                CommandInfo command = Tpm2.CommandInfoFromCommandCode(commandCode);
                if (command == null)
                {
                    throw new Exception("Unrecognized command");
                }

                if (commandCode == TpmCc.ContextLoad || commandCode == TpmCc.ContextSave)
                {
                    //throw new Exception("ContextLoad and ContextSave not supported in this build");
                    Console.Error.WriteLine("ContextLoad and ContextSave not supported in this build");
                    outBuf = Marshaller.GetTpmRepresentation(new Object[] {
                        TpmSt.NoSessions,
                        (uint)10,
                        TpmRc.NotUsed
                    });
                }

                // Look up referenced objects and sessions
                ObjectContext[] neededObjects  = GetReferencedObjects(caller, inHandles);
                ObjectContext[] neededSessions = GetSessions(caller, inSessions);
                if (neededObjects == null || neededSessions == null)
                {
                    // This means that one or more of the handles was not registered for the context
                    byte[] ret = FormatError(TpmRc.Handle);
                    outBuf = ret;
                    return;
                }

                // Load referenced objects and sessions (free slots if needed)
                bool loadOk  = LoadEntities(neededObjects);
                bool loadOk2 = LoadEntities(neededSessions);
                if (!loadOk || !loadOk2)
                {
                    throw new Exception("Failed to make space for objects or sessions at to execute command");
                }

                // At this point everything referenced should be loaded, and there will be a free slot if needed
                // so we can translate the input handles to the underlying handles
                ReplaceHandlesIn(inHandles, inSessions, neededObjects, neededSessions);

                // create the translated command from the various components we have been manipulating
                byte[] commandBuf = CommandProcessor.CreateCommand(commandHeader.CommandCode, inHandles, inSessions, commandParmsNoHandles);
                Debug.Assert(commandBuf.Length == inBuf.Length);

                byte[] responseBuf;

                // Todo: Virtualize GetCapability for handle enumeration.

                //
                // Execute command on underlying TPM device.
                // If we get an ObjectMemory or SessionMemory error we try to make more space and try again
                // Note: If the TPM device throws an error above we let it propagate out.  There should be no side
                // effects on TPM state that the TBS cares about.
                //
                do
                {
                    TpmDevice.DispatchCommand(active, commandBuf, out responseBuf);
                    TpmRc resCode = GetResultCode(responseBuf);
                    if (resCode == TpmRc.Success)
                    {
                        break;
                    }
                    if (resCode == TpmRc.ObjectMemory)
                    {
                        bool slotMade = MakeSpace(SlotType.ObjectSlot, neededObjects);
                        if (!slotMade)
                        {
                            throw new Exception("Failed to make an object slot in the TPM");
                        }
                        continue;
                    }
                    if (resCode == TpmRc.SessionMemory)
                    {
                        bool slotMade = MakeSpace(SlotType.SessionSlot, neededSessions);
                        if (!slotMade)
                        {
                            throw new Exception("Failed to make a session slot in the TPM");
                        }
                        continue;
                    }
                    break;
                } while (true);

                // Parse the response from the TPM
                // TODO: Make this use the new methods in Tpm2

                // ReSharper disable once UnusedVariable
                var          mOut = new Marshaller(responseBuf);
                TpmSt        responseTag;
                uint         responseParamSize;
                TpmRc        resultCode;
                TpmHandle[]  responseHandles;
                SessionOut[] responseSessions;
                byte[]       responseParmsNoHandles, responseParmsWithHandles;

                CommandProcessor.SplitResponse(responseBuf,
                                               command.HandleCountOut,
                                               out responseTag,
                                               out responseParamSize,
                                               out resultCode,
                                               out responseHandles,
                                               out responseSessions,
                                               out responseParmsNoHandles,
                                               out responseParmsWithHandles);

                // If we have an error there is no impact on the loaded sessions, but we update
                // the LRU values because the user will likely try again.
                if (resultCode != TpmRc.Success)
                {
                    outBuf = responseBuf;
                    UpdateLastUseCount(new[] { neededObjects, neededSessions });
                    return;
                }

                // Update TBS database with any newly created TPM objects
                ProcessUpdatedTpmState(caller, command, responseHandles, neededObjects);

                // And if there were any newly created objects use the new DB entries to translate the handles
                ReplaceHandlesOut(responseHandles);
                byte[] translatedResponse = CommandProcessor.CreateResponse(resultCode, responseHandles, responseSessions, responseParmsNoHandles);

                outBuf = translatedResponse;
                Debug.Assert(outBuf.Length == responseBuf.Length);
            } // lock(this)
        }
コード例 #11
0
ファイル: SlotManager.cs プロジェクト: Microsoft/TSS.MSR
 /// <summary>
 /// Look up TBS ObjectContext records given the handles in the inHandles input parms.
 /// </summary>
 /// <param name="caller"></param>
 /// <param name="inHandles"></param>
 /// <returns></returns>
 private ObjectContext[] GetReferencedObjects(TbsContext caller, TpmHandle[] inHandles)
 {
     var neededContexts = new ObjectContext[inHandles.Length];
     for (int j = 0; j < inHandles.Length; j++)
     {
         neededContexts[j] = ContextManager.GetContext(caller, inHandles[j]);
         if (neededContexts[j] == null)
         {
             return null;
         }
     }
     return neededContexts;
 }
コード例 #12
0
ファイル: SlotManager.cs プロジェクト: Microsoft/TSS.MSR
 /// <summary>
 /// Removes all TPM objects referenced by this context and then removes the context from the TBS database.
 /// </summary>
 /// <param name="c"></param>
 public void DisposeContext(TbsContext c)
 {
     lock (this)
     {
         foreach (ObjectContext o in ContextManager.ObjectContexts)
         {
             if (o.Owner != c)
             {
                 continue;
             }
             if (o.Loaded)
             {
                 Tpm.FlushContext(o.TheTpmHandle);
                 o.Loaded = false;
             }
             else
             {
                 if (o.TheSlotType == SlotType.SessionSlot)
                 {
                     // TODO: Need to flush saved sessions?
                     // Tpm.FlushContext(o.TheTpmHandle);
                     Tpm.FlushContext(o.Context.savedHandle);
                 }
             }
         }
         ContextManager.RemoveAll(c);
     }
 }
コード例 #13
0
ファイル: SlotManager.cs プロジェクト: Microsoft/TSS.MSR
 /// <summary>
 /// Get the TBS ObjectContext given SessionIn objects collected from the inputs stream.
 /// </summary>
 /// <param name="caller"></param>
 /// <param name="inSessions"></param>
 /// <returns></returns>
 private ObjectContext[] GetSessions(TbsContext caller, SessionIn[] inSessions)
 {
     var contexts = new ObjectContext[inSessions.Length];
     for (int j = 0; j < inSessions.Length; j++)
     {
         contexts[j] = ContextManager.GetContext(caller, inSessions[j].handle);
         if (contexts[j] == null)
         {
             return null;
         }
     }
     return contexts;
 }
コード例 #14
0
ファイル: SlotManager.cs プロジェクト: Microsoft/TSS.MSR
 /// <summary>
 /// Updates TBS context database for commands that either fill or empty slots.
 /// </summary>
 /// <param name="caller"></param>
 /// <param name="command"></param>
 /// <param name="responseHandles"></param>
 /// <param name="inputObjects"></param>
 private void ProcessUpdatedTpmState(TbsContext caller, CommandInfo command, TpmHandle[] responseHandles, ObjectContext[] inputObjects)
 {
     switch (command.CommandCode)
     {
         // Commands that fill a slot (apart from contextLoad, which is more complex)
         case TpmCc.Load:
         case TpmCc.LoadExternal:
         case TpmCc.CreatePrimary:
         case TpmCc.HmacStart:
         case TpmCc.HashSequenceStart:
         case TpmCc.StartAuthSession:
             var t = new TpmHandle(responseHandles[0].handle);
             // ReSharper disable once UnusedVariable
             ObjectContext context2 = ContextManager.CreateObjectContext(caller, t);
             break;
         case TpmCc.ContextLoad:
         case TpmCc.ContextSave:
             throw new Exception("ProcessUpdatedTpmState: Should not be here");
         case TpmCc.FlushContext:
         case TpmCc.SequenceComplete:
             ContextManager.Remove(inputObjects[0]);
             break;
         case TpmCc.EventSequenceComplete:
             ContextManager.Remove(inputObjects[1]);
             break;
     }
 }
コード例 #15
0
ファイル: SlotManager.cs プロジェクト: Microsoft/TSS.MSR
        /// <summary>
        /// Dispatch a command to the underlying TPM. This method implements all significant functionality.
        /// DispatchCommand examines the command stream and performs (approximately) the following functions
        /// 1) If the command references a handle (session or transient object) then TBS makes sure that the entity 
        ///     is loaded. If it is, then the handle is "translated" to the underlying TPM handle. If it is not, then
        ///     TBS checks to see if it has a saved context for the entity, and if so loads it.
        /// 2) If the command will fill a slot, then TBS ensures that a slot is available. It does this by ContextSaving
        ///     the LRU entity of the proper type (that is not used in this command).
        /// </summary>
        /// <param name="caller"></param>
        /// <param name="active"></param>
        /// <param name="inBuf"></param>
        /// <param name="outBuf"></param>
        /// <exception cref="Exception"></exception>
        internal void DispatchCommand(TbsContext caller, CommandModifier active, byte[] inBuf, out byte[] outBuf)
        {
            lock (this)
            {
                CommandNumber++;
                // ReSharper disable once CompareOfFloatsByEqualityOperator
                if (StateSaveProbability != 0.0)
                {
                    // S3 debug support
                    DebugStateSave();
                    LastStateSaveCommandNumber = CommandNumber;
                }

                CommandHeader commandHeader;
                TpmHandle[] inHandles;
                SessionIn[] inSessions;
                byte[] commandParmsNoHandles;
                bool legalCommand = CommandProcessor.CrackCommand(inBuf, out commandHeader, out inHandles, out inSessions, out commandParmsNoHandles);

                if (!legalCommand)
                {
                    // Is a diagnostics command.  Pass through to TPM (a real RM would refuse).
                    TpmDevice.DispatchCommand(active, inBuf, out outBuf);
                    return;
                }

                TpmCc commandCode = commandHeader.CommandCode;

                // Lookup command
                CommandInfo command = Tpm2.CommandInfoFromCommandCode(commandCode);
                if (command == null)
                {
                    throw new Exception("Unrecognized command");
                }

                if (commandCode == TpmCc.ContextLoad || commandCode == TpmCc.ContextSave)
                {
                    //throw new Exception("ContextLoad and ContextSave not supported in this build");
#if !NETFX_CORE
                    Console.Error.WriteLine("ContextLoad and ContextSave not supported in this build");
#endif
                    outBuf = Marshaller.GetTpmRepresentation(new Object[] {
                        TpmSt.NoSessions,
                        (uint)10,
                        TpmRc.NotUsed });
                }

                // Look up referenced objects and sessions
                ObjectContext[] neededObjects = GetReferencedObjects(caller, inHandles);
                ObjectContext[] neededSessions = GetSessions(caller, inSessions);
                ObjectContext[] neededEntities =
                        neededObjects != null
                            ? neededSessions != null
                                ? neededObjects.Concat(neededSessions).ToArray()
                                : neededObjects
                            : neededSessions;
                if (neededObjects == null || neededSessions == null)
                {
                    // This means that one or more of the handles was not registered for the context
                    byte[] ret = FormatError(TpmRc.Handle);
                    outBuf = ret;
                    return;
                }

                // Load referenced objects and sessions (free slots if needed)
                // It's important to load all object and session handles in a single call
                // to LoadEntities(), as for some commands (e.g. GetSessionAuditDigest)
                // the objects array may contain session handles. In this case the session
                // handles loaded by the invocation of LoadEntities for neededObjects
                // may be evicted again during the subsequent call for neededSessions.
                bool loadOk = LoadEntities(neededEntities);
                if (!loadOk)
                {
                    throw new Exception("Failed to make space for objects or sessions at to execute command");
                }

                // At this point everything referenced should be loaded, and there will be a free slot if needed
                // so we can translate the input handles to the underlying handles 
                ReplaceHandlesIn(inHandles, inSessions, neededObjects, neededSessions);

                // create the translated command from the various components we have been manipulating
                byte[] commandBuf = CommandProcessor.CreateCommand(commandHeader.CommandCode, inHandles, inSessions, commandParmsNoHandles);
                Debug.Assert(commandBuf.Length == inBuf.Length);

                byte[] responseBuf;
                
                // Todo: Virtualize GetCapability for handle enumeration.

                //
                // Execute command on underlying TPM device.
                // If we get an ObjectMemory or SessionMemory error we try to make more space and try again
                // Note: If the TPM device throws an error above we let it propagate out.  There should be no side 
                // effects on TPM state that the TBS cares about.
                //
                do
                {
                    TpmDevice.DispatchCommand(active, commandBuf, out responseBuf);
                    TpmRc res = GetResultCode(responseBuf);
                    if (res == TpmRc.Success)
                    {
                        break;
                    }

                    var slotType = SlotType.NoSlot;
                    if (res == TpmRc.ObjectHandles || res == TpmRc.ObjectMemory)
                    {
                        slotType = SlotType.ObjectSlot;
                    }
                    else if (res == TpmRc.SessionHandles || res == TpmRc.SessionMemory)
                    {
                        slotType = SlotType.SessionSlot;
                    }
                    else
                    {
                        // Command failure not related to resources
                        break;
                    }
                    bool slotMade = MakeSpace(slotType, neededEntities);
                    if (!slotMade)
                    {
                        throw new Exception("Failed to make an object slot in the TPM");
                    }
                } while (true);

                // Parse the response from the TPM
                // TODO: Make this use the new methods in Tpm2

                // ReSharper disable once UnusedVariable
                var mOut = new Marshaller(responseBuf);
                TpmSt responseTag;
                uint responseParamSize;
                TpmRc resultCode;
                TpmHandle[] responseHandles;
                SessionOut[] responseSessions;
                byte[] responseParmsNoHandles, responseParmsWithHandles;

                CommandProcessor.SplitResponse(responseBuf,
                                               command.HandleCountOut,
                                               out responseTag,
                                               out responseParamSize,
                                               out resultCode,
                                               out responseHandles,
                                               out responseSessions,
                                               out responseParmsNoHandles,
                                               out responseParmsWithHandles);

                // If we have an error there is no impact on the loaded sessions, but we update
                // the LRU values because the user will likely try again.
                if (resultCode != TpmRc.Success)
                {
                    outBuf = responseBuf;
                    UpdateLastUseCount(new[] {neededObjects, neededSessions});
                    return;
                }

                // Update TBS database with any newly created TPM objects
                ProcessUpdatedTpmState(caller, command, responseHandles, neededObjects);

                // And if there were any newly created objects use the new DB entries to translate the handles
                ReplaceHandlesOut(responseHandles);
                byte[] translatedResponse = CommandProcessor.CreateResponse(resultCode, responseHandles, responseSessions, responseParmsNoHandles);

                outBuf = translatedResponse;
                Debug.Assert(outBuf.Length == responseBuf.Length);
            } // lock(this)
        }