Пример #1
0
 /// <summary>
 /// Opens a properly-formed TPM command stream into its constituent components.
 /// Note: commandParams does NOT include handles.
 /// </summary>
 /// <param name="command"></param>
 /// <param name="header"></param>
 /// <param name="handles"></param>
 /// <param name="sessions"></param>
 /// <param name="commandParms"></param>
 public static bool CrackCommand(
     byte[] command,
     out CommandHeader header,
     out TpmHandle[] handles,
     out SessionIn[] sessions,
     out byte[] commandParms)
 {
     var m = new Marshaller(command);
     header = m.Get<CommandHeader>();
     CommandInfo commandInfo = Tpm2.CommandInfoFromCommandCode(header.CommandCode);
     if (header.Tag == TpmSt.Null)
     {
         // A diagnostics command. Pass through unmodified
         handles = null;
         sessions = null;
         commandParms = null;
         return false;
     }
     handles = new TpmHandle[commandInfo.HandleCountIn];
     for (int j = 0; j < handles.Length; j++)
     {
         handles[j] = m.Get<TpmHandle>();
     }
     // Note sessions are only present if the command tag indicates sessions
     if (header.Tag == TpmSt.Sessions)
     {
         var sessionLength = m.Get<uint>();
         uint sessionEnd = m.GetGetPos() + sessionLength;
         var inSessions = new List<SessionIn>();
         while (m.GetGetPos() < sessionEnd)
         {
             var s = m.Get<SessionIn>();
             inSessions.Add(s);
         }
         sessions = inSessions.ToArray();
     }
     else
     {
         sessions = new SessionIn[0];
     }
     // And finally parameters
     commandParms = m.GetArray<byte>((int)(m.GetValidLength() - m.GetGetPos()));
     if (m.GetValidLength() != header.CommandSize)
     {
         throw new Exception("Command length in header does not match input byte-stream");
     }
     return true;
 }
Пример #2
0
        public static void SplitResponse(
            byte[] response,
            uint numHandles,
            out TpmSt tag,
            out uint paramSize,
            out TpmRc responseCode,
            out TpmHandle[] handles,
            out SessionOut[] sessions,
            out byte[] responseParmsNoHandles,
            out byte[] responseParmsWithHandles)
        {
            var m = new Marshaller(response);
            tag = m.Get<TpmSt>();
            paramSize = m.Get<uint>();
            responseCode = m.Get<TpmRc>();
            // If error we only get the header
            if (responseCode != TpmRc.Success)
            {
                handles = new TpmHandle[0];
                sessions = new SessionOut[0];
                responseParmsNoHandles = new byte[0];
                responseParmsWithHandles = new byte[0];
                return;
            }

            handles = new TpmHandle[numHandles];
            for (int j = 0; j < numHandles; j++)
            {
                handles[j] = m.Get<TpmHandle>();
            }
            uint parmsEnd = m.GetValidLength();
            if (tag == TpmSt.Sessions)
            {
                var sessionOffset = m.Get<uint>();
                uint startOfParmsX = m.GetGetPos();
                parmsEnd = startOfParmsX + sessionOffset;
                m.SetGetPos(parmsEnd);
                var sessX = new List<SessionOut>();
                while (m.GetGetPos() < m.GetValidLength())
                {
                    var s = m.Get<SessionOut>();
                    sessX.Add(s);
                }
                sessions = sessX.ToArray();
                m.SetGetPos(startOfParmsX);
            }
            else
            {
                sessions = new SessionOut[0];
            }

            uint startOfParms = m.GetGetPos();
            uint parmsLength = parmsEnd - m.GetGetPos();

            // Get the response buf with no handles
            responseParmsNoHandles = new byte[parmsLength];
            Array.Copy(response, (int)startOfParms, responseParmsNoHandles, 0, (int)parmsLength);

            // Get the response buf with handles
            responseParmsWithHandles = new byte[parmsLength + numHandles * 4];
            Array.Copy(response, 10, responseParmsWithHandles, 0, (int)numHandles * 4);
            Array.Copy(response, (int)startOfParms, responseParmsWithHandles, (int)numHandles * 4, (int)parmsLength);
        }
