void ActivateAikSample(Tpm2 tpm, TestContext testCtx) { // Use an RSA primary key in the Endorsement hierarchy (EK) to 'activate' // another primary in the Endorsement hierarchy (AIK). // Note: this procedure can be used to activate a key in the Storage hierarchy, too. // "Activation" means secure passing sensitive data generated by a CA (e.g. // a symmetric key protecting a freshly generated certificate), so that only // a device with the TPM owning the target AIK and EK can access these data. // Create a primary key that we will certify. ////////////////////////////////////////////////////////////////////////////// // Device side code ////////////////////////////////////////////////////////////////////////////// // Name algorithm of the new AIK TpmAlgId nameAlg = TpmAlgId.Sha256; // The key to be certified needs a policy. It can be ANY policy, but here // it is configured to allow AIK usage only with the following commands var policyAIK = new PolicyTree(nameAlg); var policyOR = new TpmPolicyOr(); policyAIK.SetPolicyRoot(policyOR); policyOR.AddPolicyBranch(new TpmPolicyCommand(TpmCc.ActivateCredential, "Activate")); policyOR.AddPolicyBranch(new TpmPolicyCommand(TpmCc.Certify, "Certify")); policyOR.AddPolicyBranch(new TpmPolicyCommand(TpmCc.CertifyCreation, "CertifyCreation")); policyOR.AddPolicyBranch(new TpmPolicyCommand(TpmCc.Quote, "Quote")); var inAIKPub = new TpmPublic(nameAlg, ObjectAttr.Restricted | ObjectAttr.Sign | ObjectAttr.FixedParent | ObjectAttr.FixedTPM | ObjectAttr.AdminWithPolicy | ObjectAttr.SensitiveDataOrigin, policyAIK.GetPolicyDigest(), new RsaParms(new SymDefObject(), new SchemeRsassa(TpmAlgId.Sha256), 2048, 0), new Tpm2bPublicKeyRsa()); TpmPublic AIKpub; var inSens = new SensitiveCreate(Substrate.RandomAuth(nameAlg), null); CreationData creationData; byte[] creationHash; TkCreation creationTk; TpmHandle hAIK = tpm.CreatePrimary(TpmRh.Endorsement, inSens, inAIKPub, null, null, out AIKpub, out creationData, out creationHash, out creationTk); // An alternative using test substrate helper //TpmHandle hAIK = Substrate.CreatePrimary(tpm, inAIKPub, TpmRh.Endorsement); // Normally a device would have a pre-provisioned persistent EK with the above handle TpmHandle hEK = new TpmHandle(0x81010001); // Get its public part TpmPublic EKpub = null; // In a test environment the real EK may be absent. In this case use // a primary key temporarily created in the Endorsement hierarchy. byte[] name, qname; tpm._AllowErrors() .ReadPublic(hEK, out name, out qname); if (!tpm._LastCommandSucceeded()) { var inEKpub = new TpmPublic(TpmAlgId.Sha256, ObjectAttr.Restricted | ObjectAttr.Decrypt | ObjectAttr.FixedParent | ObjectAttr.FixedTPM | ObjectAttr.AdminWithPolicy | ObjectAttr.SensitiveDataOrigin, new byte[] { 0x83, 0x71, 0x97, 0x67, 0x44, 0x84, 0xb3, 0xf8, 0x1a, 0x90, 0xcc, 0x8d, 0x46, 0xa5, 0xd7, 0x24, 0xfd, 0x52, 0xd7, 0x6e, 0x06, 0x52, 0x0b, 0x64, 0xf2, 0xa1, 0xda, 0x1b, 0x33, 0x14, 0x69, 0xaa }, new RsaParms(new SymDefObject(TpmAlgId.Aes, 128, TpmAlgId.Cfb), null, 2048, 0), new Tpm2bPublicKeyRsa()); inSens = new SensitiveCreate(null, null); hEK = tpm.CreatePrimary(TpmRh.Endorsement, inSens, inEKpub, null, null, out EKpub, out creationData, out creationHash, out creationTk); } // Marshal public parts of AIK and EK byte[] AIKpubBytes = AIKpub.GetTpmRepresentation(); byte[] EKpubBytes = EKpub.GetTpmRepresentation(); // Here the device uses network to pass AIKpubBytes and EKpubBytes (as well as // the EK certificate) to CA service. ////////////////////////////////////////////////////////////////////////////// // Service (CA) side code ////////////////////////////////////////////////////////////////////////////// // Unmarshal AIKpub and EKpub (and EK certificate) var m = new Marshaller(AIKpubBytes); AIKpub = m.Get <TpmPublic>(); m = new Marshaller(EKpubBytes); EKpub = m.Get <TpmPublic>(); // Symmetric key to be used by CA to encrypt the new certificate it creates for AIK byte[] secretKey = Substrate.RandomBytes(32); // Here CA does the following (the sample does not show this code): // - Validates EK certificate // - Generates a new certificate for AIK (AIKcert) // - Encrypts AIKcert with secretKey // Create an activation blob // Internal wrapper key encrypted with EKpub to be used by TPM2_ActivateCredential() command. byte[] encSecret; // An encrypted and HMACed object tied to the destination TPM. It contains // 'secret' to be extracted by the successful TPM2_ActivateCredential() command // (that can only succeeed on the TPM that originated both EK and AIK). IdObject certInfo; // Run TSS.Net equivalent of TPM2_MakeCredential() command (convenient for the server side, // as it will likely be faster than the TPM transaction and allows concurrent execution). certInfo = EKpub.CreateActivationCredentials(secretKey, AIKpub.GetName(), out encSecret); // Marshal certInfo // IdObject data type requires customized marshaling m = new Marshaller(); m.Put(certInfo.integrityHMAC.Length, "integrityHMAC.Length"); m.Put(certInfo.integrityHMAC, "integrityHMAC"); m.Put(certInfo.encIdentity.Length, "encIdentity"); m.Put(certInfo.encIdentity, "encIdentity.Length"); byte[] certInfoBytes = m.GetBytes(); // Here the CA passes certInfoBytes and encSecret (as well as the encrypted // AIK certificate) back to the device via network. ////////////////////////////////////////////////////////////////////////////// // Device side code again ////////////////////////////////////////////////////////////////////////////// // Unmarshal certInfo and encSecret (and encrypted AIK certificate) m = new Marshaller(certInfoBytes); int len = m.Get <int>(); certInfo.integrityHMAC = m.GetArray <byte>(len); len = m.Get <int>(); certInfo.encIdentity = m.GetArray <byte>(len); // encSecret is a byte array, so, normally, no special unmarshalling is required // Create policy session to authorize AIK usage AuthSession sessAIK = tpm.StartAuthSessionEx(TpmSe.Policy, nameAlg); sessAIK.RunPolicy(tpm, policyAIK, "Activate"); // Create policy description and corresponding policy session to authorize EK usage var policyEK = new PolicyTree(EKpub.nameAlg); policyEK.SetPolicyRoot(new TpmPolicySecret(TpmRh.Endorsement, false, 0, null, null)); AuthSession sessEK = tpm.StartAuthSessionEx(TpmSe.Policy, EKpub.nameAlg); sessEK.RunPolicy(tpm, policyEK); byte[] recoveredSecretKey = tpm[sessAIK, sessEK].ActivateCredential(hAIK, hEK, certInfo, encSecret); testCtx.AssertEqual("Secret.1", recoveredSecretKey, secretKey); // Here the device can use recoveredSecretKey to decrypt the AIK certificate ////////////////////////////////////////////////////////////////////////////// // End of activation sequence ////////////////////////////////////////////////////////////////////////////// // // Now prepare activation using the TPM built-in command // byte[] encSecret2 = null; IdObject certInfo2 = tpm.MakeCredential(hEK, secretKey, AIKpub.GetName(), out encSecret2); // Reinitialize policy sessions tpm.PolicyRestart(sessAIK); sessAIK.RunPolicy(tpm, policyAIK, "Activate"); tpm.PolicyRestart(sessEK); sessEK.RunPolicy(tpm, policyEK); recoveredSecretKey = tpm[sessAIK, sessEK].ActivateCredential(hAIK, hEK, certInfo2, encSecret2); testCtx.AssertEqual("Secret.2", recoveredSecretKey, secretKey); // Cleanup tpm.FlushContext(sessAIK); tpm.FlushContext(sessEK); tpm.FlushContext(hAIK); if (hEK.handle != 0x81010001) { tpm.FlushContext(hEK); } } // ActivateAikSample
private TpmRc GetResultCode(byte[] responseBuf) { var mOut = new Marshaller(responseBuf); // ReSharper disable once UnusedVariable var responseTag = mOut.Get<TpmSt>(); // ReSharper disable once UnusedVariable var responseParamSize = mOut.Get<uint>(); var resultCode = mOut.Get<TpmRc>(); return resultCode; }
/// <summary> /// Activates an identity key within the TPM device. /// </summary> /// <param name="encryptedKey">The encrypted identity key.</param> public override void ActivateIdentityKey(byte[] encryptedKey) { if (Logging.IsEnabled) { Logging.Enter(this, $"{encryptedKey}", nameof(ActivateIdentityKey)); } Destroy(); // Take the pieces out of the container var m = new Marshaller(encryptedKey, DataRepresentation.Tpm); Tpm2bIdObject cred2b = m.Get <Tpm2bIdObject>(); byte[] encryptedSecret = new byte[m.Get <ushort>()]; encryptedSecret = m.GetArray <byte>(encryptedSecret.Length, "encryptedSecret"); TpmPrivate dupBlob = m.Get <TpmPrivate>(); byte[] encWrapKey = new byte[m.Get <ushort>()]; encWrapKey = m.GetArray <byte>(encWrapKey.Length, "encWrapKey"); UInt16 pubSize = m.Get <UInt16>(); _idKeyPub = m.Get <TpmPublic>(); byte[] cipherText = new byte[m.Get <ushort>()]; cipherText = m.GetArray <byte>(cipherText.Length, "uriInfo"); // Setup the authorization session for the EK var policyNode = new TpmPolicySecret(TpmHandle.RhEndorsement, false, 0, Array.Empty <byte>(), Array.Empty <byte>()); var policy = new PolicyTree(_ekPub.nameAlg); policy.SetPolicyRoot(policyNode); AuthSession ekSession = _tpm2.StartAuthSessionEx(TpmSe.Policy, _ekPub.nameAlg); ekSession.RunPolicy(_tpm2, policy); // Perform the activation ekSession.Attrs &= ~SessionAttr.ContinueSession; _activationSecret = _tpm2[Array.Empty <byte>(), ekSession].ActivateCredential( new TpmHandle(TPM_20_SRK_HANDLE), new TpmHandle(TPM_20_EK_HANDLE), cred2b.credential, encryptedSecret); TpmPrivate importedKeyBlob = _tpm2.Import( new TpmHandle(TPM_20_SRK_HANDLE), _activationSecret, _idKeyPub, dupBlob, encWrapKey, new SymDefObject(TpmAlgId.Aes, 128, TpmAlgId.Cfb)); _idKeyHandle = _tpm2.Load(new TpmHandle(TPM_20_SRK_HANDLE), importedKeyBlob, _idKeyPub); // Persist the key in NV TpmHandle hmacKeyHandle = new TpmHandle(AIOTH_PERSISTED_KEY_HANDLE); _tpm2.EvictControl(new TpmHandle(TpmRh.Owner), _idKeyHandle, hmacKeyHandle); // Unload the transient copy from the TPM _tpm2.FlushContext(_idKeyHandle); _idKeyHandle = hmacKeyHandle; if (Logging.IsEnabled) { Logging.Exit(this, $"{encryptedKey}", nameof(ActivateIdentityKey)); } }
internal virtual void ToHost(Marshaller m) { var members = GetFieldsToMarshal(true); dbg.Indent(); for (int i = 0; i < members.Length; ++i) { TpmStructMemberInfo memInfo = members[i]; Type memType = Globs.GetMemberType(memInfo); var wt = members[i].WireType; switch(wt) { case MarshalType.Union: { dbg.Trace("Union " + memType.Name + " with selector " + memInfo.Tag.Value); memInfo.Value = m.Get(UnionElementFromSelector(memType, memInfo.Tag.Value), memType.Name); break; } case MarshalType.FixedLengthArray: { object arr = Globs.GetMember(memInfo, this); memInfo.Value = m.GetArray(memType.GetElementType(), (arr as Array).Length, memInfo.Name); break; } case MarshalType.VariableLengthArray: { int size = m.GetSizeTag(memInfo.SizeLength, memInfo.SizeName); memInfo.Value = m.GetArray(memType.GetElementType(), size, memInfo.Name); Debug.Assert(size == ((Array)memInfo.Value).Length); dbg.Trace("Received Array " + memInfo.Name + " of size " + size); break; } case MarshalType.SizedStruct: { int size = m.GetSizeTag(memInfo.SizeLength, memInfo.SizeName); if (size != 0) { memInfo.Value = m.Get(memType, memInfo.Name); Debug.Assert(size == Marshaller.GetTpmRepresentation(memInfo.Value).Length); } dbg.Trace("Received Struct " + memInfo.Name + " of size " + size); break; } default: // Only attempt unmarshaling a field, if it is not sized or // if its size is non-zero. if (memInfo.Tag == null || memInfo.Tag.GetValueAsUInt() != 0) { memInfo.Value = m.Get(memType, memInfo.Name); } break; } dbg.Trace((i + 1) + ": " + memInfo.Name + " = " + memInfo.Value); // Some property values are dynamically obtained from their linked fields. // Correspondingly, they do not have a setter, so we bypass them here. Debug.Assert(wt != MarshalType.LengthOfStruct && wt != MarshalType.ArrayCount); if (wt != MarshalType.UnionSelector) { Globs.SetMember(memInfo, this, memInfo.Value); } } dbg.Unindent(); }
/// <summary> /// Activates a symmetric identity within the Hardware Security Module. /// </summary> /// <param name="activation">The authentication challenge key supplied by the service.</param> public override void ActivateSymmetricIdentity(byte[] activation) { Destroy(); // Take the pieces out of the container var m = new Marshaller(activation, DataRepresentation.Tpm); byte[] credentialBlob = new byte[m.Get <ushort>()]; credentialBlob = m.GetArray <byte>(credentialBlob.Length, "credentialBlob"); byte[] encryptedSecret = new byte[m.Get <ushort>()]; encryptedSecret = m.GetArray <byte>(encryptedSecret.Length, "encryptedSecret"); TpmPrivate dupBlob = m.Get <TpmPrivate>(); byte[] encWrapKey = new byte[m.Get <ushort>()]; encWrapKey = m.GetArray <byte>(encryptedSecret.Length, "encWrapKey"); UInt16 pubSize = m.Get <UInt16>(); _idKeyPub = m.Get <TpmPublic>(); byte[] cipherText = new byte[m.Get <ushort>()]; cipherText = m.GetArray <byte>(cipherText.Length, "uriInfo"); // Setup the authorization session for the EK var policyNode = new TpmPolicySecret( TpmHandle.RhEndorsement, _ekAuth ?? Array.Empty <byte>(), new AuthValue(), false, 0, Array.Empty <byte>(), Array.Empty <byte>()); var policy = new PolicyTree(_ekPub.nameAlg); policy.SetPolicyRoot(policyNode); AuthSession ekSession = _tpm2.StartAuthSessionEx(TpmSe.Policy, _ekPub.nameAlg); ekSession.RunPolicy(_tpm2, policy); // Perform the activation ekSession.Attrs &= ~SessionAttr.ContinueSession; _activationSecret = _tpm2[Array.Empty <byte>(), ekSession].ActivateCredential( new TpmHandle(TPM_20_SRK_HANDLE), new TpmHandle(TPM_20_EK_HANDLE), credentialBlob, encryptedSecret); TpmPrivate importedKeyBlob = _tpm2.Import( new TpmHandle(TPM_20_SRK_HANDLE), _activationSecret, _idKeyPub, dupBlob, encWrapKey, new SymDefObject(TpmAlgId.Aes, 128, TpmAlgId.Cfb)); _idKeyHandle = _tpm2.Load(new TpmHandle(TPM_20_SRK_HANDLE), importedKeyBlob, _idKeyPub); // Persist the key in NV TpmHandle hmacKeyHandle = new TpmHandle(AIOTH_PERSISTED_KEY_HANDLE); _tpm2.EvictControl(new TpmHandle(TpmRh.Owner), _idKeyHandle, hmacKeyHandle); // Unload the transient copy from the TPM _tpm2.FlushContext(_idKeyHandle); _idKeyHandle = hmacKeyHandle; // Unwrap the URI byte[] clearText = SymmCipher.Decrypt( new SymDefObject(TpmAlgId.Aes, 128, TpmAlgId.Cfb), _activationSecret, new byte[16], cipherText); UnicodeEncoding unicode = new UnicodeEncoding(); string uriData = unicode.GetString(clearText); int idx = uriData.IndexOf('/'); if (idx > 0) { string hostName = uriData.Substring(0, idx); string deviceId = uriData.Substring(idx + 1); // Persist the URI ProvisionUri(hostName, deviceId); } }
public static TpmRc GetResponseCode(byte[] response) { if (response.Length > 10) return TpmRc.Success; var m = new Marshaller(response); // ReSharper disable once UnusedVariable var tag = m.Get<TpmSt>(); // ReSharper disable once UnusedVariable var paramSize = m.Get<uint>(); var responseCode = m.Get<TpmRc>(); TpmRc maskedResponse = Tpm2.GetBaseErrorCode(responseCode); return maskedResponse; }
public static string ParseResponse(string commandCode, byte[] buf) { TpmHandle[] outHandles; SessionOut[] outSessions; byte[] responseParmsNoHandles; byte[] responseParmsWithHandles; string response = ""; if (1 != CommandInformation.Info.Count(item => item.CommandCode.ToString() == commandCode)) { response = "Command code not recognized. Defined command codes are:\n"; // ReSharper disable once LoopCanBeConvertedToQuery foreach (CommandInfo info in CommandInformation.Info) { response += info.CommandCode.ToString() + " "; } return response; } CommandInfo command = CommandInformation.Info.First(item => item.CommandCode.ToString() == commandCode); TpmSt tag; uint paramSize; TpmRc responseCode; SplitResponse(buf, command.HandleCountOut, out tag, out paramSize, out responseCode, out outHandles, out outSessions, out responseParmsNoHandles, out responseParmsWithHandles); if (responseCode != TpmRc.Success) { TpmRc resultCode; response += "Error:\n"; response += Tpm2.GetErrorString(command.InStructType, (uint)responseCode, out resultCode); return response; } // At this point in the processing stack we cannot deal with encrypted responses bool responseIsEncrypted = false; foreach (SessionOut s in outSessions) { if (s.attributes.HasFlag(SessionAttr.Encrypt) && (command.TheParmCryptInfo.HasFlag(ParmCryptInfo.DecOut2) || command.TheParmCryptInfo.HasFlag(ParmCryptInfo.DecOut2)) ) responseIsEncrypted = true; } response += "Response Header:\n"; response += " Tag=" + tag.ToString() + "\n"; response += " Response code=" + responseCode.ToString() + "\n"; response += "Response Parameters:\n"; if (!responseIsEncrypted) { var m2 = new Marshaller(responseParmsWithHandles); Object inParms = m2.Get(command.OutStructType, ""); response += inParms + "\n"; } else { var m2 = new Marshaller(responseParmsWithHandles); Object encOutParms; switch (command.TheParmCryptInfo) { // TODO: this is not the right type if we ever do size-checks case ParmCryptInfo.DecOut2: encOutParms = m2.Get(typeof (Tpm2bMaxBuffer), ""); break; default: throw new NotImplementedException("NOT IMPLEMENTED"); } response += "Encrypted: " + encOutParms + "\n"; } response += "Sessions [" + outSessions.Length + "]\n"; for (int j = 0; j < outSessions.Length; j++) { // ReSharper disable once FormatStringProblem response += String.Format("{0}: 0x{1:x}\n", j, outSessions[j]); } return response; }
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); }
public static string ParseCommand(byte[] buf) { CommandHeader commandHeader; TpmHandle[] inHandles; SessionIn[] inSessions; byte[] commandParmsNoHandles; string response = ""; bool ok = CrackCommand(buf, out commandHeader, out inHandles, out inSessions, out commandParmsNoHandles); if (!ok) { response = "The TPM command is not properly formatted. Doing the best I can...\n"; } CommandInfo command = Tpm2.CommandInfoFromCommandCode(commandHeader.CommandCode); if (command == null) { response += String.Format("The command-code {0} is not defined. Aborting\n", commandHeader.CommandCode); return response; } response += "Header:\n"; response += commandHeader + "\n"; var m2 = new Marshaller(); foreach (TpmHandle h in inHandles) { m2.Put(h, ""); } byte[] commandParmsWithHandles = Globs.Concatenate(new[] {m2.GetBytes(), commandParmsNoHandles}); var m = new Marshaller(commandParmsWithHandles); object inParms = m.Get(command.InStructType, ""); response += "Command Parameters:\n"; response += inParms + "\n"; response += "Sessions [" + inSessions.Length + "]\n"; for (int j = 0; j < inSessions.Length; j++) { // ReSharper disable once FormatStringProblem response += String.Format("{0}: 0x{1:x}\n", j, inSessions[j]); } return response; }
/// <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; }
/// <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; }
// This is installed as the raw command callback handler on the underlying TPM. // It is used to generate low-level test statistics (number of commands executed, // etc.), dumps of the conversation with the TPM and to keep a record of all // command sequences seen that contain types that we haven't seen before. // In the case of a multi-context TPM this will be called on different threads, // but locking should be handled safely by MainTestLogger. void ICommandCallbacks.PostCallback(byte[] inBuf, byte[] outBuf) { TimeSpan cmdExecutionTime = DateTime.Now - CurCmdStartTime; if (inBuf.Length < 10) { return; } Marshaller m = new Marshaller(inBuf); TpmSt sessionTag = m.Get <TpmSt>(); uint parmSize = m.Get <UInt32>(); TpmCc commandCode = m.Get <TpmCc>(); if (commandCode == TpmCc.Clear) { ClearWasExecuted = true; } Marshaller mOut = new Marshaller(outBuf); TpmSt responseTag = mOut.Get <TpmSt>(); uint responseParamSize = mOut.Get <uint>(); TpmRc responseCode = mOut.Get <TpmRc>(); if (ValidateTestAttributes) { // ValidateTestAttributes should not be set for a stress run LogTestAttributes(sessionTag, commandCode); try { if (responseCode == TpmRc.Success) { ValidateHandleUsage(commandCode, inBuf); } } catch (Exception) { // Invalid command buffer can mess this up } } if (sessionTag.Equals(TpmSt.Null)) { return; } // There are two encoding for errors - formats 0 and 1. Decode the error type uint resultCodeValue = (uint)responseCode; bool formatOneErrorType = ((resultCodeValue & 0x80) != 0); uint resultCodeMask = formatOneErrorType ? 0xBFU : 0x97FU; TpmRc maskedError = (TpmRc)((uint)responseCode & resultCodeMask); lock (this) { // log the command info to the test logger so that it can collect stats LogCommandExecution(commandCode, maskedError, cmdExecutionTime); } #if false // Keep a copy of successfully executed commands that contain types we have // not seen so far. This is for tests that need good-command candidate strings, // like TestCommandDispatcherCoverage. // Code 0x80280400 is returned by TBS when the command is blocked by Windows. if (maskedError == TpmRc.Success && !Tpm2.IsTbsError(resultCodeValue)) { // look at all types in command string. If we have a new type we keep it CrackedCommand cc = CommandProcessor.CrackCommand(inBuf); CommandInfo info = CommandInformation.Info.First(x => x.CommandCode == cc.Header.CommandCode); byte[] inStructBytes = Globs.Concatenate( Globs.GetZeroBytes((int)info.HandleCountIn * 4), cc.CommandParms); Marshaller mx = new Marshaller(inStructBytes); TpmStructureBase bb = (TpmStructureBase)mx.Get(info.InStructType, ""); // If a new type is contained, save this command for testing in // TestDispatcherCoverage. if (HasNewTypes(bb)) { ExecutedCommandInfo.Add(inBuf); } } else #else if (maskedError != TpmRc.Success) #endif { // If a command failed, we can get here only if the corresponding // expected error assertion was specified. ++NumAsserts; } ReportProgress(); // output TPM IO to a text file for later processing if (Logger.LogTpmIo) { while (TpmIoWriter == null) { try { string ioLogPath; if (Logger.LogPath != null) { ioLogPath = System.IO.Path.Combine(Logger.LogPath, "tpm_io.txt"); } else { string fileName; lock (this) { fileName = "tpm_io-" + DateTime.Now.ToString("yyyy-MMM-dd-HH"); if (PrevLogName == fileName) { fileName += "(" + ++PrevLogInstance + ")"; } else { PrevLogName = fileName; PrevLogInstance = 1; } } fileName += ".txt"; #if TSS_MIN_API ioLogPath = fileName; #else string docsPath = Environment.GetFolderPath( Environment.SpecialFolder.MyDocuments); ioLogPath = System.IO.Path.Combine(docsPath, fileName); #endif } TpmIoWriter = new StreamWriter(new FileStream(ioLogPath, FileMode.Create)); Logger.WriteToLog("Dumping TPM I/O to " + ioLogPath); } catch (Exception e) { string message = "Failed to open the tpm_io.txt file for writing.\n" + "Error: " + e.Message; Logger.WriteErrorToLog(message); } } // get the test source code line that initiated the command string caller = "unknown"; #if !TSS_NO_STACK StackTrace trace = new StackTrace(true); StackFrame[] frames = trace.GetFrames(); int frameCount = frames.Length; StackFrame f = null; // start at 1 to not count the currently executing function for (int j = 1; j < frameCount; j++) { f = frames[j]; if (f.GetMethod().DeclaringType.Assembly == Logger.TestAssembly) { caller = f.GetFileName() + ":" + f.GetFileLineNumber(); break; } } #endif string commandCodeString = Enum.GetName(typeof(TpmCc), commandCode); string inString = "{MALFORMED COMMAND BUFFER}"; string outString = "{MALFORMED RESPONSE BUFFER}"; try { inString = CommandProcessor.ParseCommand(inBuf); } catch (Exception) { } try { outString = CommandProcessor.ParseResponse(commandCodeString, outBuf); } catch (Exception) { } lock (this) { TpmIoWriter.WriteLine(commandCode); TpmIoWriter.WriteLine(caller); TpmIoWriter.WriteLine(">>>> Raw input"); TpmIoWriter.WriteLine(Globs.HexFromByteArray(inBuf)); TpmIoWriter.WriteLine(">>>> Raw output"); TpmIoWriter.WriteLine(Globs.HexFromByteArray(outBuf)); TpmIoWriter.WriteLine(">>>> Parsed input"); TpmIoWriter.WriteLine(inString); TpmIoWriter.WriteLine(">>>> Parsed output"); TpmIoWriter.WriteLine(outString); TpmIoWriter.WriteLine("-----------------------------------------"); TpmIoWriter.Flush(); } } if (ChainedCallbacks != null) { ChainedCallbacks.PostCallback(inBuf, outBuf); } } // ICommandCallbacks.PostCallback
internal override void ToHost(Marshaller m) { var id = (TpmAlgId)m.Get(typeof (TpmAlgId), "HashAlg"); if (id == TpmAlgId.Null) { _HashAlg = id; HashData = new byte[0]; return; } _HashAlg = id; int hashLength = CryptoLib.DigestSize(id); HashData = m.GetArray<byte>(hashLength, "HashData"); }
internal override void ToHost(Marshaller m) { Algorithm = (TpmAlgId)m.Get(typeof (TpmAlgId), "algorithm"); if (Algorithm == TpmAlgId.None || Algorithm == TpmAlgId.Null) { return; } KeyBits = (ushort)m.Get(typeof (ushort), "keyBits"); Mode = (TpmAlgId)m.Get(typeof (TpmAlgId), "mode"); }
internal override void ToHost(Marshaller m) { Algorithm = m.Get<TpmAlgId>(); switch (Algorithm) { case TpmAlgId.None: case TpmAlgId.Null: return; case TpmAlgId.Xor: KeyBits = 0; Mode = m.Get<TpmAlgId>(); break; case TpmAlgId.Aes: KeyBits = m.Get<ushort>(); Mode = m.Get<TpmAlgId>(); break; default: throw new NotImplementedException(); } }
internal override void ToHost(Marshaller m) { Algorithm = m.Get<TpmAlgId>(); switch (Algorithm) { case TpmAlgId.None: case TpmAlgId.Null: return; case TpmAlgId.Xor: KeyBits = 0; Mode = m.Get<TpmAlgId>(); break; case TpmAlgId.Aes: KeyBits = m.Get<ushort>(); Mode = m.Get<TpmAlgId>(); break; default: Globs.Throw<NotImplementedException>("SymDef.ToHost: Unknown algorithm"); break; } }