/// <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; }
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); }
/// <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; }