public TpmException(TpmRc rawResponse, string errorDescription, TpmStructureBase cmdParms) : base(errorDescription) { ErrorString = TpmErrorHelpers.ErrorNumber(rawResponse).ToString(); RawResponse = rawResponse; CmdParms = cmdParms; }
public async Task <Tpm2CreateResponse> CreateAsync( TpmHandle parentHandle, SensitiveCreate inSensitive, TpmPublic inPublic, byte[] outsideInfo, PcrSelection[] creationPCR) { var inS = new Tpm2CreateRequest(parentHandle, inSensitive, inPublic, outsideInfo, creationPCR); TpmStructureBase outSBase = null; await Task.Run(() => DispatchMethod(TpmCc.Create, inS, typeof(Tpm2CreateResponse), out outSBase, 1, 0)); var outS = (Tpm2CreateResponse)outSBase; return(outS); }
public async Task <ISignatureUnion> SignAsync( TpmHandle keyHandle, byte[] digest, ISigSchemeUnion inScheme, TkHashcheck validation) { var inS = new Tpm2SignRequest(keyHandle, digest, inScheme, validation); TpmStructureBase outSBase = null; await Task.Run(() => DispatchMethod(TpmCc.Sign, inS, typeof(Tpm2SignResponse), out outSBase, 1, 0)); var outS = (Tpm2SignResponse)outSBase; return(outS.signature); }
public async Task <Tpm2CreatePrimaryResponse> CreatePrimaryAsync( TpmHandle primaryHandle, SensitiveCreate inSensitive, TpmPublic inPublic, byte[] outsideInfo, PcrSelection[] creationPCR) { var inS = new Tpm2CreatePrimaryRequest { primaryHandle = primaryHandle, inSensitive = inSensitive, inPublic = inPublic, outsideInfo = outsideInfo, creationPCR = creationPCR }; TpmStructureBase outSBase = null; await Task.Run(() => DispatchMethod(TpmCc.CreatePrimary, inS, typeof(Tpm2CreatePrimaryResponse), out outSBase, 1, 1)); var outS = (Tpm2CreatePrimaryResponse)outSBase; return(outS); }
/// <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); }
/// <summary> /// Log the command / response to the debug stream /// </summary> /// <param name="commandCode"></param> /// <param name="theStruct"></param> /// <param name="outOrIn"></param> private void Log(TpmCc commandCode, TpmStructureBase theStruct, int outOrIn) { if (!CommandLogging) return; Debug.WriteLine("COMMAND " + Enum.GetName(typeof (TpmCc), commandCode)); switch (outOrIn) { case 0: Debug.WriteLine("COMMAND STRUCTURE"); break; case 1: Debug.WriteLine("RESPONSE STRUCTURE"); break; } string ss = theStruct.ToString(); Debug.WriteLine(ss); }
} // DoParmEncryption() /// <summary> /// Updates information associated by the library with TPM entity handles upon /// successful completion of a command that either creates a new entity or /// changes the properties of an existing one. /// /// Some important data associated with TPM entities cannot be retrieved from /// TPM either because of their sensitivity or because of substantial overhead. /// The information of the former kind is an auth value (for permanent handles, /// transient and persistent objects, NV indices) and a bound handle (for /// sessions). Information tracked for the sake of performance optimization /// is objects and NV index name. /// </summary> /// <param name="ordinal"></param> /// <param name="inParms"></param> /// <param name="inHandles"></param> // ReSharper disable once UnusedParameter.Local private void UpdateHandleData(TpmCc ordinal, TpmStructureBase inParms, TpmHandle[] inHandles, TpmStructureBase outParms) { switch (ordinal) { case TpmCc.Create: { var req = (Tpm2CreateRequest)inParms; var resp = (Tpm2CreateResponse)outParms; TpmHash priv = TpmHash.FromData(PrivHashAlg, resp.outPrivate.buffer); AuthValues[priv] = Globs.CopyData(req.inSensitive.userAuth); break; } case TpmCc.CreatePrimary: { var req = (Tpm2CreatePrimaryRequest)inParms; var resp = (Tpm2CreatePrimaryResponse)outParms; resp.objectHandle.Auth = req.inSensitive.userAuth; ProcessName(resp.objectHandle, resp.name, resp.outPublic); break; } case TpmCc.Load: { var req = (Tpm2LoadRequest)inParms; var resp = (Tpm2LoadResponse)outParms; TpmHash priv = TpmHash.FromData(PrivHashAlg, req.inPrivate.buffer); if (AuthValues.ContainsKey(priv)) resp.objectHandle.Auth = AuthValues[priv]; ProcessName(resp.objectHandle, resp.name, req.inPublic); break; } case TpmCc.LoadExternal: { var req = (Tpm2LoadExternalRequest)inParms; var resp = (Tpm2LoadExternalResponse)outParms; byte[] name = req.inPublic.GetName(); ProcessName(resp.objectHandle, resp.name, req.inPublic); break; } case TpmCc.StartAuthSession: { var req = (Tpm2StartAuthSessionRequest)inParms; var resp = (Tpm2StartAuthSessionResponse)outParms; SessionParams[resp.sessionHandle] = new AuthSession(req.sessionType, req.tpmKey, req.bind, req.nonceCaller, resp.nonceTPM, req.symmetric, req.authHash); break; } case TpmCc.HmacStart: { var req = (Tpm2HmacStartRequest)inParms; var resp = (Tpm2HmacStartResponse)outParms; resp.sequenceHandle.Auth = req.auth; resp.sequenceHandle.Name = null; break; } case TpmCc.NvDefineSpace: { var req = (Tpm2NvDefineSpaceRequest)inParms; req.publicInfo.nvIndex.Auth = req.auth; req.publicInfo.nvIndex.Name = null; break; } case TpmCc.NvChangeAuth: { var req = (Tpm2NvChangeAuthRequest)inParms; req.nvIndex.Auth = req.newAuth; break; } case TpmCc.ObjectChangeAuth: { var req = (Tpm2ObjectChangeAuthRequest)inParms; var resp = (Tpm2ObjectChangeAuthResponse)outParms; TpmHash priv = TpmHash.FromData(PrivHashAlg, resp.outPrivate.buffer); AuthValues[priv] = Globs.CopyData(req.newAuth); break; } case TpmCc.HierarchyChangeAuth: { var req = (Tpm2HierarchyChangeAuthRequest)inParms; AuthValue auth = Globs.CopyData(req.newAuth); switch (req.authHandle.handle) { case (uint)TpmRh.Owner: OwnerAuth = auth; break; case (uint)TpmRh.Endorsement: EndorsementAuth = auth; break; case (uint)TpmRh.Platform: PlatformAuth = auth; break; case (uint)TpmRh.Lockout: LockoutAuth = auth; break; } req.authHandle.Auth = auth; break; } case TpmCc.PcrSetAuthValue: { var req = (Tpm2PcrSetAuthValueRequest)inParms; req.pcrHandle.Auth = req.auth; if (PcrHandles == null) { uint numPcrs = GetProperty(this, Pt.PcrCount); PcrHandles = new TpmHandle[numPcrs]; } int pcrId = (int)req.pcrHandle.GetOffset(); Debug.Assert(pcrId < PcrHandles.Length); PcrHandles[pcrId] = req.pcrHandle; break; } case TpmCc.EvictControl: { var req = (Tpm2EvictControlRequest)inParms; var resp = (Tpm2EvictControlResponse)outParms; if (req.objectHandle.GetType() != Ht.Persistent) { req.persistentHandle.Auth = req.objectHandle.Auth; req.persistentHandle.Name = req.objectHandle.Name; } break; } case TpmCc.Clear: { OwnerAuth = new AuthValue(); EndorsementAuth = new AuthValue(); LockoutAuth = new AuthValue(); break; } case TpmCc.NvWrite: { var req = (Tpm2NvWriteRequest)inParms; // Force name recalculation before next use req.nvIndex.Name = null; break; } case TpmCc.NvWriteLock: { var req = (Tpm2NvWriteLockRequest)inParms; // Force name recalculation before next use req.nvIndex.Name = null; break; } case TpmCc.NvReadLock: { var req = (Tpm2NvReadLockRequest)inParms; // Force name recalculation before next use req.nvIndex.Name = null; break; } case TpmCc.HashSequenceStart: { var req = (Tpm2HashSequenceStartRequest)inParms; var resp = (Tpm2HashSequenceStartResponse)outParms; resp.sequenceHandle.Auth = req.auth; break; } case TpmCc.Startup: { var req = (Tpm2StartupRequest)inParms; if (req.startupType == Su.Clear) { PlatformAuth = new AuthValue(); } break; } case TpmCc.ContextSave: { var req = (Tpm2ContextSaveRequest)inParms; var resp = (Tpm2ContextSaveResponse)outParms; resp.context.savedHandle.Auth = req.saveHandle.Auth; resp.context.savedHandle.Name = req.saveHandle.Name; break; } case TpmCc.ContextLoad: { var req = (Tpm2ContextLoadRequest)inParms; var resp = (Tpm2ContextLoadResponse)outParms; resp.loadedHandle.Auth = req.context.savedHandle.Auth; resp.loadedHandle.Name = req.context.savedHandle.Name; break; } case TpmCc.NvUndefineSpaceSpecial: { var req = (Tpm2NvUndefineSpaceSpecialRequest)inParms; req.nvIndex.Auth = null; break; } } } // UpdateHandleData()
/// <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); }
/// <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; }