/// <summary> // Calculate and return the name of the entity. The name is an alg-prepended // digest in a byte buffer /// </summary> /// <returns></returns> public byte[] GetName() { byte[] rawData = GetTpmRepresentation(); TpmHash pubDigest = TpmHash.FromData(nameAlg, rawData); return(Marshaller.GetTpmRepresentation(pubDigest)); }
/// <summary> /// Composes and returns a valid TPM response buffer containg the given /// error response code. /// </summary> /// <param name="errorCode"></param> /// <returns></returns> public static byte[] BuildErrorResponseBuffer(TpmRc errorCode) { return(Marshaller.GetTpmRepresentation(new Object[] { TpmSt.NoSessions, (uint)10, errorCode })); }
/// <summary> /// Make a new TpmHash from the hash of the TPM representation of data /// </summary> /// <param name="hashAlg"></param> /// <param name="data"></param> /// <returns></returns> public static TpmHash FromObject(TpmAlgId hashAlg, Object data) { var newHash = new TpmHash(hashAlg); byte[] temp = Marshaller.GetTpmRepresentation(data); newHash.HashData = CryptoLib.HashData(hashAlg, temp); return(newHash); }
} // VerifySignature() /// <summary> /// Generates the key exchange key and the public part of the ephemeral key /// using specified encoding parameters in the KDF (ECC only). /// </summary> /// <param name="encodingParms"></param> /// <param name="decryptKeyNameAlg"></param> /// <param name="ephemPub"></param> /// <returns>key exchange key blob</returns> public byte[] EcdhGetKeyExchangeKey(byte[] encodingParms, TpmAlgId decryptKeyNameAlg, out EccPoint ephemPub) { byte[] keyExchangeKey = null; ephemPub = null; #if !__MonoCS__ var eccParms = (EccParms)PublicParms.parameters; int keyBits = RawEccKey.GetKeyLength(eccParms.curveID); // Make a new ephemeral key #if TSS_USE_BCRYPT var ephKey = Generate(RawEccKey.GetEccAlg(PublicParms), (uint)keyBits); byte[] ephPub = ephKey.Export(Native.BCRYPT_ECCPUBLIC_BLOB); byte[] otherPub = Key.Export(Native.BCRYPT_ECCPUBLIC_BLOB); #else using (var eph = new ECDiffieHellmanCng(keyBits)) { byte[] otherPub = EcDhProvider.PublicKey.ToByteArray(); byte[] ephPub = eph.PublicKey.ToByteArray(); eph.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash; eph.HashAlgorithm = GetCngAlgorithm(decryptKeyNameAlg); #endif // !TSS_USE_BCRYPT byte[] herPubX, herPubY; RawEccKey.KeyInfoFromPublicBlob(otherPub, out herPubX, out herPubY); byte[] myPubX, myPubY; RawEccKey.KeyInfoFromPublicBlob(ephPub, out myPubX, out myPubY); byte[] otherInfo = Globs.Concatenate(new[] { encodingParms, myPubX, herPubX }); // The TPM uses the following number of bytes from the KDF int bytesNeeded = CryptoLib.DigestSize(decryptKeyNameAlg); keyExchangeKey = new byte[bytesNeeded]; for (int pos = 0, count = 1, bytesToCopy = 0; pos < bytesNeeded; ++count, pos += bytesToCopy) { byte[] secretPrepend = Marshaller.GetTpmRepresentation((UInt32)count); #if TSS_USE_BCRYPT byte[] fragment = ephKey.DeriveKey(Key, decryptKeyNameAlg, secretPrepend, otherInfo); #else eph.SecretAppend = otherInfo; eph.SecretPrepend = secretPrepend; byte[] fragment = eph.DeriveKeyMaterial(EcDhProvider.Key); #endif // !TSS_USE_BCRYPT bytesToCopy = Math.Min(bytesNeeded - pos, fragment.Length); Array.Copy(fragment, 0, keyExchangeKey, pos, bytesToCopy); } ephemPub = new EccPoint(myPubX, myPubY); #if !TSS_USE_BCRYPT } #endif #endif // !__MonoCS__ return(keyExchangeKey); }
/// <summary> /// Create activation blobs that can be passed to ActivateCredential. Two blobs are returned - /// (a) - encryptedSecret - is the symmetric key cfb-symmetrically encrypted with an enveloping key /// (b) credentialBlob (the return value of this function) - is the enveloping key OEAP (RSA) encrypted /// by the public part of this key. /// </summary> /// <param name="secret"></param> /// <param name="nameAlgId"></param> /// <param name="nameOfKeyToBeActivated"></param> /// <param name="encryptedSecret"></param> /// <returns>CredentialBlob (</returns> public byte[] CreateActivationCredentials( byte[] secret, TpmAlgId nameAlgId, byte[] nameOfKeyToBeActivated, out byte[] encryptedSecret) { byte[] seed, encSecret; switch (type) { case TpmAlgId.Rsa: // The seed should be the same size as the symmKey seed = Globs.GetRandomBytes((CryptoLib.DigestSize(nameAlg) + 7) / 8); encSecret = EncryptOaep(seed, ActivateEncodingParms); break; case TpmAlgId.Ecc: EccPoint pubEphem; seed = EcdhGetKeyExchangeKey(ActivateEncodingParms, nameAlg, out pubEphem); encSecret = Marshaller.GetTpmRepresentation(pubEphem); break; default: Globs.Throw <NotImplementedException>("CreateActivationCredentials: Unsupported algorithm"); encryptedSecret = new byte[0]; return(new byte[0]); } var cvx = new Tpm2bDigest(secret); byte[] cvTpm2B = Marshaller.GetTpmRepresentation(cvx); SymDefObject symDef = TssObject.GetSymDef(this); byte[] symKey = KDF.KDFa(nameAlg, seed, "STORAGE", nameOfKeyToBeActivated, new byte[0], symDef.KeyBits); byte[] encIdentity; using (SymmCipher symm2 = SymmCipher.Create(symDef, symKey)) { encIdentity = symm2.Encrypt(cvTpm2B); } var hmacKeyBits = CryptoLib.DigestSize(nameAlg); byte[] hmacKey = KDF.KDFa(nameAlg, seed, "INTEGRITY", new byte[0], new byte[0], hmacKeyBits * 8); byte[] outerHmac = CryptoLib.HmacData(nameAlg, hmacKey, Globs.Concatenate(encIdentity, nameOfKeyToBeActivated)); byte[] activationBlob = Globs.Concatenate( Marshaller.ToTpm2B(outerHmac), encIdentity); encryptedSecret = encSecret; return(activationBlob); }
private void ProcessError(string message, out byte[] byteBuf) { Console.Error.WriteLine(message); Console.Error.WriteLine("This will be processed as a TpmRc.NotUsed error"); byteBuf = Marshaller.GetTpmRepresentation(new Object[] { TpmSt.NoSessions, (uint)10, TpmRc.NotUsed }); }
/// <summary> /// Get the date of the specification from which the TPM was built. /// </summary> /// <param name="manufacturer"></param> /// <param name="year"></param> /// <param name="dayOfYear"></param> /// <param name="tpm"></param> public static void GetTpmInfo(Tpm2 tpm, out string manufacturer, out uint year, out uint dayOfYear) { // ReSharper disable once RedundantAssignment manufacturer = ""; year = GetProperty(tpm, Pt.Year); dayOfYear = GetProperty(tpm, Pt.DayOfYear); uint manX = GetProperty(tpm, Pt.Manufacturer); var arr = Marshaller.GetTpmRepresentation(manX); manufacturer = (new System.Text.UTF8Encoding()).GetString(arr, 0, arr.Length); }
/// <summary> /// Calculate the qualified name of an object presumed loaded under the provided ancestral chain /// in a given hierarchy. /// </summary> /// <param name="hierarchyHandle"></param> /// <param name="children"></param> /// <returns></returns> public static byte[] GetQualifiedName(TpmHandle hierarchyHandle, TpmPublic[] children) { byte[] runningName = Marshaller.GetTpmRepresentation(hierarchyHandle); foreach (TpmPublic pub in children) { byte[] thisName = pub.GetName(); runningName = Globs.Concatenate ( Marshaller.GetTpmRepresentation(pub.nameAlg), CryptoLib.HashData(pub.nameAlg, new[] { runningName, thisName }) ); } return(runningName); }
/// <summary> /// Create an enveloped (encrypted and integrity protected) private area from a provided sensitive. /// </summary> /// <param name="iv"></param> /// <param name="sens"></param> /// <param name="nameHash"></param> /// <param name="publicName"></param> /// <param name="symWrappingAlg"></param> /// <param name="symKey"></param> /// <param name="parentNameAlg"></param> /// <param name="parentSeed"></param> /// <param name="f"></param> /// <returns></returns> public static byte[] CreatePrivateFromSensitive( SymDefObject symWrappingAlg, byte[] symKey, byte[] iv, Sensitive sens, TpmAlgId nameHash, byte[] publicName, TpmAlgId parentNameAlg, byte[] parentSeed, TssObject.Transformer f = null) { // ReSharper disable once InconsistentNaming byte[] tpm2bIv = Marshaller.ToTpm2B(iv); Transform(tpm2bIv, f); byte[] sensitive = sens.GetTpmRepresentation(); Transform(sensitive, f); // ReSharper disable once InconsistentNaming byte[] tpm2bSensitive = Marshaller.ToTpm2B(sensitive); Transform(tpm2bSensitive, f); byte[] encSensitive = SymCipher.Encrypt(symWrappingAlg, symKey, iv, tpm2bSensitive); Transform(encSensitive, f); byte[] decSensitive = SymCipher.Decrypt(symWrappingAlg, symKey, iv, encSensitive); Debug.Assert(f != null || Globs.ArraysAreEqual(decSensitive, tpm2bSensitive)); var hmacKeyBits = CryptoLib.DigestSize(parentNameAlg) * 8; byte[] hmacKey = KDF.KDFa(parentNameAlg, parentSeed, "INTEGRITY", new byte[0], new byte[0], hmacKeyBits); Transform(hmacKey, f); byte[] dataToHmac = Marshaller.GetTpmRepresentation(tpm2bIv, encSensitive, publicName); Transform(dataToHmac, f); byte[] outerHmac = CryptoLib.Hmac(parentNameAlg, hmacKey, dataToHmac); Transform(outerHmac, f); byte[] priv = Marshaller.GetTpmRepresentation(Marshaller.ToTpm2B(outerHmac), tpm2bIv, encSensitive); Transform(priv, f); return(priv); }
/// <summary> /// Calculate and return the auth-hmac (or plaintext auth if it is a policy session with PlaintextAuth set) /// based on the current session parms. /// </summary> /// <param name="parmHash"></param> /// <param name="direction"></param> /// <param name="nonceDec"></param> /// <param name="nonceEnc"></param> /// <returns></returns> internal byte[] GetAuthHmac(byte[] parmHash, Direction direction, byte[] nonceDec = null, byte[] nonceEnc = null) { // special case. If this is a policy session and the session includes PolicyPassword the // TPM expects and assumes that the HMAC field will have the plaintext entity field as in // a PWAP session (the related PolicyAuthValue demands an HMAC as usual) if (PlaintextAuth) { return(Handle.Auth ?? AuthHandle.Auth); } byte[] nonceNewer, nonceOlder; if (direction == Direction.Command) { nonceNewer = NonceCaller; nonceOlder = NonceTpm; } else { nonceNewer = NonceTpm; nonceOlder = NonceCaller; } byte[] sessionAttrs = Marshaller.GetTpmRepresentation(Attrs); byte[] auth = Handle.Auth; if (AuthHandle != null && Handle != TpmRh.TpmRsPw && auth == null && ((SessionType != TpmSe.Policy && BindObject != AuthHandle) || (SessionType == TpmSe.Policy && SessIncludesAuth))) { auth = Globs.TrimTrailingZeros(AuthHandle.Auth); } byte[] hmacKey = Globs.Concatenate(SessionKey, auth); byte[] bufToHmac = Globs.Concatenate(new[] { parmHash, nonceNewer, nonceOlder, nonceDec, nonceEnc, sessionAttrs }); byte[] hmac = CryptoLib.Hmac(AuthHash, hmacKey, bufToHmac); #if false Console.WriteLine(Globs.FormatBytesCompact("hmacKey: ", hmacKey)); Console.WriteLine(Globs.FormatBytesCompact("nonceNewer: ", nonceNewer)); Console.WriteLine(Globs.FormatBytesCompact("nonceOlder: ", nonceOlder)); Console.WriteLine(Globs.FormatBytesCompact("nonceDec: ", nonceDec)); Console.WriteLine(Globs.FormatBytesCompact("nonceEnc: ", nonceEnc)); Console.WriteLine(Globs.FormatBytesCompact("attrs: ", sessionAttrs)); Console.WriteLine(Globs.FormatBytesCompact("HMAC: ", hmac)); #endif return(hmac); }
/// <summary> /// Get the TPM name of the associated entity. /// /// If the entity is a transient object, persistent object or NV index, the /// name must have been previously set explicitly by the caller (by means of /// SetName() or GetName(Tpm2 tpm) methods) or implicitly by the framework /// (when an object is created by means of CreatePrimary, CreateLoaded or /// Create command). /// /// Otherwise the name is a 4-byte TPM representation of the handle value. /// </summary> public byte[] GetName() { Ht ht = GetType(); switch (ht) { case Ht.Transient: case Ht.Persistent: case Ht.NvIndex: return(_Name); case Ht.Pcr: case Ht.HmacSession: case Ht.PolicySession: case Ht.Permanent: return(Marshaller.GetTpmRepresentation(handle)); default: return(null); } }
/// <summary> /// Implements marshaling logic for most of the TPM object types. /// Can be overridden if a custom marshaling logic is required (e.g. when /// marshaling of a field depends on other field's value). /// </summary> /// <param name="m"></param> /// <returns></returns> internal virtual void ToNet(Marshaller m) { var members = GetFieldsToMarshal(); dbg.Indent(); for (int i = 0; i < members.Length; ++i) { var mem = members[i]; object memVal = Globs.GetMember(mem, this); dbg.Trace(i + ": " + mem.Name + " = " + memVal); if (mem.SizeLength > 0) { bool arr = mem.WireType == MarshalType.VariableLengthArray; int len = arr ? (memVal == null ? 0 : ((Array)memVal).Length) : Marshaller.GetTpmRepresentation(memVal).Length; dbg.Trace("Sending " + (arr ? "Array " : "Struct ") + mem.Name + " of size " + len); m.PutSizeTag(len, mem.SizeLength, mem.SizeName); } m.Put(memVal, mem.Name); } dbg.Unindent(); }
/// <summary> /// Create an enveloped (encrypted and integrity protected) private area from a provided sensitive. /// </summary> /// <param name="iv"></param> /// <param name="sens"></param> /// <param name="nameHash"></param> /// <param name="publicName"></param> /// <param name="symWrappingAlg"></param> /// <param name="symKey"></param> /// <param name="parentNameAlg"></param> /// <param name="parentSeed"></param> /// <param name="f"></param> /// <returns></returns> public static byte[] CreatePrivateFromSensitive( SymDefObject symWrappingAlg, byte[] symKey, byte[] iv, Sensitive sens, TpmAlgId nameHash, byte[] publicName, TpmAlgId parentNameAlg, byte[] parentSeed) { // ReSharper disable once InconsistentNaming byte[] tpm2bIv = Marshaller.ToTpm2B(iv); byte[] sensitive = sens.GetTpmRepresentation(); // ReSharper disable once InconsistentNaming byte[] tpm2bSensitive = Marshaller.ToTpm2B(sensitive); byte[] encSensitive = SymmCipher.Encrypt(symWrappingAlg, symKey, iv, tpm2bSensitive); byte[] decSensitive = SymmCipher.Decrypt(symWrappingAlg, symKey, iv, encSensitive); var hmacKeyBits = CryptoLib.DigestSize(parentNameAlg) * 8; byte[] hmacKey = KDF.KDFa(parentNameAlg, parentSeed, "INTEGRITY", new byte[0], new byte[0], hmacKeyBits); byte[] dataToHmac = Marshaller.GetTpmRepresentation(tpm2bIv, encSensitive, publicName); byte[] outerHmac = CryptoLib.HmacData(parentNameAlg, hmacKey, dataToHmac); byte[] priv = Marshaller.GetTpmRepresentation(Marshaller.ToTpm2B(outerHmac), tpm2bIv, encSensitive); return(priv); }
/// <summary> /// Replace the hash value with the hash of the concatenation of the current value and the TPM representation /// of objectToExtend /// </summary> /// <param name="objectToExtend"></param> /// <returns></returns> public TpmHash Extend(Object objectToExtend) { byte[] temp = Marshaller.GetTpmRepresentation(objectToExtend); HashData = CryptoLib.HashData(HashAlg, HashData, temp); return(this); }
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> /// Creates a duplication blob for the current key that can be Imported as a child /// of newParent. Three forms are possible. GetPlaintextDuplicationBlob() allows /// plaintext-import. This function enables duplication with and without an /// inner wrapper (depending on whether innerWrapper is null) /// </summary> /// <param name="newParent"></param> /// <param name="innerWrapper"></param> /// <param name="encSecret"></param> /// <returns></returns> public TpmPrivate GetDuplicationBlob( TpmPublic pubNewParent, SymCipher innerWrapper, out byte[] encSecret) { byte[] encSensitive; if (innerWrapper == null) { // No inner wrapper encSensitive = Marshaller.ToTpm2B(Sensitive.GetTpmRepresentation()); Transform(encSensitive); } else { byte[] sens = Marshaller.ToTpm2B(Sensitive.GetTpmRepresentation()); byte[] toHash = Globs.Concatenate(sens, GetName()); Transform(toHash); byte[] innerIntegrity = Marshaller.ToTpm2B(CryptoLib.HashData( Public.nameAlg, toHash)); byte[] innerData = Globs.Concatenate(innerIntegrity, sens); Transform(innerData); encSensitive = innerWrapper.Encrypt(innerData); Transform(encSensitive); } byte[] seed; SymDefObject symDef = GetSymDef(pubNewParent).Copy(); // TPM duplication procedures always use CFB mode symDef.Mode = TpmAlgId.Cfb; using (var swNewParent = AsymCryptoSystem.CreateFrom(pubNewParent)) { switch (pubNewParent.type) { case TpmAlgId.Rsa: // The seed should be the same size as the scheme hash LastSeed = seed = Globs.GetRandomBytes( CryptoLib.DigestSize(swNewParent.OaepHash)); encSecret = swNewParent.EncryptOaep(seed, DuplicateEncodingParms); break; case TpmAlgId.Ecc: EccPoint pubEphem; seed = swNewParent.EcdhGetKeyExchangeKey(DuplicateEncodingParms, pubNewParent.nameAlg, out pubEphem); encSecret = Marshaller.GetTpmRepresentation(pubEphem); break; default: Globs.Throw <NotImplementedException>( "GetDuplicationBlob: Unsupported algorithm"); encSecret = new byte[0]; return(new TpmPrivate()); } } Transform(seed); Transform(encSecret); byte[] symKey = KDF.KDFa(pubNewParent.nameAlg, seed, "STORAGE", Public.GetName(), new byte[0], symDef.KeyBits); Transform(symKey); byte[] dupSensitive; using (SymCipher enc2 = SymCipher.Create(symDef, symKey)) { if (enc2 == null) { return(null); } dupSensitive = enc2.Encrypt(encSensitive); } Transform(dupSensitive); var npNameNumBits = CryptoLib.DigestSize(pubNewParent.nameAlg) * 8; byte[] hmacKey = KDF.KDFa(pubNewParent.nameAlg, seed, "INTEGRITY", new byte[0], new byte[0], npNameNumBits); byte[] outerDataToHmac = Globs.Concatenate(dupSensitive, Public.GetName()); Transform(outerDataToHmac); byte[] outerHmac = Marshaller.ToTpm2B(CryptoLib.Hmac(pubNewParent.nameAlg, hmacKey, outerDataToHmac)); Transform(outerHmac); byte[] dupBlob = Globs.Concatenate(outerHmac, dupSensitive); Transform(dupBlob); return(new TpmPrivate(dupBlob)); }
/// <summary> /// Create activation blobs that can be passed to ActivateCredential. Two /// blobs are returned: /// 1) encryptedSecret - symmetric key cfb-symmetrically encrypted with the /// enveloping key; /// 2) credentialBlob - the enveloping key OEAP (RSA) encrypted by the public /// part of this key. This is the return value of this /// function /// </summary> /// <param name="secret"></param> /// <param name="nameOfKeyToBeActivated"></param> /// <param name="encryptedSecret"></param> /// <returns>CredentialBlob (</returns> public IdObject CreateActivationCredentials(byte[] secret, byte[] nameOfKeyToBeActivated, out byte[] encryptedSecret) { byte[] seed, encSecret; switch (type) { case TpmAlgId.Rsa: // The seed should be the same size as the name algorithmdigest seed = Globs.GetRandomBytes(CryptoLib.DigestSize(nameAlg)); encSecret = EncryptOaep(seed, ActivateEncodingParms); break; case TpmAlgId.Ecc: EccPoint ephemPubPt; seed = EcdhGetKeyExchangeKey(ActivateEncodingParms, out ephemPubPt); encSecret = Marshaller.GetTpmRepresentation(ephemPubPt); break; default: Globs.Throw <NotImplementedException>( "CreateActivationCredentials: Unsupported algorithm"); encryptedSecret = new byte[0]; return(null); } Transform(seed); Transform(encSecret); var cvx = new Tpm2bDigest(secret); byte[] cvTpm2B = Marshaller.GetTpmRepresentation(cvx); Transform(cvTpm2B); SymDefObject symDef = TssObject.GetSymDef(this); byte[] symKey = KDF.KDFa(nameAlg, seed, "STORAGE", nameOfKeyToBeActivated, new byte[0], symDef.KeyBits); Transform(symKey); byte[] encIdentity; // TPM only uses CFB mode in its command implementations var sd = symDef.Copy(); sd.Mode = TpmAlgId.Cfb; using (var sym = SymCipher.Create(sd, symKey)) { // Not all keys specs are supported by SW crypto if (sym == null) { encryptedSecret = null; return(null); } encIdentity = sym.Encrypt(cvTpm2B); } Transform(encIdentity); var hmacKeyBits = CryptoLib.DigestSize(nameAlg); byte[] hmacKey = KDF.KDFa(nameAlg, seed, "INTEGRITY", new byte[0], new byte[0], hmacKeyBits * 8); Transform(hmacKey); byte[] outerHmac = CryptoLib.Hmac(nameAlg, hmacKey, Globs.Concatenate(encIdentity, nameOfKeyToBeActivated)); Transform(outerHmac); encryptedSecret = encSecret; return(new IdObject(outerHmac, encIdentity)); }
} // VerifySignature() /// <summary> /// Generates the key exchange key and the public part of the ephemeral key /// using specified encoding parameters in the KDF (ECC only). /// </summary> /// <param name="encodingParms"></param> /// <param name="decryptKeyNameAlg"></param> /// <param name="ephemPub"></param> /// <returns>key exchange key blob</returns> public byte[] EcdhGetKeyExchangeKey(byte[] encodingParms, TpmAlgId decryptKeyNameAlg, out EccPoint ephemPub) { var eccParms = (EccParms)PublicParms.parameters; int keyBits = RawEccKey.GetKeyLength(eccParms.curveID); byte[] keyExchangeKey = null; ephemPub = new EccPoint(); // Make a new ephemeral key var prov = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(RawEccKey.GetEccAlg(PublicParms)); var ephKey = prov.CreateKeyPair((uint)keyBits); IBuffer ephPubBuf = ephKey.ExportPublicKey(CryptographicPublicKeyBlobType.BCryptEccFullPublicKey); byte[] ephPub; CryptographicBuffer.CopyToByteArray(ephPubBuf, out ephPub); IBuffer otherPubBuf = Key.ExportPublicKey(CryptographicPublicKeyBlobType.BCryptEccFullPublicKey); byte[] otherPub; CryptographicBuffer.CopyToByteArray(otherPubBuf, out otherPub); byte[] herPubX, herPubY; RawEccKey.KeyInfoFromPublicBlob(otherPub, out herPubX, out herPubY); byte[] myPubX, myPubY; RawEccKey.KeyInfoFromPublicBlob(ephPub, out myPubX, out myPubY); byte[] otherInfo = Globs.Concatenate(new[] { encodingParms, myPubX, herPubX }); // The TPM uses the following number of bytes from the KDF int bytesNeeded = CryptoLib.DigestSize(decryptKeyNameAlg); keyExchangeKey = new byte[bytesNeeded]; for (int pos = 0, count = 1, bytesToCopy = 0; pos < bytesNeeded; ++count, pos += bytesToCopy) { byte[] secretPrepend = Marshaller.GetTpmRepresentation((UInt32)count); string algName; KeyDerivationParameters deriveParams; switch (decryptKeyNameAlg) { case TpmAlgId.Kdf1Sp800108: algName = KeyDerivationAlgorithmNames.Sp800108CtrHmacSha256; deriveParams = KeyDerivationParameters.BuildForSP800108(CryptographicBuffer.CreateFromByteArray(secretPrepend), CryptographicBuffer.CreateFromByteArray(otherInfo)); break; case TpmAlgId.Kdf1Sp80056a: algName = KeyDerivationAlgorithmNames.Sp80056aConcatSha256; deriveParams = KeyDerivationParameters.BuildForSP80056a(CryptographicBuffer.ConvertStringToBinary(algName, BinaryStringEncoding.Utf8), CryptographicBuffer.ConvertStringToBinary("TPM", BinaryStringEncoding.Utf8), CryptographicBuffer.CreateFromByteArray(secretPrepend), CryptographicBuffer.ConvertStringToBinary("", BinaryStringEncoding.Utf8), CryptographicBuffer.CreateFromByteArray(otherInfo)); break; case TpmAlgId.Kdf2: algName = KeyDerivationAlgorithmNames.Pbkdf2Sha256; deriveParams = KeyDerivationParameters.BuildForPbkdf2(CryptographicBuffer.CreateFromByteArray(secretPrepend), 1000); break; default: Globs.Throw <ArgumentException>("wrong KDF name"); return(null); } KeyDerivationAlgorithmProvider deriveProv = KeyDerivationAlgorithmProvider.OpenAlgorithm(algName); IBuffer keyMaterial = CryptographicEngine.DeriveKeyMaterial(Key, deriveParams, (uint)keyBits); byte[] fragment; CryptographicBuffer.CopyToByteArray(keyMaterial, out fragment); bytesToCopy = Math.Min(bytesNeeded - pos, fragment.Length); Array.Copy(fragment, 0, keyExchangeKey, pos, bytesToCopy); } ephemPub = new EccPoint(myPubX, myPubY); return(keyExchangeKey); }
/// <summary> /// Implements unmarshaling logic for most of the TPM object types. /// Can be overridden if a custom unmarshaling logic is required (e.g. /// when unmarshaling of a field depends on other field's value). /// </summary> /// <param name="m"></param> /// <returns></returns> internal virtual void ToHost(Marshaller m) { dbg.Indent(); var members = GetFieldsToMarshal(true); uint mshlStartPos = m.GetGetPos(); for (int i = 0; i < members.Length; ++i) { TpmStructMemberInfo memInfo = members[i]; Type memType = Globs.GetMemberType(memInfo); var wireType = memInfo.WireType; int size = -1; switch (wireType) { case MarshalType.Union: { dbg.Trace("Union " + memType.Name + " with selector " + memInfo.Tag.Value); var elt = UnionElementFromSelector(memType, memInfo.Tag.Value); memInfo.Value = m.Get(elt, 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.SpecialVariableLengthArray: { size = CryptoLib.DigestSize((TpmAlgId)members[i - 1].Value); UnmarshalArray(m, memInfo, memType, size); break; } case MarshalType.VariableLengthArray: { size = m.GetSizeTag(memInfo.SizeLength, memInfo.SizeName); UnmarshalArray(m, memInfo, memType, size); break; } case MarshalType.EncryptedVariableLengthArray: { uint unmarshaled = m.GetGetPos() - mshlStartPos; size = m.SizedStructLen[m.SizedStructLen.Count - 1] - (int)unmarshaled; UnmarshalArray(m, memInfo, memType, size); break; } case MarshalType.SizedStruct: { size = m.GetSizeTag(memInfo.SizeLength, memInfo.SizeName); if (size == 0) { break; } m.SizedStructLen.Add(size); memInfo.Value = m.Get(memType, memInfo.Name); int unmSize = Marshaller.GetTpmRepresentation(memInfo.Value).Length; if (unmSize != size) { if (unmSize < size && memType.Name == "TpmPublic") { var pub = memInfo.Value as TpmPublic; var label = Marshaller.GetTpmRepresentation(pub.unique); var context = m.GetArray(typeof(byte), size - unmSize, "") as byte[]; pub.unique = new TpmDerive(label, context); } else { var msg = string.Format("Invalid size {0} (instead of " + "{1}) for unmarshaled {2}.{3}", unmSize, size, this.GetType(), memInfo.Name); throw new TssException(msg); } } m.SizedStructLen.RemoveAt(m.SizedStructLen.Count - 1); 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) + ": " + wireType + " " + memInfo.Name + (size != -1 ? " of size " + size : "")); // Some property values are dynamically obtained from their linked fields. // Correspondingly, they do not have a setter, so we bypass them here. Debug.Assert(wireType != MarshalType.LengthOfStruct && wireType != MarshalType.ArrayCount); if (wireType != MarshalType.UnionSelector) { Globs.SetMember(memInfo, this, memInfo.Value); } } dbg.Unindent(); }
/// <summary> /// Dispatch a command to the underlying TPM. This method implements all significant functionality. /// DispatchCommand examines the command stream and performs (approximately) the following functions /// 1) If the command references a handle (session or transient object) then TBS makes sure that the entity /// is loaded. If it is, then the handle is "translated" to the underlying TPM handle. If it is not, then /// TBS checks to see if it has a saved context for the entity, and if so loads it. /// 2) If the command will fill a slot, then TBS ensures that a slot is available. It does this by ContextSaving /// the LRU entity of the proper type (that is not used in this command). /// </summary> /// <param name="caller"></param> /// <param name="active"></param> /// <param name="inBuf"></param> /// <param name="outBuf"></param> /// <exception cref="Exception"></exception> internal void DispatchCommand(TbsContext caller, CommandModifier active, byte[] inBuf, out byte[] outBuf) { lock (this) { CommandNumber++; // ReSharper disable once CompareOfFloatsByEqualityOperator if (StateSaveProbability != 0.0) { // S3 debug support DebugStateSave(); LastStateSaveCommandNumber = CommandNumber; } CommandHeader commandHeader; TpmHandle[] inHandles; SessionIn[] inSessions; byte[] commandParmsNoHandles; bool legalCommand = CommandProcessor.CrackCommand(inBuf, out commandHeader, out inHandles, out inSessions, out commandParmsNoHandles); if (!legalCommand) { // Is a diagnostics command. Pass through to TPM (a real RM would refuse). TpmDevice.DispatchCommand(active, inBuf, out outBuf); return; } TpmCc commandCode = commandHeader.CommandCode; // Lookup command CommandInfo command = Tpm2.CommandInfoFromCommandCode(commandCode); if (command == null) { throw new Exception("Unrecognized command"); } if (commandCode == TpmCc.ContextLoad || commandCode == TpmCc.ContextSave) { //throw new Exception("ContextLoad and ContextSave not supported in this build"); Console.Error.WriteLine("ContextLoad and ContextSave not supported in this build"); outBuf = Marshaller.GetTpmRepresentation(new Object[] { TpmSt.NoSessions, (uint)10, TpmRc.NotUsed }); } // Look up referenced objects and sessions ObjectContext[] neededObjects = GetReferencedObjects(caller, inHandles); ObjectContext[] neededSessions = GetSessions(caller, inSessions); if (neededObjects == null || neededSessions == null) { // This means that one or more of the handles was not registered for the context byte[] ret = FormatError(TpmRc.Handle); outBuf = ret; return; } // Load referenced objects and sessions (free slots if needed) bool loadOk = LoadEntities(neededObjects); bool loadOk2 = LoadEntities(neededSessions); if (!loadOk || !loadOk2) { throw new Exception("Failed to make space for objects or sessions at to execute command"); } // At this point everything referenced should be loaded, and there will be a free slot if needed // so we can translate the input handles to the underlying handles ReplaceHandlesIn(inHandles, inSessions, neededObjects, neededSessions); // create the translated command from the various components we have been manipulating byte[] commandBuf = CommandProcessor.CreateCommand(commandHeader.CommandCode, inHandles, inSessions, commandParmsNoHandles); Debug.Assert(commandBuf.Length == inBuf.Length); byte[] responseBuf; // Todo: Virtualize GetCapability for handle enumeration. // // Execute command on underlying TPM device. // If we get an ObjectMemory or SessionMemory error we try to make more space and try again // Note: If the TPM device throws an error above we let it propagate out. There should be no side // effects on TPM state that the TBS cares about. // do { TpmDevice.DispatchCommand(active, commandBuf, out responseBuf); TpmRc resCode = GetResultCode(responseBuf); if (resCode == TpmRc.Success) { break; } if (resCode == TpmRc.ObjectMemory) { bool slotMade = MakeSpace(SlotType.ObjectSlot, neededObjects); if (!slotMade) { throw new Exception("Failed to make an object slot in the TPM"); } continue; } if (resCode == TpmRc.SessionMemory) { bool slotMade = MakeSpace(SlotType.SessionSlot, neededSessions); if (!slotMade) { throw new Exception("Failed to make a session slot in the TPM"); } continue; } break; } while (true); // Parse the response from the TPM // TODO: Make this use the new methods in Tpm2 // ReSharper disable once UnusedVariable var mOut = new Marshaller(responseBuf); TpmSt responseTag; uint responseParamSize; TpmRc resultCode; TpmHandle[] responseHandles; SessionOut[] responseSessions; byte[] responseParmsNoHandles, responseParmsWithHandles; CommandProcessor.SplitResponse(responseBuf, command.HandleCountOut, out responseTag, out responseParamSize, out resultCode, out responseHandles, out responseSessions, out responseParmsNoHandles, out responseParmsWithHandles); // If we have an error there is no impact on the loaded sessions, but we update // the LRU values because the user will likely try again. if (resultCode != TpmRc.Success) { outBuf = responseBuf; UpdateLastUseCount(new[] { neededObjects, neededSessions }); return; } // Update TBS database with any newly created TPM objects ProcessUpdatedTpmState(caller, command, responseHandles, neededObjects); // And if there were any newly created objects use the new DB entries to translate the handles ReplaceHandlesOut(responseHandles); byte[] translatedResponse = CommandProcessor.CreateResponse(resultCode, responseHandles, responseSessions, responseParmsNoHandles); outBuf = translatedResponse; Debug.Assert(outBuf.Length == responseBuf.Length); } // lock(this) }
/// <summary> /// Creates a duplication blob for the current key that can be Imported as a child /// of newParent. Three forms are possible. GetPlaintextDuplicationBlob() allows /// plaintext-import. This function enables duplication with and without an /// inner wrapper (depending on whether innerWrapper is null) /// </summary> /// <param name="newParent"></param> /// <param name="innerWrapper"></param> /// <param name="encryptedWrappingKey"></param> /// <returns></returns> public TpmPrivate GetDuplicationBlob( TpmPublic newParent, SymmCipher innerWrapper, out byte[] encryptedWrappingKey) { byte[] encSensitive; if (innerWrapper == null) { // No inner wrapper encSensitive = Marshaller.ToTpm2B(sensitivePart.GetTpmRepresentation()); } else { byte[] sens = Marshaller.ToTpm2B(sensitivePart.GetTpmRepresentation()); byte[] toHash = Globs.Concatenate(sens, GetName()); byte[] innerIntegrity = Marshaller.ToTpm2B(CryptoLib.HashData(publicPart.nameAlg, toHash)); byte[] innerData = Globs.Concatenate(innerIntegrity, sens); encSensitive = innerWrapper.Encrypt(innerData); } byte[] seed, encSecret; SymDefObject symDef = GetSymDef(newParent); using (AsymCryptoSystem newParentPubKey = AsymCryptoSystem.CreateFrom(newParent)) { switch (newParent.type) { case TpmAlgId.Rsa: // The seed should be the same size as the symmKey seed = Globs.GetRandomBytes((symDef.KeyBits + 7) / 8); encSecret = newParentPubKey.EncryptOaep(seed, DuplicateEncodingParms); break; case TpmAlgId.Ecc: EccPoint pubEphem; seed = newParentPubKey.EcdhGetKeyExchangeKey(DuplicateEncodingParms, newParent.nameAlg, out pubEphem); encSecret = Marshaller.GetTpmRepresentation(pubEphem); break; default: Globs.Throw <NotImplementedException>("GetDuplicationBlob: Unsupported algorithm"); encryptedWrappingKey = new byte[0]; return(new TpmPrivate()); } } encryptedWrappingKey = encSecret; byte[] symKey = KDF.KDFa(newParent.nameAlg, seed, "STORAGE", publicPart.GetName(), new byte[0], symDef.KeyBits); byte[] dupSensitive; using (SymmCipher enc2 = SymmCipher.Create(symDef, symKey)) { dupSensitive = enc2.Encrypt(encSensitive); } var npNameNumBits = CryptoLib.DigestSize(newParent.nameAlg) * 8; byte[] hmacKey = KDF.KDFa(newParent.nameAlg, seed, "INTEGRITY", new byte[0], new byte[0], npNameNumBits); byte[] outerDataToHmac = Globs.Concatenate(dupSensitive, publicPart.GetName()); byte[] outerHmac = Marshaller.ToTpm2B(CryptoLib.HmacData(newParent.nameAlg, hmacKey, outerDataToHmac)); byte[] dupBlob = Globs.Concatenate(outerHmac, dupSensitive); return(new TpmPrivate(dupBlob)); }
/// <summary> /// Dispatch a command to the underlying TPM. This method implements all /// significant functionality. It examines the command stream and performs /// (approximately) the following actions: /// 1) If the command references a handle (session or transient object), then /// TBS makes sure that the entity is loaded. If it is, then the handle is /// "translated" to the underlying TPM handle. If it is not, then TBS checks /// to see if it has a saved context for the entity, and if so, loads it. /// 2) If the command will fill a slot, then TBS ensures that a slot is available. /// It does this by ContextSaving the LRU entity of the proper type (that is /// not used in this command). /// </summary> /// <param name="caller"></param> /// <param name="active"></param> /// <param name="inBuf"></param> /// <param name="outBuf"></param> /// <exception cref="Exception"></exception> internal void DispatchCommand(TbsContext caller, CommandModifier active, byte[] inBuf, out byte[] outBuf) { lock (this) { CommandNumber++; // ReSharper disable once CompareOfFloatsByEqualityOperator if (StateSaveProbability != 0.0) { // S3 debug support DebugStateSave(); LastStateSaveCommandNumber = CommandNumber; } CommandHeader commandHeader; TpmHandle[] inHandles; SessionIn[] inSessions; byte[] commandParmsNoHandles; bool legalCommand = CommandProcessor.CrackCommand(inBuf, out commandHeader, out inHandles, out inSessions, out commandParmsNoHandles); if (!legalCommand) { // Is a diagnostics command. Pass through to TPM (a real RM would refuse). TpmDevice.DispatchCommand(active, inBuf, out outBuf); return; } TpmCc cc = commandHeader.CommandCode; // Lookup command CommandInfo command = Tpm2.CommandInfoFromCommandCode(cc); if (command == null) { throw new Exception("Unrecognized command"); } if (cc == TpmCc.ContextLoad || cc == TpmCc.ContextSave) { Debug.WriteLine("ContextLoad and ContextSave are not supported in this build"); outBuf = Marshaller.GetTpmRepresentation(new Object[] { TpmSt.NoSessions, (uint)10, TpmRc.NotUsed }); } // Look up referenced objects and sessions ObjectContext[] neededObjects = GetReferencedObjects(caller, inHandles); ObjectContext[] neededSessions = GetSessions(caller, inSessions); ObjectContext[] neededEntities = neededObjects != null ? neededSessions != null ? neededObjects.Concat(neededSessions).ToArray() : neededObjects : neededSessions; #if false // LibTester may intentionally use invalid handles, therefore it always // work in the passthru mode (all correctness checks by TSS infra suppressed) if (!Tpm2._TssBehavior.Passthrough && (neededObjects == null || neededSessions == null)) #endif if (neededObjects == null || neededSessions == null) { // One or more of the handles was not registered for the context byte[] ret = FormatError(TpmRc.Handle); outBuf = ret; return; } // Load referenced objects and sessions (free slots if needed) // It's important to load all object and session handles in a single call // to LoadEntities(), as for some commands (e.g. GetSessionAuditDigest) // the objects array may contain session handles. In this case the session // handles loaded by the invocation of LoadEntities for neededObjects // may be evicted again during the subsequent call for neededSessions. var expectedResponses = Tpm._GetExpectedResponses(); if (!LoadEntities(neededEntities)) { throw new Exception("Failed to make space for objects or sessions"); } else { // At this point everything referenced should be loaded, and // there will be a free slot if needed so we can translate // the input handles to the underlying handles ReplaceHandlesIn(inHandles, inSessions, neededObjects, neededSessions); } // Re-create the command using translated object and session handles byte[] commandBuf = CommandProcessor.CreateCommand(commandHeader.CommandCode, inHandles, inSessions, commandParmsNoHandles); if (!Tpm2._TssBehavior.Passthrough) { Debug.Assert(commandBuf.Length == inBuf.Length); } byte[] responseBuf; // TODO: Virtualize TPM2_GetCapability() for handle enumeration. // // Execute command on underlying TPM device. // If we get an ObjectMemory or SessionMemory error we try to make more space and try again // Note: If the TPM device throws an error above we let it propagate out. There should be no side // effects on TPM state that the TBS cares about. // ulong firstCtxSeqNum = 0; while (true) { Tpm._ExpectResponses(expectedResponses); TpmDevice.DispatchCommand(active, commandBuf, out responseBuf); TpmRc res = GetResultCode(responseBuf); if (res == TpmRc.Success || expectedResponses != null && expectedResponses.Contains(res)) { break; } if (res == TpmRc.ContextGap) { ulong seqNum = ShortenSessionContextGap(firstCtxSeqNum); if (seqNum == 0) { break; // Failed to handle CONTEXT_GAP error } if (firstCtxSeqNum == 0) { firstCtxSeqNum = seqNum; } //if (firstCtxSeqNum != 0) // Console.WriteLine("DispatchCommand: CONTEXT_GAP handled"); continue; } var slotType = SlotType.NoSlot; if (res == TpmRc.ObjectHandles || res == TpmRc.ObjectMemory) { slotType = SlotType.ObjectSlot; } else if (res == TpmRc.SessionHandles || res == TpmRc.SessionMemory) { slotType = SlotType.SessionSlot; } else { // Command failure not related to resources break; } if (!MakeSpace(slotType, neededEntities)) { // Failed to make an object slot in the TPM responseBuf = TpmErrorHelpers.BuildErrorResponseBuffer(TpmRc.Memory); break; } } // Parse the response from the TPM TpmSt responseTag; uint responseParamSize; TpmRc resultCode; TpmHandle[] responseHandles; SessionOut[] responseSessions; byte[] responseParmsNoHandles, responseParmsWithHandles; CommandProcessor.SplitResponse(responseBuf, command.HandleCountOut, out responseTag, out responseParamSize, out resultCode, out responseHandles, out responseSessions, out responseParmsNoHandles, out responseParmsWithHandles); // In case of an error there is no impact on the loaded sessions, but // we update the LRU values because the user will likely try again. if (resultCode != TpmRc.Success) { outBuf = responseBuf; UpdateLastUseCount(new[] { neededObjects, neededSessions }); return; } // Update TBS database with any newly created TPM objects ProcessUpdatedTpmState(caller, command, responseHandles, neededObjects); // And if there were any newly created objects use the new DB entries // to translate the handles ReplaceHandlesOut(responseHandles); outBuf = CommandProcessor.CreateResponse(resultCode, responseHandles, responseSessions, responseParmsNoHandles); Debug.Assert(outBuf.Length == responseBuf.Length); } // lock(this) }
internal override TpmHash GetPolicyDigest(TpmAlgId hashAlg) { return(GetNextAcePolicyDigest(hashAlg) .Extend(Marshaller.GetTpmRepresentation(TpmCc.PolicyPhysicalPresence))); }