Beispiel #1
0
        /// <summary>
        /// Handles TPM response value.
        /// Converts the error code into a human-readable form, invokes callbacks and
        /// encapsulates error info into a .Net exception.
        /// </summary>
        /// <param name="responseTag"></param>
        /// <param name="responseParamSize"></param>
        /// <param name="resultCode"></param>
        /// <param name="inParms"></param>
        /// <returns></returns>
        // ReSharper disable once UnusedParameter.Local
        private bool ProcessError(TpmSt responseTag, uint responseParamSize,
                                  TpmRc resultCode, TpmStructureBase inParms)
        {
            string errorString;

            AssertExpectedResponsesValid();

            // Process TPM success case (both expected success, and unexpected success)
            if (resultCode == TpmRc.Success)
            {
                LastError = TpmRc.Success;
                if (IsSuccessExpected())
                {
                    return true;
                }
                // Else we have unexpectedly succeeded

                // If TolerateErrors is set, then no error indication is provided apart
                // from setting LastReponseCode (the caller must query to find that
                // the error does not match).
                if (TolerateErrors)
                {
                    return false;
                }
                // If there is an installed error handler invoke it.
                if (TheErrorHandler != null)
                {
                    TheErrorHandler(resultCode, ExpectedResponses);
                    return false;
                }
                // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
                if (ExpectedResponses.Length == 1)
                {
                    errorString = string.Format("Error {0} was expected but command {1} succeeded",
                                                ExpectedResponses[0],
                                                CurrentCommand);
                }
                else
                {
                    errorString = string.Format("Errors {{{0}}} were expected but command {1} succeeded",
                                                string.Join(", ", ExpectedResponses),
                                                CurrentCommand);
                }

                _ClearCommandContext();
                throw new TssException(errorString);
            }
            // Else we have an error
            if (responseTag != TpmSt.NoSessions)
            {
                throw new Exception("Ill-formed responseTag (not NoSessions)");
            }
            if (responseParamSize != 10)
            {
                throw new Exception("Ill-formed reponseParamSize (not 10)");
            }

            // There are two encodings for errors - format 0 and format 1.
            var resultCodeValue = (uint)resultCode;
            bool formatOneErrorType = TpmErrorHelpers.IsFmt1(resultCode);

            // Extract the actual error number
            LastError = TpmErrorHelpers.ErrorNumber(resultCode);

            string errorEntity = "Unknown";
            uint errorEntityIndex = 0;
            string errorParmName = "Unknown";
            if (formatOneErrorType)
            {
                errorEntityIndex = (resultCodeValue & 0xF00U) >> 8;
                if ((resultCodeValue & 0x40) != 0)
                {
                    errorEntity = "Parameter";
                    errorParmName = GetParmName(inParms.GetType(), errorEntityIndex);
                }
                else
                {
                    if (errorEntityIndex >= 8)
                    {
                        errorEntityIndex -= 8;
                        errorEntity = "Session";
                    }
                    else
                    {
                        errorEntity = "Handle";
                    }
                }
            }
            string errorDetails = FormatString("\r\n" +
                                               "Details: \n" +
                                               "[Code=TpmRc.{0}],"+
                                               "[RawCode=0x{1:X},{1}]\n" +
                                               "[ErrorEntity={2}], [ParmNum={3}]\n" +
                                               "[ParmName={4}]",
                                               new Object[] {
                                                   LastError.ToString(), 
                                                   resultCodeValue,
                                                   errorEntity,
                                                   errorEntityIndex,
                                                   errorParmName
                                               });
            // We have found out all we can about the error.  Now process it according to the tpm context

            // AllowErrors() specifies that errors should be handled silently
            if ((OuterCommand == TpmCc.None && IsErrorAllowed(LastError)) ||
                (OuterCommand != TpmCc.None && LastError == TpmRc.Canceled))
            {
                return false;
            }

            if (OuterCommand != TpmCc.None || ExpectedResponses == null)
            {
                errorString = string.Format("Error {{{0}}} was returned for command {1}.",
                                            LastError, CurrentCommand);
            }
            else
            {
                errorString = string.Format("Error {{{0}}} was returned instead of {{{1}}} for command {2}.",
                                            LastError,
                                            string.Join(", ", ExpectedResponses),
                                            CurrentCommand);
            }

            if (AreErrorsExpected())
            {
                // We have a mismatched error. If a warning handler is installed, call it.
                if (TheWarningHandler != null)
                {
                    TheWarningHandler(errorString);
                    _ClearCommandContext();
                    return false;
                }
            }

            // Otherwise propagate the unexpected error as an exception
            _ClearCommandContext();
            errorString += errorDetails;
            throw new TpmException(resultCode, errorString);
        }
Beispiel #2
0
        /// <summary>
        /// Splits a TpmStructureBase command or response, and splits it into 
        /// handles and the parms data
        /// </summary>
        /// <param name="s"></param>
        /// <param name="numHandles"></param>
        /// <param name="handles"></param>
        /// <param name="parms"></param>
        public static void Fragment(TpmStructureBase s, uint numHandles, out TpmHandle[] handles, out byte[] parms)
        {
            handles = new TpmHandle[numHandles];
            // Get the handles (note we need to return the actual object because it contains the name.
            // The handles are always first, and will be simple fields or get/set props.
            MemberInfo[] fields;
            try
            {
                fields = s.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance);
            }
            catch (Exception)
            {
                throw;
            }

            int fieldPos = 0;
            for (int j = 0; j < numHandles; j++)
            {
                MemberInfo f;
                do
                {
                    // Ignore setters
                    f = fields[fieldPos++];
                } while (f.Name.StartsWith("set_"));
                // Either a simple field
                var ff = f as FieldInfo;
                if (ff != null)
                {
                    handles[j] = (TpmHandle)ff.GetValue(s);
                }
                // A get or set accessor
                var mm = f as MethodInfo;
                if (mm != null)
                {
                    object hRep = mm.Invoke(s, null);
                    handles[j] = hRep is TpmHandle ? (TpmHandle)hRep : ((TpmHandleX)hRep).Handle;
                }
            }
            // And the rest is the parms
            byte[] b = Marshaller.GetTpmRepresentation(s);
            parms = new byte[b.Length - numHandles * 4];
            Array.Copy(b, (int)numHandles * 4, parms, 0, b.Length - (int)numHandles * 4);
        }
Beispiel #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;
        }