// This file was extracted from the TCG Published // Trusted Platform Module Library // Part 4: Supporting Routines // Family "2.0" // Level 00 Revision 01.16 // October 30, 2014 #include "InternalRoutines.h" #include "Object_spt_fp.h" #include "Platform.h" // // // // Local Functions // // EqualCryptSet() // // Check if the crypto sets in two public areas are equal // // Error Returns Meaning // // TPM_RC_ASYMMETRIC mismatched parameters // TPM_RC_HASH mismatched name algorithm // TPM_RC_TYPE mismatched type // static TPM_RC EqualCryptSet( TPMT_PUBLIC *publicArea1, // IN: public area 1 TPMT_PUBLIC *publicArea2 // IN: public area 2 ) { UINT16 size1; UINT16 size2; BYTE params1[sizeof(TPMU_PUBLIC_PARMS)]; BYTE params2[sizeof(TPMU_PUBLIC_PARMS)]; BYTE *buffer; INT32 bufferSize; // Compare name hash if(publicArea1->nameAlg != publicArea2->nameAlg) return TPM_RC_HASH; // Compare algorithm if(publicArea1->type != publicArea2->type) return TPM_RC_TYPE; // TPMU_PUBLIC_PARMS field should be identical buffer = params1; bufferSize = sizeof(TPMU_PUBLIC_PARMS); size1 = TPMU_PUBLIC_PARMS_Marshal(&publicArea1->parameters, &buffer, &bufferSize, publicArea1->type); buffer = params2; bufferSize = sizeof(TPMU_PUBLIC_PARMS); size2 = TPMU_PUBLIC_PARMS_Marshal(&publicArea2->parameters, &buffer, &bufferSize, publicArea2->type); if(size1 != size2 || !MemoryEqual(params1, params2, size1)) return TPM_RC_ASYMMETRIC; return TPM_RC_SUCCESS; } // // // GetIV2BSize() // // Get the size of TPM2B_IV in canonical form that will be append to the start of the sensitive data. It // includes both size of size field and size of iv data // // Return Value Meaning // static UINT16 GetIV2BSize( TPM_HANDLE protectorHandle // IN: the protector handle ) { OBJECT *protector = NULL; // Pointer to the protector object TPM_ALG_ID symAlg; // UINT16 keyBits; // Determine the symmetric algorithm and size of key if(protectorHandle == TPM_RH_NULL) { // Use the context encryption algorithm and key size symAlg = CONTEXT_ENCRYPT_ALG; keyBits = CONTEXT_ENCRYPT_KEY_BITS; } else { protector = ObjectGet(protectorHandle); symAlg = protector->publicArea.parameters.asymDetail.symmetric.algorithm; keyBits= protector->publicArea.parameters.asymDetail.symmetric.keyBits.sym; } // The IV size is a UINT16 size field plus the block size of the symmetric // algorithm return sizeof(UINT16) + CryptGetSymmetricBlockSize(symAlg, keyBits); } // // // ComputeProtectionKeyParms() // // This function retrieves the symmetric protection key parameters for the sensitive data The parameters // retrieved from this function include encryption algorithm, key size in bit, and a TPM2B_SYM_KEY // containing the key material as well as the key size in bytes This function is used for any action that // requires encrypting or decrypting of the sensitive area of an object or a credential blob // static void ComputeProtectionKeyParms( TPM_HANDLE protectorHandle, // IN: the protector handle TPM_ALG_ID hashAlg, // IN: hash algorithm for KDFa TPM2B_NAME *name, // IN: name of the object TPM2B_SEED *seedIn, // IN: optional seed for duplication blob. // For non duplication blob, this // parameter should be NULL TPM_ALG_ID *symAlg, // OUT: the symmetric algorithm UINT16 *keyBits, // OUT: the symmetric key size in bits TPM2B_SYM_KEY *symKey // OUT: the symmetric key ) { TPM2B_SEED *seed = NULL; OBJECT *protector = NULL; // Pointer to the protector // Determine the algorithms for the KDF and the encryption/decryption // For TPM_RH_NULL, using context settings if(protectorHandle == TPM_RH_NULL) { // Use the context encryption algorithm and key size *symAlg = CONTEXT_ENCRYPT_ALG; symKey->t.size = CONTEXT_ENCRYPT_KEY_BYTES; *keyBits = CONTEXT_ENCRYPT_KEY_BITS; } else { TPMT_SYM_DEF_OBJECT *symDef; protector = ObjectGet(protectorHandle); symDef = &protector->publicArea.parameters.asymDetail.symmetric; *symAlg = symDef->algorithm; *keyBits= symDef->keyBits.sym; symKey->t.size = (*keyBits + 7) / 8; } // Get seed for KDF seed = GetSeedForKDF(protectorHandle, seedIn); // KDFa to generate symmetric key and IV value KDFa(hashAlg, (TPM2B *)seed, "STORAGE", (TPM2B *)name, NULL, symKey->t.size * 8, symKey->t.buffer, NULL); return; } // // // ComputeOuterIntegrity() // // The sensitive area parameter is a buffer that holds a space for the integrity value and the marshaled // sensitive area. The caller should skip over the area set aside for the integrity value and compute the hash // of the remainder of the object. The size field of sensitive is in unmarshaled form and the sensitive area // contents is an array of bytes. // static void ComputeOuterIntegrity( TPM2B_NAME *name, // IN: the name of the object TPM_HANDLE protectorHandle, // IN: The handle of the object that // provides protection. For object, it // is parent handle. For credential, it // is the handle of encrypt object. For // a Temporary Object, it is TPM_RH_NULL TPMI_ALG_HASH hashAlg, // IN: algorithm to use for integrity TPM2B_SEED *seedIn, // IN: an external seed may be provided for // duplication blob. For non duplication // blob, this parameter should be NULL UINT32 sensitiveSize, // IN: size of the marshaled sensitive data BYTE *sensitiveData, // IN: sensitive area TPM2B_DIGEST *integrity // OUT: integrity ) { HMAC_STATE hmacState; TPM2B_DIGEST hmacKey; TPM2B_SEED *seed = NULL; // Get seed for KDF seed = GetSeedForKDF(protectorHandle, seedIn); // Determine the HMAC key bits hmacKey.t.size = CryptGetHashDigestSize(hashAlg); // KDFa to generate HMAC key KDFa(hashAlg, (TPM2B *)seed, "INTEGRITY", NULL, NULL, hmacKey.t.size * 8, hmacKey.t.buffer, NULL); // Start HMAC and get the size of the digest which will become the integrity integrity->t.size = CryptStartHMAC2B(hashAlg, &hmacKey.b, &hmacState); // Adding the marshaled sensitive area to the integrity value CryptUpdateDigest(&hmacState, sensitiveSize, sensitiveData); // Adding name CryptUpdateDigest2B(&hmacState, (TPM2B *)name); // Compute HMAC CryptCompleteHMAC2B(&hmacState, &integrity->b); return; } // // // ComputeInnerIntegrity() // // This function computes the integrity of an inner wrap // static void ComputeInnerIntegrity( TPM_ALG_ID hashAlg, // IN: hash algorithm for inner wrap TPM2B_NAME *name, // IN: the name of the object UINT16 dataSize, // IN: the size of sensitive data BYTE *sensitiveData, // IN: sensitive data TPM2B_DIGEST *integrity // OUT: inner integrity ) { HASH_STATE hashState; // Start hash and get the size of the digest which will become the integrity integrity->t.size = CryptStartHash(hashAlg, &hashState); // Adding the marshaled sensitive area to the integrity value CryptUpdateDigest(&hashState, dataSize, sensitiveData); // Adding name CryptUpdateDigest2B(&hashState, &name->b); // Compute hash CryptCompleteHash2B(&hashState, &integrity->b); return; } // // // ProduceInnerIntegrity() // // This function produces an inner integrity for regular private, credential or duplication blob It requires the // sensitive data being marshaled to the innerBuffer, with the leading bytes reserved for integrity hash. It // assume the sensitive data starts at address (innerBuffer + integrity size). This function integrity at the // beginning of the inner buffer It returns the total size of buffer with the inner wrap // static UINT16 ProduceInnerIntegrity( TPM2B_NAME *name, // IN: the name of the object TPM_ALG_ID hashAlg, // IN: hash algorithm for inner wrap UINT16 dataSize, // IN: the size of sensitive data, excluding the // leading integrity buffer size BYTE *innerBuffer // IN/OUT: inner buffer with sensitive data in // it. At input, the leading bytes of this // buffer is reserved for integrity ) { BYTE *sensitiveData; // pointer to the sensitive data TPM2B_DIGEST integrity; UINT16 integritySize; BYTE *buffer; // Auxiliary buffer pointer INT32 bufferSize; // sensitiveData points to the beginning of sensitive data in innerBuffer integritySize = sizeof(UINT16) + CryptGetHashDigestSize(hashAlg); sensitiveData = innerBuffer + integritySize; ComputeInnerIntegrity(hashAlg, name, dataSize, sensitiveData, &integrity); // Add integrity at the beginning of inner buffer buffer = innerBuffer; bufferSize = sizeof(TPM2B_DIGEST); TPM2B_DIGEST_Marshal(&integrity, &buffer, &bufferSize); return dataSize + integritySize; } // // // CheckInnerIntegrity() // // This function check integrity of inner blob // // Error Returns Meaning // // TPM_RC_INTEGRITY if the outer blob integrity is bad // unmarshal errors unmarshal errors while unmarshaling integrity // static TPM_RC CheckInnerIntegrity( TPM2B_NAME *name, // IN: the name of the object TPM_ALG_ID hashAlg, // IN: hash algorithm for inner wrap UINT16 dataSize, // IN: the size of sensitive data, including the // leading integrity buffer size BYTE *innerBuffer // IN/OUT: inner buffer with sensitive data in // it ) { TPM_RC result; TPM2B_DIGEST integrity; TPM2B_DIGEST integrityToCompare; BYTE *buffer; // Auxiliary buffer pointer INT32 size; // Unmarshal integrity buffer = innerBuffer; size = (INT32) dataSize; result = TPM2B_DIGEST_Unmarshal(&integrity, &buffer, &size); if(result == TPM_RC_SUCCESS) { // Compute integrity to compare ComputeInnerIntegrity(hashAlg, name, (UINT16) size, buffer, &integrityToCompare); // Compare outer blob integrity if(!Memory2BEqual(&integrity.b, &integrityToCompare.b)) result = TPM_RC_INTEGRITY; } return result; } // // // Public Functions // // AreAttributesForParent() // // This function is called by create, load, and import functions. // // Return Value Meaning // // TRUE properties are those of a parent // FALSE properties are not those of a parent // BOOL AreAttributesForParent( OBJECT *parentObject // IN: parent handle ) { // This function is only called when a parent is needed. Any // time a "parent" is used, it must be authorized. When // the authorization is checked, both the public and sensitive // areas must be loaded. Just make sure... pAssert(parentObject->attributes.publicOnly == CLEAR); if(ObjectDataIsStorage(&parentObject->publicArea)) return TRUE; else return FALSE; } // // // SchemeChecks() // // This function validates the schemes in the public area of an object. This function is called by // TPM2_LoadExternal() and PublicAttributesValidation(). // // Error Returns Meaning // // TPM_RC_ASYMMETRIC non-duplicable storage key and its parent have different public // parameters // TPM_RC_ATTRIBUTES attempt to inject sensitive data for an asymmetric key; or attempt to // create a symmetric cipher key that is not a decryption key // TPM_RC_HASH non-duplicable storage key and its parent have different name // algorithm // TPM_RC_KDF incorrect KDF specified for decrypting keyed hash object // TPM_RC_KEY invalid key size values in an asymmetric key public area // TPM_RC_SCHEME inconsistent attributes decrypt, sign, restricted and key's scheme ID; // or hash algorithm is inconsistent with the scheme ID for keyed hash // object // TPM_RC_SYMMETRIC a storage key with no symmetric algorithm specified; or non-storage // key with symmetric algorithm different from TPM_ALG_NULL // TPM_RC_TYPE unexpected object type; or non-duplicable storage key and its parent // have different types // TPM_RC SchemeChecks( BOOL load, // IN: TRUE if load checks, FALSE if // TPM2_Create() TPMI_DH_OBJECT parentHandle, // IN: input parent handle TPMT_PUBLIC *publicArea // IN: public area of the object ) { // Checks for an asymmetric key if(CryptIsAsymAlgorithm(publicArea->type)) { TPMT_ASYM_SCHEME *keyScheme; keyScheme = &publicArea->parameters.asymDetail.scheme; // An asymmetric key can't be injected // This is only checked when creating an object if(!load && (publicArea->objectAttributes.sensitiveDataOrigin == CLEAR)) return TPM_RC_ATTRIBUTES; if(load && !CryptAreKeySizesConsistent(publicArea)) return TPM_RC_KEY; // Keys that are both signing and decrypting must have TPM_ALG_NULL // for scheme if( publicArea->objectAttributes.sign == SET && publicArea->objectAttributes.decrypt == SET && keyScheme->scheme != TPM_ALG_NULL) return TPM_RC_SCHEME; // A restrict sign key must have a non-NULL scheme if( publicArea->objectAttributes.restricted == SET && publicArea->objectAttributes.sign == SET && keyScheme->scheme == TPM_ALG_NULL) return TPM_RC_SCHEME; // Keys must have a valid sign or decrypt scheme, or a TPM_ALG_NULL // scheme // NOTE: The unmarshaling for a public area will unmarshal based on the // object type. If the type is an RSA key, then only RSA schemes will be // allowed because a TPMI_ALG_RSA_SCHEME will be unmarshaled and it // consists only of those algorithms that are allowed with an RSA key. // This means that there is no need to again make sure that the algorithm // is compatible with the object type. if( keyScheme->scheme != TPM_ALG_NULL && ( ( publicArea->objectAttributes.sign == SET && !CryptIsSignScheme(keyScheme->scheme) ) || ( publicArea->objectAttributes.decrypt == SET && !CryptIsDecryptScheme(keyScheme->scheme) ) ) ) return TPM_RC_SCHEME; // Special checks for an ECC key #ifdef TPM_ALG_ECC if(publicArea->type == TPM_ALG_ECC) { TPM_ECC_CURVE curveID = publicArea->parameters.eccDetail.curveID; const TPMT_ECC_SCHEME *curveScheme = CryptGetCurveSignScheme(curveID); // The curveId must be valid or the unmarshaling is busted. pAssert(curveScheme != NULL); // If the curveID requires a specific scheme, then the key must select // the same scheme if(curveScheme->scheme != TPM_ALG_NULL) { if(keyScheme->scheme != curveScheme->scheme) return TPM_RC_SCHEME; // The scheme can allow any hash, or not... if( curveScheme->details.anySig.hashAlg != TPM_ALG_NULL && ( keyScheme->details.anySig.hashAlg != curveScheme->details.anySig.hashAlg ) ) return TPM_RC_SCHEME; } // For now, the KDF must be TPM_ALG_NULL if(publicArea->parameters.eccDetail.kdf.scheme != TPM_ALG_NULL) return TPM_RC_KDF; } #endif // Checks for a storage key (restricted + decryption) if( publicArea->objectAttributes.restricted == SET && publicArea->objectAttributes.decrypt == SET) { // A storage key must have a valid protection key if( publicArea->parameters.asymDetail.symmetric.algorithm == TPM_ALG_NULL) return TPM_RC_SYMMETRIC; // A storage key must have a null scheme if(publicArea->parameters.asymDetail.scheme.scheme != TPM_ALG_NULL) return TPM_RC_SCHEME; // A storage key must match its parent algorithms unless // it is duplicable or a primary (including Temporary Primary Objects) if( HandleGetType(parentHandle) != TPM_HT_PERMANENT && publicArea->objectAttributes.fixedParent == SET ) { // If the object to be created is a storage key, and is fixedParent, // its crypto set has to match its parent's crypto set. TPM_RC_TYPE, // TPM_RC_HASH or TPM_RC_ASYMMETRIC may be returned at this point return EqualCryptSet(publicArea, &(ObjectGet(parentHandle)->publicArea)); } } else { // Non-storage keys must have TPM_ALG_NULL for the symmetric algorithm if( publicArea->parameters.asymDetail.symmetric.algorithm != TPM_ALG_NULL) return TPM_RC_SYMMETRIC; }// End of asymmetric decryption key checks } // End of asymmetric checks // Check for bit attributes else if(publicArea->type == TPM_ALG_KEYEDHASH) { TPMT_KEYEDHASH_SCHEME *scheme = &publicArea->parameters.keyedHashDetail.scheme; // If both sign and decrypt are set the scheme must be TPM_ALG_NULL // and the scheme selected when the key is used. // If neither sign nor decrypt is set, the scheme must be TPM_ALG_NULL // because this is a data object. if( publicArea->objectAttributes.sign == publicArea->objectAttributes.decrypt) { if(scheme->scheme != TPM_ALG_NULL) return TPM_RC_SCHEME; return TPM_RC_SUCCESS; } // If this is a decryption key, make sure that is is XOR and that there // is a KDF else if(publicArea->objectAttributes.decrypt) { if( scheme->scheme != TPM_ALG_XOR || scheme->details.xor_.hashAlg == TPM_ALG_NULL) return TPM_RC_SCHEME; if(scheme->details.xor_.kdf == TPM_ALG_NULL) return TPM_RC_KDF; return TPM_RC_SUCCESS; } // only supported signing scheme for keyedHash object is HMAC if( scheme->scheme != TPM_ALG_HMAC || scheme->details.hmac.hashAlg == TPM_ALG_NULL) return TPM_RC_SCHEME; // end of the checks for keyedHash return TPM_RC_SUCCESS; } else if (publicArea->type == TPM_ALG_SYMCIPHER) { // Must be a decrypting key and may not be a signing key if( publicArea->objectAttributes.decrypt == CLEAR || publicArea->objectAttributes.sign == SET ) return TPM_RC_ATTRIBUTES; } else return TPM_RC_TYPE; return TPM_RC_SUCCESS; } // // // PublicAttributesValidation() // // This function validates the values in the public area of an object. This function is called by // TPM2_Create(), TPM2_Load(), and TPM2_CreatePrimary() // // Error Returns Meaning // // TPM_RC_ASYMMETRIC non-duplicable storage key and its parent have different public // parameters // TPM_RC_ATTRIBUTES fixedTPM, fixedParent, or encryptedDuplication attributes are // inconsistent between themselves or with those of the parent object; // inconsistent restricted, decrypt and sign attributes; attempt to inject // sensitive data for an asymmetric key; attempt to create a symmetric // cipher key that is not a decryption key // TPM_RC_HASH non-duplicable storage key and its parent have different name // algorithm // TPM_RC_KDF incorrect KDF specified for decrypting keyed hash object // TPM_RC_KEY invalid key size values in an asymmetric key public area // TPM_RC_SCHEME inconsistent attributes decrypt, sign, restricted and key's scheme ID; // or hash algorithm is inconsistent with the scheme ID for keyed hash // object // TPM_RC_SIZE authPolicy size does not match digest size of the name algorithm in // publicArea // TPM_RC_SYMMETRIC a storage key with no symmetric algorithm specified; or non-storage // key with symmetric algorithm different from TPM_ALG_NULL // TPM_RC_TYPE unexpected object type; or non-duplicable storage key and its parent // have different types // TPM_RC PublicAttributesValidation( BOOL load, // IN: TRUE if load checks, FALSE if // TPM2_Create() TPMI_DH_OBJECT parentHandle, // IN: input parent handle TPMT_PUBLIC *publicArea // IN: public area of the object ) { OBJECT *parentObject = NULL; if(HandleGetType(parentHandle) != TPM_HT_PERMANENT) parentObject = ObjectGet(parentHandle); // Check authPolicy digest consistency if( publicArea->authPolicy.t.size != 0 && ( publicArea->authPolicy.t.size != CryptGetHashDigestSize(publicArea->nameAlg) ) ) return TPM_RC_SIZE; // If the parent is fixedTPM (including a Primary Object) the object must have // the same value for fixedTPM and fixedParent if( parentObject == NULL || parentObject->publicArea.objectAttributes.fixedTPM == SET) { if( publicArea->objectAttributes.fixedParent != publicArea->objectAttributes.fixedTPM ) return TPM_RC_ATTRIBUTES; } else // The parent is not fixedTPM so the object can't be fixedTPM if(publicArea->objectAttributes.fixedTPM == SET) return TPM_RC_ATTRIBUTES; // A restricted object cannot be both sign and decrypt and it can't be neither // sign nor decrypt if ( publicArea->objectAttributes.restricted == SET && ( publicArea->objectAttributes.decrypt == publicArea->objectAttributes.sign) ) return TPM_RC_ATTRIBUTES; // A fixedTPM object can not have encryptedDuplication bit SET if( publicArea->objectAttributes.fixedTPM == SET && publicArea->objectAttributes.encryptedDuplication == SET) return TPM_RC_ATTRIBUTES; // If a parent object has fixedTPM CLEAR, the child must have the // same encryptedDuplication value as its parent. // Primary objects are considered to have a fixedTPM parent (the seeds). if( ( parentObject != NULL && parentObject->publicArea.objectAttributes.fixedTPM == CLEAR) // Get here if parent is not fixed TPM && ( publicArea->objectAttributes.encryptedDuplication != parentObject->publicArea.objectAttributes.encryptedDuplication ) ) return TPM_RC_ATTRIBUTES; return SchemeChecks(load, parentHandle, publicArea); } // // // FillInCreationData() // // Fill in creation data for an object. // void FillInCreationData( TPMI_DH_OBJECT parentHandle, // IN: handle of parent TPMI_ALG_HASH nameHashAlg, // IN: name hash algorithm TPML_PCR_SELECTION *creationPCR, // IN: PCR selection TPM2B_DATA *outsideData, // IN: outside data TPM2B_CREATION_DATA *outCreation, // OUT: creation data for output TPM2B_DIGEST *creationDigest // OUT: creation digest // ) { BYTE creationBuffer[sizeof(TPMS_CREATION_DATA)]; BYTE *buffer; INT32 bufferSize; HASH_STATE hashState; // Fill in TPMS_CREATION_DATA in outCreation // Compute PCR digest PCRComputeCurrentDigest(nameHashAlg, creationPCR, &outCreation->t.creationData.pcrDigest); // Put back PCR selection list outCreation->t.creationData.pcrSelect = *creationPCR; // Get locality outCreation->t.creationData.locality = LocalityGetAttributes(_plat__LocalityGet()); outCreation->t.creationData.parentNameAlg = TPM_ALG_NULL; // If the parent is is either a primary seed or TPM_ALG_NULL, then the Name // and QN of the parent are the parent's handle. if(HandleGetType(parentHandle) == TPM_HT_PERMANENT) { BYTE *buffer = &outCreation->t.creationData.parentName.t.name[0]; INT32 bufferSize = sizeof(TPM_HANDLE); outCreation->t.creationData.parentName.t.size = TPM_HANDLE_Marshal(&parentHandle, &buffer, &bufferSize); // Parent qualified name of a Temporary Object is the same as parent's // name MemoryCopy2B(&outCreation->t.creationData.parentQualifiedName.b, &outCreation->t.creationData.parentName.b, sizeof(outCreation->t.creationData.parentQualifiedName.t.name)); } else // Regular object { OBJECT *parentObject = ObjectGet(parentHandle); // Set name algorithm outCreation->t.creationData.parentNameAlg = parentObject->publicArea.nameAlg; // Copy parent name outCreation->t.creationData.parentName = parentObject->name; // Copy parent qualified name outCreation->t.creationData.parentQualifiedName = parentObject->qualifiedName; } // Copy outside information outCreation->t.creationData.outsideInfo = *outsideData; // Marshal creation data to canonical form buffer = creationBuffer; bufferSize = sizeof(TPMS_CREATION_DATA); outCreation->t.size = TPMS_CREATION_DATA_Marshal(&outCreation->t.creationData, &buffer, &bufferSize); // Compute hash for creation field in public template creationDigest->t.size = CryptStartHash(nameHashAlg, &hashState); CryptUpdateDigest(&hashState, outCreation->t.size, creationBuffer); CryptCompleteHash2B(&hashState, &creationDigest->b); return; } // GetSeedForKDF() // // Get a seed for KDF. The KDF for encryption and HMAC key use the same seed. It returns a pointer to // the seed // TPM2B_SEED* GetSeedForKDF( TPM_HANDLE protectorHandle, // IN: the protector handle TPM2B_SEED *seedIn // IN: the optional input seed ) { OBJECT *protector = NULL; // Pointer to the protector // Get seed for encryption key. Use input seed if provided. // Otherwise, using protector object's seedValue. TPM_RH_NULL is the only // exception that we may not have a loaded object as protector. In such a // case, use nullProof as seed. if(seedIn != NULL) { return seedIn; } else { if(protectorHandle == TPM_RH_NULL) { return (TPM2B_SEED *) &gr.nullProof; } else { protector = ObjectGet(protectorHandle); return (TPM2B_SEED *) &protector->sensitive.seedValue; } } } // // // ProduceOuterWrap() // // This function produce outer wrap for a buffer containing the sensitive data. It requires the sensitive data // being marshaled to the outerBuffer, with the leading bytes reserved for integrity hash. If iv is used, iv // space should be reserved at the beginning of the buffer. It assumes the sensitive data starts at address // (outerBuffer + integrity size {+ iv size}). This function performs: // a) Add IV before sensitive area if required // b) encrypt sensitive data, if iv is required, encrypt by iv. otherwise, encrypted by a NULL iv // c) add HMAC integrity at the beginning of the buffer It returns the total size of blob with outer wrap // UINT16 ProduceOuterWrap( TPM_HANDLE protector, // IN: The handle of the object that provides // protection. For object, it is parent // handle. For credential, it is the handle // of encrypt object. TPM2B_NAME *name, // IN: the name of the object TPM_ALG_ID hashAlg, // IN: hash algorithm for outer wrap TPM2B_SEED *seed, // IN: an external seed may be provided for // duplication blob. For non duplication // blob, this parameter should be NULL BOOL useIV, // IN: indicate if an IV is used UINT16 dataSize, // IN: the size of sensitive data, excluding the // leading integrity buffer size or the // optional iv size BYTE *outerBuffer // IN/OUT: outer buffer with sensitive data in // it ) { TPM_ALG_ID symAlg; UINT16 keyBits; TPM2B_SYM_KEY symKey; TPM2B_IV ivRNG; // IV from RNG TPM2B_IV *iv = NULL; UINT16 ivSize = 0; // size of iv area, including the size field BYTE *sensitiveData; // pointer to the sensitive data TPM2B_DIGEST integrity; UINT16 integritySize; BYTE *buffer; // Auxiliary buffer pointer INT32 bufferSize; // Compute the beginning of sensitive data. The outer integrity should // always exist if this function function is called to make an outer wrap integritySize = sizeof(UINT16) + CryptGetHashDigestSize(hashAlg); sensitiveData = outerBuffer + integritySize; // If iv is used, adjust the pointer of sensitive data and add iv before it if(useIV) { ivSize = GetIV2BSize(protector); // Generate IV from RNG. The iv data size should be the total IV area // size minus the size of size field ivRNG.t.size = ivSize - sizeof(UINT16); CryptGenerateRandom(ivRNG.t.size, ivRNG.t.buffer); // Marshal IV to buffer buffer = sensitiveData; bufferSize = sizeof(TPM2B_IV); TPM2B_IV_Marshal(&ivRNG, &buffer, &bufferSize); // adjust sensitive data starting after IV area sensitiveData += ivSize; // Use iv for encryption iv = &ivRNG; } // Compute symmetric key parameters for outer buffer encryption ComputeProtectionKeyParms(protector, hashAlg, name, seed, &symAlg, &keyBits, &symKey); // Encrypt inner buffer in place CryptSymmetricEncrypt(sensitiveData, symAlg, keyBits, TPM_ALG_CFB, symKey.t.buffer, iv, dataSize, sensitiveData); // Compute outer integrity. Integrity computation includes the optional IV // area ComputeOuterIntegrity(name, protector, hashAlg, seed, dataSize + ivSize, outerBuffer + integritySize, &integrity); // Add integrity at the beginning of outer buffer buffer = outerBuffer; bufferSize = sizeof(TPM2B_DIGEST); TPM2B_DIGEST_Marshal(&integrity, &buffer, &bufferSize); // return the total size in outer wrap return dataSize + integritySize + ivSize; } // // // // UnwrapOuter() // // This function remove the outer wrap of a blob containing sensitive data This function performs: // a) check integrity of outer blob // b) decrypt outer blob // // Error Returns Meaning // // TPM_RC_INSUFFICIENT error during sensitive data unmarshaling // TPM_RC_INTEGRITY sensitive data integrity is broken // TPM_RC_SIZE error during sensitive data unmarshaling // TPM_RC_VALUE IV size for CFB does not match the encryption algorithm block size // TPM_RC UnwrapOuter( TPM_HANDLE protector, // IN: The handle of the object that provides // protection. For object, it is parent // handle. For credential, it is the handle // of encrypt object. TPM2B_NAME *name, // IN: the name of the object TPM_ALG_ID hashAlg, // IN: hash algorithm for outer wrap TPM2B_SEED *seed, // IN: an external seed may be provided for // duplication blob. For non duplication // blob, this parameter should be NULL. BOOL useIV, // IN: indicates if an IV is used UINT16 dataSize, // IN: size of sensitive data in outerBuffer, // including the leading integrity buffer // size, and an optional iv area BYTE *outerBuffer // IN/OUT: sensitive data ) { TPM_RC result; TPM_ALG_ID symAlg = TPM_ALG_NULL; TPM2B_SYM_KEY symKey; UINT16 keyBits = 0; TPM2B_IV ivIn; // input IV retrieved from input buffer TPM2B_IV *iv = NULL; BYTE *sensitiveData; // pointer to the sensitive data TPM2B_DIGEST integrityToCompare; TPM2B_DIGEST integrity; INT32 size; // Unmarshal integrity sensitiveData = outerBuffer; size = (INT32) dataSize; result = TPM2B_DIGEST_Unmarshal(&integrity, &sensitiveData, &size); if(result == TPM_RC_SUCCESS) { // Compute integrity to compare ComputeOuterIntegrity(name, protector, hashAlg, seed, (UINT16) size, sensitiveData, &integrityToCompare); // Compare outer blob integrity if(!Memory2BEqual(&integrity.b, &integrityToCompare.b)) return TPM_RC_INTEGRITY; // Get the symmetric algorithm parameters used for encryption ComputeProtectionKeyParms(protector, hashAlg, name, seed, &symAlg, &keyBits, &symKey); // Retrieve IV if it is used if(useIV) { result = TPM2B_IV_Unmarshal(&ivIn, &sensitiveData, &size); if(result == TPM_RC_SUCCESS) { // The input iv size for CFB must match the encryption algorithm // block size if(ivIn.t.size != CryptGetSymmetricBlockSize(symAlg, keyBits)) result = TPM_RC_VALUE; else iv = &ivIn; } } } // If no errors, decrypt private in place if(result == TPM_RC_SUCCESS) CryptSymmetricDecrypt(sensitiveData, symAlg, keyBits, TPM_ALG_CFB, symKey.t.buffer, iv, (UINT16) size, sensitiveData); return result; } // // // SensitiveToPrivate() // // This function prepare the private blob for off the chip storage The operations in this function: // a) marshal TPM2B_SENSITIVE structure into the buffer of TPM2B_PRIVATE // b) apply encryption to the sensitive area. // c) apply outer integrity computation. // void SensitiveToPrivate( TPMT_SENSITIVE *sensitive, // IN: sensitive structure TPM2B_NAME *name, // IN: the name of the object TPM_HANDLE parentHandle, // IN: The parent's handle TPM_ALG_ID nameAlg, // IN: hash algorithm in public area. This // parameter is used when parentHandle is // NULL, in which case the object is // temporary. TPM2B_PRIVATE *outPrivate // OUT: output private structure ) { BYTE *buffer; // Auxiliary buffer pointer INT32 bufferSize; BYTE *sensitiveData; // pointer to the sensitive data UINT16 dataSize; // data blob size TPMI_ALG_HASH hashAlg; // hash algorithm for integrity UINT16 integritySize; UINT16 ivSize; pAssert(name != NULL && name->t.size != 0); // Find the hash algorithm for integrity computation if(parentHandle == TPM_RH_NULL) { // For Temporary Object, using self name algorithm hashAlg = nameAlg; } else { // Otherwise, using parent's name algorithm hashAlg = ObjectGetNameAlg(parentHandle); } // Starting of sensitive data without wrappers sensitiveData = outPrivate->t.buffer; // Compute the integrity size integritySize = sizeof(UINT16) + CryptGetHashDigestSize(hashAlg); // Reserve space for integrity sensitiveData += integritySize; // Get iv size ivSize = GetIV2BSize(parentHandle); // Reserve space for iv sensitiveData += ivSize; // Marshal sensitive area, leaving the leading 2 bytes for size buffer = sensitiveData + sizeof(UINT16); bufferSize = sizeof(TPMT_SENSITIVE); dataSize = TPMT_SENSITIVE_Marshal(sensitive, &buffer, &bufferSize); // Adding size before the data area buffer = sensitiveData; bufferSize = sizeof(UINT16); UINT16_Marshal(&dataSize, &buffer, &bufferSize); // Adjust the dataSize to include the size field dataSize += sizeof(UINT16); // Adjust the pointer to inner buffer including the iv sensitiveData = outPrivate->t.buffer + ivSize; //Produce outer wrap, including encryption and HMAC outPrivate->t.size = ProduceOuterWrap(parentHandle, name, hashAlg, NULL, TRUE, dataSize, outPrivate->t.buffer); return; } // // // PrivateToSensitive() // // Unwrap a input private area. Check the integrity, decrypt and retrieve data to a sensitive structure. The // operations in this function: // a) check the integrity HMAC of the input private area // b) decrypt the private buffer // c) unmarshal TPMT_SENSITIVE structure into the buffer of TPMT_SENSITIVE // // Error Returns Meaning // // TPM_RC_INTEGRITY if the private area integrity is bad // TPM_RC_SENSITIVE unmarshal errors while unmarshaling TPMS_ENCRYPT from input // private // TPM_RC_VALUE outer wrapper does not have an iV of the correct size // TPM_RC PrivateToSensitive( TPM2B_PRIVATE *inPrivate, // IN: input private structure TPM2B_NAME *name, // IN: the name of the object TPM_HANDLE parentHandle, // IN: The parent's handle TPM_ALG_ID nameAlg, // IN: hash algorithm in public area. It is // passed separately because we only pass // name, rather than the whole public area // of the object. This parameter is used in // the following two cases: 1. primary // objects. 2. duplication blob with inner // wrap. In other cases, this parameter // will be ignored TPMT_SENSITIVE *sensitive // OUT: sensitive structure ) { TPM_RC result; BYTE *buffer; INT32 size; BYTE *sensitiveData; // pointer to the sensitive data UINT16 dataSize; UINT16 dataSizeInput; TPMI_ALG_HASH hashAlg; // hash algorithm for integrity OBJECT *parent = NULL; UINT16 integritySize; UINT16 ivSize; // Make sure that name is provided pAssert(name != NULL && name->t.size != 0); // Find the hash algorithm for integrity computation if(parentHandle == TPM_RH_NULL) { // For Temporary Object, using self name algorithm hashAlg = nameAlg; } else { // Otherwise, using parent's name algorithm hashAlg = ObjectGetNameAlg(parentHandle); } // unwrap outer result = UnwrapOuter(parentHandle, name, hashAlg, NULL, TRUE, inPrivate->t.size, inPrivate->t.buffer); if(result != TPM_RC_SUCCESS) return result; // Compute the inner integrity size. integritySize = sizeof(UINT16) + CryptGetHashDigestSize(hashAlg); // Get iv size ivSize = GetIV2BSize(parentHandle); // The starting of sensitive data and data size without outer wrapper sensitiveData = inPrivate->t.buffer + integritySize + ivSize; dataSize = inPrivate->t.size - integritySize - ivSize; // Unmarshal input data size buffer = sensitiveData; size = (INT32) dataSize; result = UINT16_Unmarshal(&dataSizeInput, &buffer, &size); if(result == TPM_RC_SUCCESS) { if((dataSizeInput + sizeof(UINT16)) != dataSize) result = TPM_RC_SENSITIVE; else { // Unmarshal sensitive buffer to sensitive structure result = TPMT_SENSITIVE_Unmarshal(sensitive, &buffer, &size); if(result != TPM_RC_SUCCESS || size != 0) { pAssert( (parent == NULL) || parent->publicArea.objectAttributes.fixedTPM == CLEAR); result = TPM_RC_SENSITIVE; } else { // Always remove trailing zeros at load so that it is not necessary // to check // each time auth is checked. MemoryRemoveTrailingZeros(&(sensitive->authValue)); } } } return result; } // // // SensitiveToDuplicate() // // This function prepare the duplication blob from the sensitive area. The operations in this function: // a) marshal TPMT_SENSITIVE structure into the buffer of TPM2B_PRIVATE // b) apply inner wrap to the sensitive area if required // c) apply outer wrap if required // void SensitiveToDuplicate( TPMT_SENSITIVE *sensitive, // IN: sensitive structure TPM2B_NAME *name, // IN: the name of the object TPM_HANDLE parentHandle, // IN: The new parent's handle TPM_ALG_ID nameAlg, // IN: hash algorithm in public area. It // is passed separately because we // only pass name, rather than the // whole public area of the object. TPM2B_SEED *seed, // IN: the external seed. If external // seed is provided with size of 0, // no outer wrap should be applied // to duplication blob. TPMT_SYM_DEF_OBJECT *symDef, // IN: Symmetric key definition. If the // symmetric key algorithm is NULL, // no inner wrap should be applied. TPM2B_DATA *innerSymKey, // IN/OUT: a symmetric key may be // provided to encrypt the inner // wrap of a duplication blob. May // be generated here if needed. TPM2B_PRIVATE *outPrivate // OUT: output private structure ) { BYTE *buffer; // Auxiliary buffer pointer INT32 bufferSize; BYTE *sensitiveData; // pointer to the sensitive data TPMI_ALG_HASH outerHash = TPM_ALG_NULL;// The hash algorithm for outer wrap TPMI_ALG_HASH innerHash = TPM_ALG_NULL;// The hash algorithm for inner wrap UINT16 dataSize; // data blob size BOOL doInnerWrap = FALSE; BOOL doOuterWrap = FALSE; // Make sure that name is provided pAssert(name != NULL && name->t.size != 0); // Make sure symDef and innerSymKey are not NULL pAssert(symDef != NULL && innerSymKey != NULL); // Starting of sensitive data without wrappers sensitiveData = outPrivate->t.buffer; // Find out if inner wrap is required if(symDef->algorithm != TPM_ALG_NULL) { doInnerWrap = TRUE; // Use self nameAlg as inner hash algorithm innerHash = nameAlg; // Adjust sensitive data pointer sensitiveData += sizeof(UINT16) + CryptGetHashDigestSize(innerHash); } // Find out if outer wrap is required if(seed->t.size != 0) { doOuterWrap = TRUE; // Use parent nameAlg as outer hash algorithm outerHash = ObjectGetNameAlg(parentHandle); // Adjust sensitive data pointer sensitiveData += sizeof(UINT16) + CryptGetHashDigestSize(outerHash); } // Marshal sensitive area, leaving the leading 2 bytes for size buffer = sensitiveData + sizeof(UINT16); bufferSize = sizeof(TPMT_SENSITIVE); dataSize = TPMT_SENSITIVE_Marshal(sensitive, &buffer, &bufferSize); // Adding size before the data area buffer = sensitiveData; bufferSize = sizeof(UINT16); UINT16_Marshal(&dataSize, &buffer, &bufferSize); // Adjust the dataSize to include the size field dataSize += sizeof(UINT16); // Apply inner wrap for duplication blob. It includes both integrity and // encryption if(doInnerWrap) { BYTE *innerBuffer = NULL; BOOL symKeyInput = TRUE; innerBuffer = outPrivate->t.buffer; // Skip outer integrity space if(doOuterWrap) innerBuffer += sizeof(UINT16) + CryptGetHashDigestSize(outerHash); dataSize = ProduceInnerIntegrity(name, innerHash, dataSize, innerBuffer); // Generate inner encryption key if needed if(innerSymKey->t.size == 0) { innerSymKey->t.size = (symDef->keyBits.sym + 7) / 8; CryptGenerateRandom(innerSymKey->t.size, innerSymKey->t.buffer); // TPM generates symmetric encryption. Set the flag to FALSE symKeyInput = FALSE; } else { // assume the input key size should matches the symmetric definition pAssert(innerSymKey->t.size == (symDef->keyBits.sym + 7) / 8); } // Encrypt inner buffer in place CryptSymmetricEncrypt(innerBuffer, symDef->algorithm, symDef->keyBits.sym, TPM_ALG_CFB, innerSymKey->t.buffer, NULL, dataSize, innerBuffer); // If the symmetric encryption key is imported, clear the buffer for // output if(symKeyInput) innerSymKey->t.size = 0; } // Apply outer wrap for duplication blob. It includes both integrity and // encryption if(doOuterWrap) { dataSize = ProduceOuterWrap(parentHandle, name, outerHash, seed, FALSE, dataSize, outPrivate->t.buffer); } // Data size for output outPrivate->t.size = dataSize; return; } // // // DuplicateToSensitive() // // Unwrap a duplication blob. Check the integrity, decrypt and retrieve data to a sensitive structure. The // operations in this function: // a) check the integrity HMAC of the input private area // b) decrypt the private buffer // c) unmarshal TPMT_SENSITIVE structure into the buffer of TPMT_SENSITIVE // // Error Returns Meaning // // TPM_RC_INSUFFICIENT unmarshaling sensitive data from inPrivate failed // TPM_RC_INTEGRITY inPrivate data integrity is broken // TPM_RC_SIZE unmarshaling sensitive data from inPrivate failed // TPM_RC DuplicateToSensitive( TPM2B_PRIVATE *inPrivate, // IN: input private structure TPM2B_NAME *name, // IN: the name of the object TPM_HANDLE parentHandle, // IN: The parent's handle TPM_ALG_ID nameAlg, // IN: hash algorithm in public area. TPM2B_SEED *seed, // IN: an external seed may be provided. // If external seed is provided with // size of 0, no outer wrap is // applied TPMT_SYM_DEF_OBJECT *symDef, // IN: Symmetric key definition. If the // symmetric key algorithm is NULL, // no inner wrap is applied TPM2B_DATA *innerSymKey, // IN: a symmetric key may be provided // to decrypt the inner wrap of a // duplication blob. TPMT_SENSITIVE *sensitive // OUT: sensitive structure ) { TPM_RC result; BYTE *buffer; INT32 size; BYTE *sensitiveData; // pointer to the sensitive data UINT16 dataSize; UINT16 dataSizeInput; // Make sure that name is provided pAssert(name != NULL && name->t.size != 0); // Make sure symDef and innerSymKey are not NULL pAssert(symDef != NULL && innerSymKey != NULL); // Starting of sensitive data sensitiveData = inPrivate->t.buffer; dataSize = inPrivate->t.size; // Find out if outer wrap is applied if(seed->t.size != 0) { TPMI_ALG_HASH outerHash = TPM_ALG_NULL; // Use parent nameAlg as outer hash algorithm outerHash = ObjectGetNameAlg(parentHandle); result = UnwrapOuter(parentHandle, name, outerHash, seed, FALSE, dataSize, sensitiveData); if(result != TPM_RC_SUCCESS) return result; // Adjust sensitive data pointer and size sensitiveData += sizeof(UINT16) + CryptGetHashDigestSize(outerHash); dataSize -= sizeof(UINT16) + CryptGetHashDigestSize(outerHash); } // Find out if inner wrap is applied if(symDef->algorithm != TPM_ALG_NULL) { TPMI_ALG_HASH innerHash = TPM_ALG_NULL; // assume the input key size should matches the symmetric definition pAssert(innerSymKey->t.size == (symDef->keyBits.sym + 7) / 8); // Decrypt inner buffer in place CryptSymmetricDecrypt(sensitiveData, symDef->algorithm, symDef->keyBits.sym, TPM_ALG_CFB, innerSymKey->t.buffer, NULL, dataSize, sensitiveData); // Use self nameAlg as inner hash algorithm innerHash = nameAlg; // Check inner integrity result = CheckInnerIntegrity(name, innerHash, dataSize, sensitiveData); if(result != TPM_RC_SUCCESS) return result; // Adjust sensitive data pointer and size sensitiveData += sizeof(UINT16) + CryptGetHashDigestSize(innerHash); dataSize -= sizeof(UINT16) + CryptGetHashDigestSize(innerHash); } // Unmarshal input data size buffer = sensitiveData; size = (INT32) dataSize; result = UINT16_Unmarshal(&dataSizeInput, &buffer, &size); if(result == TPM_RC_SUCCESS) { if((dataSizeInput + sizeof(UINT16)) != dataSize) result = TPM_RC_SIZE; else { // Unmarshal sensitive buffer to sensitive structure result = TPMT_SENSITIVE_Unmarshal(sensitive, &buffer, &size); // if the results is OK make sure that all the data was unmarshaled if(result == TPM_RC_SUCCESS && size != 0) result = TPM_RC_SIZE; } } // Always remove trailing zeros at load so that it is not necessary to check // each time auth is checked. if(result == TPM_RC_SUCCESS) MemoryRemoveTrailingZeros(&(sensitive->authValue)); return result; } // // // SecretToCredential() // // This function prepare the credential blob from a secret (a TPM2B_DIGEST) The operations in this // function: // a) marshal TPM2B_DIGEST structure into the buffer of TPM2B_ID_OBJECT // b) encrypt the private buffer, excluding the leading integrity HMAC area // c) compute integrity HMAC and append to the beginning of the buffer. // d) Set the total size of TPM2B_ID_OBJECT buffer // void SecretToCredential( TPM2B_DIGEST *secret, // IN: secret information TPM2B_NAME *name, // IN: the name of the object TPM2B_SEED *seed, // IN: an external seed. TPM_HANDLE protector, // IN: The protector's handle TPM2B_ID_OBJECT *outIDObject // OUT: output credential ) { BYTE *buffer; // Auxiliary buffer pointer INT32 bufferSize; BYTE *sensitiveData; // pointer to the sensitive data TPMI_ALG_HASH outerHash; // The hash algorithm for outer wrap UINT16 dataSize; // data blob size pAssert(secret != NULL && outIDObject != NULL); // use protector's name algorithm as outer hash outerHash = ObjectGetNameAlg(protector); // Marshal secret area to credential buffer, leave space for integrity sensitiveData = outIDObject->t.credential + sizeof(UINT16) + CryptGetHashDigestSize(outerHash); // Marshal secret area buffer = sensitiveData; bufferSize = sizeof(TPM2B_DIGEST); dataSize = TPM2B_DIGEST_Marshal(secret, &buffer, &bufferSize); // Apply outer wrap outIDObject->t.size = ProduceOuterWrap(protector, name, outerHash, seed, FALSE, dataSize, outIDObject->t.credential); return; } // // // CredentialToSecret() // // Unwrap a credential. Check the integrity, decrypt and retrieve data to a TPM2B_DIGEST structure. The // operations in this function: // a) check the integrity HMAC of the input credential area // b) decrypt the credential buffer // c) unmarshal TPM2B_DIGEST structure into the buffer of TPM2B_DIGEST // // Error Returns Meaning // // TPM_RC_INSUFFICIENT error during credential unmarshaling // TPM_RC_INTEGRITY credential integrity is broken // TPM_RC_SIZE error during credential unmarshaling // TPM_RC_VALUE IV size does not match the encryption algorithm block size // TPM_RC CredentialToSecret( TPM2B_ID_OBJECT *inIDObject, // IN: input credential blob TPM2B_NAME *name, // IN: the name of the object TPM2B_SEED *seed, // IN: an external seed. TPM_HANDLE protector, // IN: The protector's handle TPM2B_DIGEST *secret // OUT: secret information ) { TPM_RC result; BYTE *buffer; INT32 size; TPMI_ALG_HASH outerHash; // The hash algorithm for outer wrap BYTE *sensitiveData; // pointer to the sensitive data UINT16 dataSize; // use protector's name algorithm as outer hash outerHash = ObjectGetNameAlg(protector); // Unwrap outer, a TPM_RC_INTEGRITY error may be returned at this point result = UnwrapOuter(protector, name, outerHash, seed, FALSE, inIDObject->t.size, inIDObject->t.credential); if(result == TPM_RC_SUCCESS) { // Compute the beginning of sensitive data sensitiveData = inIDObject->t.credential + sizeof(UINT16) + CryptGetHashDigestSize(outerHash); dataSize = inIDObject->t.size - (sizeof(UINT16) + CryptGetHashDigestSize(outerHash)); // Unmarshal secret buffer to TPM2B_DIGEST structure buffer = sensitiveData; size = (INT32) dataSize; result = TPM2B_DIGEST_Unmarshal(secret, &buffer, &size); // If there were no other unmarshaling errors, make sure that the // expected amount of data was recovered if(result == TPM_RC_SUCCESS && size != 0) return TPM_RC_SIZE; } return result; }