Пример #3
0
        /// <summary>
        /// DispatchMethod is called by auto-generated command action code. It assembles a byte[] containing
        /// the formatted TPM command based on the params passed in explicitly, and the sessions currently attached
        /// to the TPM object.  It processes the TPM response and converts it into an instantiation of the 
        /// requested object.
        /// </summary>
        /// <param name="ordinal"></param>
        /// <param name="inParms"></param>
        /// <param name="expectedResponseType"></param>
        /// <param name="outParms"></param>
        /// <param name="numInHandlesNotUsed"></param>
        /// <param name="numOutHandlesNotUsed"></param>
        /// <returns></returns>
        internal bool DispatchMethod(
            TpmCc ordinal,
            TpmStructureBase inParms,
            Type expectedResponseType,
            out TpmStructureBase outParms,
            int numInHandlesNotUsed,
            int numOutHandlesNotUsed)
        {
            outParms = null;
            // todo - ClearCommandContext should be moved to the front of this
            // routine (and we should make local copies of the context-values we depend upon)
            // There are uncaught exceptions that can be generated that would skip
            // ClearCOmmandCOntext and leave the Tpm2 with some leftover state.

            byte[] response;

            if (CurrentCommand != TpmCc.None)
            {
                OuterCommand = CurrentCommand;
            }
            CurrentCommand = ordinal;

            // The AlternateActionCallback allows alternate processing (or filtering/data
            // collection on the executing command stream.
            if (TheAlternateActionCallback != null)
            {
                bool desiredSuccessCode;
                bool alternate = TheAlternateActionCallback(ordinal,
                                                            inParms,
                                                            expectedResponseType,
                                                            out outParms,
                                                            out desiredSuccessCode);
                if (alternate)
                {
                    _ClearCommandContext();
                    return desiredSuccessCode;
                }
            }

            CommandInfo commandInfo = CommandInfoFromCommandCode(ordinal);
            byte[] parms;
            TpmHandle[] inHandles;

            try
            {
                // Get the handles and the parameters from the command input structure
                CommandProcessor.Fragment(inParms, commandInfo.HandleCountIn, out inHandles, out parms);

                // Start processing sessions
                PrepareRequestSessions(commandInfo, inHandles);
            }
            catch (Exception e)
            {
                Debug.Assert(outParms == null);
                if (e is TpmException)
                {
                    if (IsErrorAllowed(((TpmException)e).RawResponse))
                    {
                        outParms = (TpmStructureBase)Activator.CreateInstance(expectedResponseType);
                    }
                }
                _ClearCommandPrelaunchContext();
                _ClearCommandContext();
                if (outParms != null)
                {
                    return false;
                }
                throw;
            }

            // The caller can install observer/modifier callbacks, and request repeated
            // execution of the same command.
            bool repeat = false;
            byte[] parmsCopy = null;

            if (TheCmdParamsCallback != null)
            {
                parmsCopy = Globs.CopyData(parms);
            }

            // Response atoms
            TpmSt responseTag;
            TpmRc resultCode;
            uint responseParamSize;
            byte[] outParmsNoHandles, outParmsWithHandles;
            TpmHandle[] outHandles;
            SessionOut[] outSessions;

            // In normal processing there is just one pass through this do-while loop
            // If command observation/modification callbacks are installed, then the
            // caller repeats the command as long as necessary.
            bool invokeCallbacks = OuterCommand == TpmCc.None && !CpHashMode && !DoNotDispatchCommand;
            do try
            {
                if (TheCmdParamsCallback != null && invokeCallbacks)
                {
                    parms = Globs.CopyData(parmsCopy);
                    TheCmdParamsCallback(commandInfo, ref parms, inHandles);
                }

                // If there are any encrypting sessions then next we encrypt the data in place
                parms = DoParmEncryption(parms, commandInfo, 0, Direction.Command);

                // Now do the HMAC (note that the handles are needed for name-replacement)
                SessionIn[] inSessions = CreateRequestSessions(parms, inHandles);

                // CpHashMode is enabled for a single command through tpm.GetCpHash().TpmCommand(...)
                if (OuterCommand == TpmCc.None && CpHashMode)
                {
                    CommandParmHash.HashData = GetCommandHash(CommandParmHash.HashAlg, parms, inHandles);
                    outParms = (TpmStructureBase)Activator.CreateInstance(expectedResponseType);
                    CpHashMode = false;
                    _ClearCommandContext();
                    return true;
                }

                // Create the command buffer
                byte[] command = CommandProcessor.CreateCommand(ordinal, inHandles, inSessions, parms);

                // And dispatch the command
                Log(ordinal, inParms, 0);

                if (DoNotDispatchCommand)
                {
                    CommandBytes = command;
                    outParms = (TpmStructureBase)Activator.CreateInstance(expectedResponseType);
                    DoNotDispatchCommand = false;
                    _ClearCommandContext();
                    return true;
                }

                if (TheCmdBufCallback != null && invokeCallbacks)
                {
                    TheCmdBufCallback(ref command);
                }

                // And actually dispatch the command into the underlying device
                DateTime commandSentTime, responseReceivedTime;
                int nvRateRecoveryCount = 0;

                // No more than 4 retries on NV_RATE error
                for (;;)
                {
                    responseReceivedTime = commandSentTime = DateTime.Now;

                    if (!TestCycleNv)
                    {
                        commandSentTime = DateTime.Now;
                        Device.DispatchCommand(ActiveModifiers, command, out response);
                        responseReceivedTime = DateTime.Now;
                    }
                    else
                    {
                        // In TestCycleNv we submit the command with NV not-available.  If the TPM indicates that
                        // NV is not available we re-submit.
                        try
                        {
                            // Once with NV off
                            Device.SignalNvOff();
                            Device.DispatchCommand(ActiveModifiers, command, out response);
                            Device.SignalNvOn();
                            // And if it did not work, try again with NV on
                            TpmRc respCode = CommandProcessor.GetResponseCode(response);
                            if ((uint)respCode == 0x923U || respCode == TpmRc.Lockout)
                            {
                                Device.DispatchCommand(ActiveModifiers, command, out response);
                            }

                        }
                        catch (Exception)
                        {
                            Device.SignalNvOn();
                            throw;
                        }
                    }

                    // Convert the byte[] response into its constituent parts.
                    CommandProcessor.SplitResponse(response,
                                                   commandInfo.HandleCountOut,
                                                   out responseTag,
                                                   out responseParamSize,
                                                   out resultCode,
                                                   out outHandles,
                                                   out outSessions,
                                                   out outParmsNoHandles,
                                                   out outParmsWithHandles);

                    if (resultCode != TpmRc.NvRate || ++nvRateRecoveryCount > 4)
                    {
                        break;
                    }
                    //Console.WriteLine(">>>> NV_RATE: Retrying... Attempt {0}", nvRateRecoveryCount);
                    Thread.Sleep((int)Tpm2.GetProperty(this, Pt.NvWriteRecovery) + 100);
                }

                // Invoke the trace callback if installed        
                if (TheTraceCallback != null)
                {
                    TheTraceCallback(command, response);
                }

                // Collect basic statistics on command execution
                if (TheCmdStatsCallback != null && invokeCallbacks)
                {
                    repeat = TheCmdStatsCallback(ordinal, GetBaseErrorCode(resultCode),
                                        (responseReceivedTime - commandSentTime).TotalSeconds);
                }

                if (repeat && resultCode == TpmRc.Success)
                {
                    // Update session state
                    ProcessResponseSessions(outSessions);
                    int offset = (int)commandInfo.HandleCountOut * 4;
                    outParmsWithHandles = DoParmEncryption(outParmsWithHandles, commandInfo, offset, Direction.Response);
                    var m = new Marshaller(outParmsWithHandles);
                    outParms = (TpmStructureBase)m.Get(expectedResponseType, "");
#if false
                    m = new Marshaller(command);
                    TpmSt tag = m.Get<TpmSt>();
                    uint cmdSize = m.Get<uint>();
                    TpmCc actualCmd = m.Get<TpmCc>();
                    var actualHandles = new TpmHandle[inHandles.Length];
                    for (int i = 0; i < inHandles.Length; ++i)
                    {
                        actualHandles[i] = m.Get<TpmHandle>();
                    }
                    for (int i = 0; i < inSessions.Length; ++i)
                    {
                        m.Get<SessionIn>();
                    }
                    var actualParms = m.GetArray<byte>(m.GetValidLength() - m.GetGetPos());
                    if (m.GetValidLength() != cmdSize)
                    {
                        throw new Exception("Command length in header does not match input byte-stream");
                    }
#endif
                    CommandHeader actualHeader;
                    TpmHandle[] actualHandles;
                    SessionIn[] actualSessions;
                    byte[] actualParmsBuf;
                    CommandProcessor.CrackCommand(command, out actualHeader, out actualHandles, out actualSessions, out actualParmsBuf);
                    m = new Marshaller();
                    foreach (TpmHandle h in actualHandles)
                    {
                        m.Put(h, "handle");
                    }
                    m.Put(actualParmsBuf, "parms");
                    var actualParms = (TpmStructureBase)Activator.CreateInstance(inParms.GetType());
                    actualParms.ToHost(m);
                    UpdateHandleData(actualHeader.CommandCode, actualParms, actualHandles, outParms);
                    //ValidateResponseSessions(outHandles, outSessions, ordinal, resultCode, outParmsNoHandles);

                    foreach (var h in outHandles)
                    {
                        CancelSafeFlushContext(h);
                    }
                } // if (repeat && resultCode == TpmRc.Success)
            }
            catch (Exception)
            {
                _ClearCommandPrelaunchContext();
                _ClearCommandContext();
                throw;
            }
            while (repeat);

            // Update the audit session if needed
            if (AuditThisCommand)
            {
                AuditThisCommand = false;
                if (CommandAuditHash == null)
                    throw new Exception("No audit hash set for this command stream");
                byte[] parmHash = GetCommandHash(CommandAuditHash.HashAlg, parms, inHandles);
                byte[] expectedResponseHash = GetExpectedResponseHash(CommandAuditHash.HashAlg,
                                                                      outParmsNoHandles,
                                                                      ordinal,
                                                                      resultCode);
                CommandAuditHash.Extend(Globs.Concatenate(parmHash, expectedResponseHash));
            }

            // FlushContest that may be executed as part of _ClearCommandPrelaunchContext()
            // must be executed before any command-related info is updated.
            _ClearCommandPrelaunchContext();

            // Process errors if there are any
            bool commandSucceeded = ProcessError(responseTag, responseParamSize, resultCode, inParms);
            try
            {
                if (commandSucceeded)
                {
                    ProcessResponseSessions(outSessions);
                    int offset = (int)commandInfo.HandleCountOut * 4;
                    outParmsWithHandles = DoParmEncryption(outParmsWithHandles, commandInfo, offset, Direction.Response);

                    var mt = new Marshaller(outParmsWithHandles);
                    outParms = (TpmStructureBase)mt.Get(expectedResponseType, "");
                    if (TheParamsTraceCallback != null)
                    {
                        TheParamsTraceCallback(ordinal, inParms, outParms);
                    }

                    UpdateHandleData(ordinal, inParms, inHandles, outParms);
                    ValidateResponseSessions(outHandles, outSessions, ordinal, resultCode, outParmsNoHandles);

                    foreach (var s in Sessions) if (s is AuthSession)
                    {
                        var sess = s as AuthSession;
                        if (sess.Attrs.HasFlag(SessionAttr.Audit) && !TpmHandle.IsNull(sess.BindObject))
                        {
                            sess.BindObject = TpmRh.Null;
                            break; // only one audit session is expected
                        }
                    }
                }
                else
                {
                    outParms = (TpmStructureBase)Activator.CreateInstance(expectedResponseType);
                }
            }
            finally
            {
                // Check in case an exception happened before outParms was initialized
                // ReSharper disable once CSharpWarnings::CS0183
                if (outParms is TpmStructureBase)
                {
                    Log(ordinal, outParms, 1);
                }
                // Clear all per-invocation state (e.g. sessions, errors expected) ready for next command
                _ClearCommandContext();
            }
            return commandSucceeded;
        }