/// <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); }
/// <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); }
// ReSharper disable once InconsistentNaming public static byte[] KDFa(TpmAlgId hmacHash, byte[] hmacKey, string label, byte[] contextU, byte[] contextV, int 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.Hmac(hmacHash, hmacKey, toHmac); Array.Copy(fragment, 0, kdfStream, j * bitsPerLoop / 8, fragment.Length); } return(Globs.ShiftRight(kdfStream, (int)(bitsPerLoop * numLoops - numBitsRequired))); }
/// <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)); }