/// <summary> /// Create a simple bound but unseeded session. /// </summary> public AuthSession StartAuthSessionEx( TpmHandle boundEntity, TpmSe sessionType, TpmAlgId authHash, SessionAttr initialialAttrs = SessionAttr.ContinueSession, SymDef symDef = null, int nonceCallerSize = 0) { byte[] nonceTpm; var EmptySalt = new byte[0]; if (nonceCallerSize == 0) { nonceCallerSize = CryptoLib.DigestSize(authHash); } AuthSession sess = StartAuthSession(TpmRh.Null, boundEntity, GetRandomBytes(nonceCallerSize), EmptySalt, sessionType, symDef ?? new SymDef(), authHash, out nonceTpm) + initialialAttrs; _InitializeSession(sess); return(sess); }
public static byte[] PssEncode(byte[] m, TpmAlgId hashAlg, int sLen, int emBits) { var emLen = (int)Math.Ceiling(1.0 * emBits / 8); int hLen = CryptoLib.DigestSize(hashAlg); // 1 - Ignore // 2 byte[] mHash = TpmHash.FromData(hashAlg, m); // 3 if (emLen < hLen + sLen + 2) { if (Tpm2._TssBehavior.Passthrough) { return(new byte[0]); } else { throw new Exception("Encoding error"); } } // 4 byte[] salt = Globs.GetRandomBytes(sLen); // 5 byte[] mPrime = Globs.Concatenate(new[] { Globs.ByteArray(8, 0), mHash, salt }); // 6 byte[] h = CryptoLib.HashData(hashAlg, mPrime); // 7 byte[] ps = Globs.GetZeroBytes(emLen - sLen - hLen - 2); // 8 byte[] db = Globs.Concatenate(new[] { ps, new byte[] { 0x01 }, salt }); // 9 byte[] dbMask = CryptoLib.MGF(h, emLen - hLen - 1, hashAlg); // 10 byte[] maskedDb = XorEngine.Xor(db, dbMask); // 11 int numZeroBits = 8 * emLen - emBits; byte mask = GetByteMask(numZeroBits); maskedDb[0] &= mask; // 12 byte[] em = Globs.Concatenate(new[] { maskedDb, h, new byte[] { 0xbc } }); // 13 return(em); }
/// <summary> /// De-envelope inner-wrapped duplication blob. /// TODO: Move this to TpmPublic and make it fully general /// </summary> /// <param name="exportedPrivate"></param> /// <param name="encAlg"></param> /// <param name="encKey"></param> /// <param name="nameAlg"></param> /// <param name="name"></param> /// <returns></returns> public static Sensitive SensitiveFromDuplicateBlob(TpmPrivate exportedPrivate, SymDefObject encAlg, byte[] encKey, TpmAlgId nameAlg, byte[] name) { byte[] dupBlob = exportedPrivate.buffer; byte[] sensNoLen; using (SymmCipher c = Create(encAlg, encKey)) { byte[] innerObject = c.Decrypt(dupBlob); byte[] innerIntegrity, sensitive; KDF.Split(innerObject, 16 + CryptoLib.DigestSize(nameAlg) * 8, out innerIntegrity, 8 * (innerObject.Length - CryptoLib.DigestSize(nameAlg) - 2), out sensitive); byte[] expectedInnerIntegrity = Marshaller.ToTpm2B(CryptoLib.HashData(nameAlg, sensitive, name)); if (!Globs.ArraysAreEqual(expectedInnerIntegrity, innerIntegrity)) { Globs.Throw("SensitiveFromDuplicateBlob: Bad inner integrity"); } sensNoLen = Marshaller.Tpm2BToBuffer(sensitive); } var sens = Marshaller.FromTpmRepresentation <Sensitive>(sensNoLen); return(sens); }
} // 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); }
/// <summary> /// Return a TpmHash of the specified algorithm with value set to 0x01010101... Note that this /// is not a 'real' hash value, but is the initialization value of some resettable PCR /// </summary> /// <param name="alg"></param> /// <returns></returns> public static TpmHash AllOnesHash(TpmAlgId alg) { int len = CryptoLib.DigestSize(alg); var bb = new byte[len]; for (int j = 0; j < bb.Length; j++) { bb[j] = 0xFF; } return(new TpmHash(alg, bb)); }
internal override void ToNet(Marshaller m) { if (CryptoLib.DigestSize(HashAlg) != HashData.Length) { if (!Tpm2._TssBehavior.Passthrough) { throw new Exception("Hash data length does not match the algorithm"); } } m.Put(HashAlg, "HashAlg"); m.Put(HashData, "HashData"); }
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"); }
/// <summary> /// Creates a *software* key. The key will be random (not created from /// a seed). The key can be used as the root of a software hierarchy that /// can be translated into a duplication blob ready for import into a TPM. /// Depending on the type of key, the software root key can be a parent for /// other root keys that can comprise a migration group. The caller should /// specify necessary key parameters in Public. /// /// Parameter keyData is used only with symmetric or HMAC keys. If non-null /// on entry, it contains the key bytes supplied by the caller, otherwise the /// key will be randomly generated. For asymmetric keys keyData must be null. /// /// Parameter authVal specifies the authorization value associated with the key. /// If it is null, then an random value will be used. /// </summary> /// <param name="pub"></param> /// <param name="authVal"></param> /// <param name="keyData"></param> /// <returns></returns> public static TssObject Create(TpmPublic pub, AuthValue authVal = null, byte[] keyData = null) { var newKey = new TssObject(); // Create a new key from the supplied parameters IPublicIdUnion publicId; var sensData = CreateSensitiveComposite(pub, ref keyData, out publicId); var nameSize = CryptoLib.DigestSize(pub.nameAlg); // Create the associated seed value byte[] seed = Globs.GetRandomBytes(nameSize); // Fill in the fields for the symmetric private-part of the asymmetric key var sens = new Sensitive(authVal ?? AuthValue.FromRandom(nameSize), seed, sensData); newKey.Sensitive = sens; newKey.Private = new TpmPrivate(sens.GetTpm2BRepresentation()); // fill in the public data newKey.Public = pub.Copy(); if (pub.type == TpmAlgId.Keyedhash || pub.type == TpmAlgId.Symcipher) { byte[] unique = null; if (pub.objectAttributes.HasFlag(ObjectAttr.Restricted | ObjectAttr.Decrypt)) { unique = CryptoLib.Hmac(pub.nameAlg, seed, keyData); } else { unique = CryptoLib.HashData(pub.nameAlg, seed, keyData); } newKey.Public.unique = pub.type == TpmAlgId.Keyedhash ? new Tpm2bDigestKeyedhash(unique) as IPublicIdUnion : new Tpm2bDigestSymcipher(unique); } else { newKey.Public.unique = publicId; } // And return the new key return(newKey); }
/// <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); }
// ReSharper disable once InconsistentNaming public static byte[] KDFa(TpmAlgId hmacHash, byte[] hmacKey, string label, byte[] contextU, byte[] contextV, uint numBitsRequired) { int bitsPerLoop = CryptoLib.DigestSize(hmacHash) * 8; long numLoops = (numBitsRequired + bitsPerLoop - 1) / bitsPerLoop; var kdfStream = new byte[numLoops * bitsPerLoop / 8]; for (int j = 0; j < numLoops; j++) { byte[] toHmac = Globs.Concatenate(new[] { Globs.HostToNet(j + 1), Encoding.UTF8.GetBytes(label), Globs.HostToNet((byte)0), contextU, contextV, Globs.HostToNet(numBitsRequired) }); byte[] fragment = CryptoLib.HmacData(hmacHash, hmacKey, toHmac); Array.Copy(fragment, 0, kdfStream, j * bitsPerLoop / 8, fragment.Length); } return(Globs.ShiftRight(kdfStream, (int)(bitsPerLoop * numLoops - numBitsRequired))); }
/// <summary> /// Creates a *software* root key. The key will be random (not created from a seed). The key can be used /// as the root of a software hierarchy that can be translated into a duplication blob ready for import into /// a TPM. Depending on the type of key, the software root key can be a parent for other root keys that can /// comprise a migration group. The caller should specify necessary key parameters in Public. /// </summary> /// <returns></returns> public static TssObject CreateStorageParent(TpmPublic keyParameters, AuthValue authVal) { var newKey = new TssObject(); // Create a new asymmetric key from the supplied parameters IPublicIdUnion publicId; ISensitiveCompositeUnion sensitiveData = CreateSensitiveComposite(keyParameters, out publicId); // fill in the public data newKey.publicPart = keyParameters.Copy(); newKey.publicPart.unique = publicId; // Create the associated symmetric key byte[] symmKey = Globs.GetRandomBytes(CryptoLib.DigestSize(keyParameters.nameAlg)); // Fill in the fields for the symmetric private-part of the asymmetric key var sens = new Sensitive(authVal.AuthVal, symmKey, sensitiveData); newKey.sensitivePart = sens; // And return the new key return(newKey); }
/// <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> /// 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)); }
/// <summary> /// PSS verify. Note: we expect the caller to do the hash. /// </summary> /// <param name="m"></param> /// <param name="em"></param> /// <param name="sLen"></param> /// <param name="emBits"></param> /// <param name="hashAlg"></param> /// <returns></returns> public static bool PssVerify(byte[] m, byte[] em, int sLen, int emBits, TpmAlgId hashAlg) { var emLen = (int)Math.Ceiling(1.0 * emBits / 8); int hLen = CryptoLib.DigestSize(hashAlg); // 1 - Skip // 2 byte[] mHash = TpmHash.FromData(hashAlg, m); // 3 if (emLen < hLen + sLen + 2) { return(false); } // 4 if (em[em.Length - 1] != 0xbc) { return(false); } // 5 byte[] maskedDB = Globs.CopyData(em, 0, emLen - hLen - 1); byte[] h = Globs.CopyData(em, emLen - hLen - 1, hLen); // 6 int numZeroBits = 8 * emLen - emBits; // First numZero bits is zero in mask byte mask = GetByteMask(numZeroBits); if ((maskedDB[0] & mask) != maskedDB[0]) { return(false); } // 7 byte[] dbMask = CryptoLib.MGF(h, emLen - hLen - 1, hashAlg); // 8 byte[] db = XorEngine.Xor(maskedDB, dbMask); // 9 int numZeroBits2 = 8 * emLen - emBits; byte mask2 = GetByteMask(numZeroBits2); db[0] &= mask2; // 10 for (int j = 0; j < emLen - hLen - sLen - 2; j++) { if (db[j] != 0) { return(false); } } if (db[emLen - hLen - sLen - 1 - 1] != 1) { return(false); } // 11 byte[] salt = Globs.CopyData(db, db.Length - sLen); // 12 byte[] mPrime = Globs.Concatenate(new[] { Globs.ByteArray(8, 0), mHash, salt }); // 13 byte[] hPrime = TpmHash.FromData(hashAlg, mPrime); // 14 bool match = Globs.ArraysAreEqual(h, hPrime); if (match == false) { return(false); } return(true); }
} // 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); }
public static bool OaepDecode(byte[] eMx, byte[] encodingParms, TpmAlgId hashAlg, out byte[] decoded) { decoded = new byte[0]; var em = new byte[eMx.Length + 1]; Array.Copy(eMx, 0, em, 1, eMx.Length); int hLen = CryptoLib.DigestSize(hashAlg); int k = em.Length; // a. byte[] lHash = CryptoLib.HashData(hashAlg, encodingParms); // b. byte y = em[0]; byte[] maskedSeed = Globs.CopyData(em, 1, hLen); byte[] maskedDB = Globs.CopyData(em, 1 + hLen); // c. byte[] seedMask = CryptoLib.MGF(maskedDB, hLen, hashAlg); // d. byte[] seed = XorEngine.Xor(maskedSeed, seedMask); // e. byte[] dbMask = CryptoLib.MGF(seed, k - hLen - 1, hashAlg); // f. byte[] db = XorEngine.Xor(maskedDB, dbMask); // g. byte[] lHashPrime = Globs.CopyData(db, 0, hLen); // Look for the zero.. int j; for (j = hLen; j < db.Length; j++) { if (db[j] == 0) { continue; } if (db[j] == 1) { break; } return(false); } if (j == db.Length - 1) { return(false); } byte[] m = Globs.CopyData(db, j + 1); if (y != 0) { return(false); } if (!Globs.ArraysAreEqual(lHash, lHashPrime)) { return(false); } decoded = m; return(true); }
/// <summary> /// EME-OAEP PKCS1.2, section 9.1.1.1. /// </summary> /// <param name="message"></param> /// <param name="encodingParameters"></param> /// <param name="hashAlg"></param> /// <param name="modulusNumBytes"></param> /// <returns></returns> public static byte[] OaepEncode(byte[] message, byte[] encodingParameters, TpmAlgId hashAlg, int modulusNumBytes) { int encodedMessageLength = modulusNumBytes - 1; int messageLength = message.Length; int hashLength = CryptoLib.DigestSize(hashAlg); // 1 (Step numbers from RSA labs spec.) // Ignore the ParametersLength limitation // 2 if (messageLength > encodedMessageLength - 2 * hashLength - 1) { if (Tpm2._TssBehavior.Passthrough) { return(new byte[0]); } else { throw new ArgumentException("input message too long"); } } int psLen = encodedMessageLength - messageLength - 2 * hashLength - 1; var ps = new byte[psLen]; // 3 (Not needed.) for (int j = 0; j < psLen; j++) { ps[j] = 0; } // 4 byte[] pHash = CryptoLib.HashData(hashAlg, encodingParameters); // 5 var db = new byte[hashLength + psLen + 1 + messageLength]; var one = new byte[1]; one[0] = 1; pHash.CopyTo(db, 0); ps.CopyTo(db, pHash.Length); one.CopyTo(db, pHash.Length + ps.Length); message.CopyTo(db, pHash.Length + ps.Length + 1); // 6 byte[] seed = Globs.GetRandomBytes(hashLength); // 7 byte[] dbMask = CryptoLib.MGF(seed, encodedMessageLength - hashLength, hashAlg); // 8 byte[] maskedDb = XorEngine.Xor(db, dbMask); // 9 byte[] seedMask = CryptoLib.MGF(maskedDb, hashLength, hashAlg); // 10 byte[] maskedSeed = XorEngine.Xor(seed, seedMask); //11 var encodedMessage = new byte[maskedSeed.Length + maskedDb.Length]; maskedSeed.CopyTo(encodedMessage, 0); maskedDb.CopyTo(encodedMessage, maskedSeed.Length); // 12 return(encodedMessage); }
/// <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> /// Return the length of the output of the hash function in bytes /// </summary> /// <param name="hashAlg"></param> /// <returns></returns> public static ushort DigestSize(TpmAlgId hashAlg) { return((ushort)CryptoLib.DigestSize(hashAlg)); }
CreateSensitiveComposite(TpmPublic pub, ref byte[] keyData, out IPublicIdUnion publicId) { ISensitiveCompositeUnion newSens = null; publicId = null; if (pub.type == TpmAlgId.Rsa) { if (keyData != null) { Globs.Throw <ArgumentException>("Cannot specify key data for an RSA key"); return(null); } var newKeyPair = new RawRsa((pub.parameters as RsaParms).keyBits); // Put the key bits into the required structure envelopes newSens = new Tpm2bPrivateKeyRsa(newKeyPair.Private); publicId = new Tpm2bPublicKeyRsa(newKeyPair.Public); } else if (pub.type == TpmAlgId.Symcipher) { var symDef = (SymDefObject)pub.parameters; if (symDef.Algorithm != TpmAlgId.Aes) { Globs.Throw <ArgumentException>("Unsupported symmetric algorithm"); return(null); } int keySize = (symDef.KeyBits + 7) / 8; if (keyData == null) { keyData = Globs.GetRandomBytes(keySize); } else if (keyData.Length != keySize) { keyData = Globs.CopyData(keyData); } else { Globs.Throw <ArgumentException>("Wrong symmetric key length"); return(null); } newSens = new Tpm2bSymKey(keyData); } else if (pub.type == TpmAlgId.Keyedhash) { var scheme = (pub.parameters as KeyedhashParms).scheme; TpmAlgId hashAlg = scheme is SchemeHash ? (scheme as SchemeHash).hashAlg : scheme is SchemeXor ? (scheme as SchemeXor).hashAlg : pub.nameAlg; var digestSize = CryptoLib.DigestSize(hashAlg); if (keyData == null) { keyData = Globs.GetRandomBytes(digestSize); } else if (keyData.Length <= CryptoLib.BlockSize(hashAlg)) { keyData = Globs.CopyData(keyData); } else { Globs.Throw <ArgumentException>("HMAC key is too big"); return(null); } newSens = new Tpm2bSensitiveData(keyData); } else { Globs.Throw <ArgumentException>("Unsupported key type"); } return(newSens); }
/// <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> /// 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)); }