/********************************************************************************/ /* */ /* Implementation of cryptographic primitives for RSA */ /* Written by Ken Goldman */ /* IBM Thomas J. Watson Research Center */ /* */ /* Licenses and Notices */ /* */ /* 1. Copyright Licenses: */ /* */ /* - Trusted Computing Group (TCG) grants to the user of the source code in */ /* this specification (the "Source Code") a worldwide, irrevocable, */ /* nonexclusive, royalty free, copyright license to reproduce, create */ /* derivative works, distribute, display and perform the Source Code and */ /* derivative works thereof, and to grant others the rights granted herein. */ /* */ /* - The TCG grants to the user of the other parts of the specification */ /* (other than the Source Code) the rights to reproduce, distribute, */ /* display, and perform the specification solely for the purpose of */ /* developing products based on such documents. */ /* */ /* 2. Source Code Distribution Conditions: */ /* */ /* - Redistributions of Source Code must retain the above copyright licenses, */ /* this list of conditions and the following disclaimers. */ /* */ /* - Redistributions in binary form must reproduce the above copyright */ /* licenses, this list of conditions and the following disclaimers in the */ /* documentation and/or other materials provided with the distribution. */ /* */ /* 3. Disclaimers: */ /* */ /* - THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF */ /* LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH */ /* RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES) */ /* THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR OTHERWISE. */ /* Contact TCG Administration (admin@trustedcomputinggroup.org) for */ /* information on specification licensing rights available through TCG */ /* membership agreements. */ /* */ /* - THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED */ /* WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR */ /* FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR */ /* NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY */ /* OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE. */ /* */ /* - Without limitation, TCG and its members and licensors disclaim all */ /* liability, including liability for infringement of any proprietary */ /* rights, relating to use of information in this specification and to the */ /* implementation of this specification, and TCG disclaims all liability for */ /* cost of procurement of substitute goods or services, lost profits, loss */ /* of use, loss of data or any incidental, consequential, direct, indirect, */ /* or special damages, whether under contract, tort, warranty or otherwise, */ /* arising in any way out of use or reliance upon this specification or any */ /* information herein. */ /* */ /* (c) Copyright IBM Corp. and others, 2016 - 2024 */ /* */ /********************************************************************************/ //** Introduction // // This file contains implementation of cryptographic primitives for RSA. // Vendors may replace the implementation in this file with their own library // functions. //** Includes // Need this define to get the 'private' defines for this function #define CRYPT_RSA_C #include "Tpm.h" #include "TpmMath_Util_fp.h" #if ALG_RSA //** Obligatory Initialization Functions //*** CryptRsaInit() // Function called at _TPM_Init(). BOOL CryptRsaInit(void) { return TRUE; } //*** CryptRsaStartup() // Function called at TPM2_Startup() BOOL CryptRsaStartup(void) { return TRUE; } //** Internal Functions //*** RsaInitializeExponent() // This function initializes the bignum data structure that holds the private // exponent. This function returns the pointer to the private exponent value so that // it can be used in an initializer for a data declaration. static privateExponent* RsaInitializeExponent(privateExponent* Z) { // verify privateExponent packing matches the usage of the bn pointer as an // array in below function MUST_BE(offsetof(privateExponent, Q) == SIZEOF_MEMBER(privateExponent, P)); Crypt_Int** bn = (Crypt_Int**)&Z->P; int i; // for(i = 0; i < 5; i++) { bn[i] = (Crypt_Int*)&(Z->entries[i]); ExtMath_Initialize_Int(bn[i], MAX_RSA_KEY_BITS / 2); } return Z; } //*** MakePgreaterThanQ() // This function swaps the pointers for P and Q if Q happens to be larger than Q. static void MakePgreaterThanQ(privateExponent* Z) { if(ExtMath_UnsignedCmp(Z->P, Z->Q) < 0) { Crypt_Int* bnT = Z->P; Z->P = Z->Q; Z->Q = bnT; } } //*** PackExponent() // This function takes the bignum private exponent and converts it into TPM2B form. // In this form, the size field contains the overall size of the packed data. The // buffer contains 5, equal sized values in P, Q, dP, dQ, qInv order. For example, if // a key has a 2Kb public key, then the packed private key will contain 5, 1Kb values. // This form makes it relatively easy to load and save the values without changing // the normal unmarshaling to do anything more than allow a larger TPM2B for the // private key. Also, when exporting the value, all that is needed is to change the // size field of the private key in order to save just the P value. // Return Type: BOOL // TRUE(1) success // FALSE(0) failure // The data is too big to fit static BOOL PackExponent(TPM2B_PRIVATE_KEY_RSA* packed, privateExponent* Z) { int i; UINT16 primeSize = (UINT16)BITS_TO_BYTES(ExtMath_MostSigBitNum(Z->P)); UINT16 pS = primeSize; // pAssert((primeSize * 5) <= sizeof(packed->t.buffer)); packed->t.size = (primeSize * 5) + RSA_prime_flag; for(i = 0; i < 5; i++) if(!ExtMath_IntToBytes( (Crypt_Int*)&Z->entries[i], &packed->t.buffer[primeSize * i], &pS)) return FALSE; if(pS != primeSize) return FALSE; return TRUE; } //*** UnpackExponent() // This function unpacks the private exponent from its TPM2B form into its bignum // form. // Return Type: BOOL // TRUE(1) success // FALSE(0) TPM2B is not the correct size static BOOL UnpackExponent(TPM2B_PRIVATE_KEY_RSA* b, privateExponent* Z) { UINT16 primeSize = b->t.size & ~RSA_prime_flag; int i; Crypt_Int** bn = &Z->P; // GOTO_ERROR_UNLESS(b->t.size & RSA_prime_flag); RsaInitializeExponent(Z); GOTO_ERROR_UNLESS((primeSize % 5) == 0); primeSize /= 5; for(i = 0; i < 5; i++) GOTO_ERROR_UNLESS( ExtMath_IntFromBytes(bn[i], &b->t.buffer[primeSize * i], primeSize) != NULL); MakePgreaterThanQ(Z); return TRUE; Error: return FALSE; } //*** ComputePrivateExponent() // This function computes the private exponent from the primes. // Return Type: BOOL // TRUE(1) success // FALSE(0) failure static BOOL ComputePrivateExponent( Crypt_Int* pubExp, // IN: the public exponent privateExponent* Z // IN/OUT: on input, has primes P and Q. On // output, has P, Q, dP, dQ, and pInv ) { BOOL pOK; BOOL qOK; CRYPT_PRIME_VAR(pT); // // make p the larger value so that m2 is always less than p MakePgreaterThanQ(Z); //dP = (1/e) mod (p-1) pOK = ExtMath_SubtractWord(pT, Z->P, 1); pOK = pOK && ExtMath_ModInverse(Z->dP, pubExp, pT); //dQ = (1/e) mod (q-1) qOK = ExtMath_SubtractWord(pT, Z->Q, 1); qOK = qOK && ExtMath_ModInverse(Z->dQ, pubExp, pT); // qInv = (1/q) mod p if(pOK && qOK) pOK = qOK = ExtMath_ModInverse(Z->qInv, Z->Q, Z->P); if(!pOK) ExtMath_SetWord(Z->P, 0); if(!qOK) ExtMath_SetWord(Z->Q, 0); return pOK && qOK; } //*** RsaPrivateKeyOp() // This function is called to do the exponentiation with the private key. Compile // options allow use of the simple (but slow) private exponent, or the more complex // but faster CRT method. // Return Type: BOOL // TRUE(1) success // FALSE(0) failure static BOOL RsaPrivateKeyOp(Crypt_Int* inOut, // IN/OUT: number to be exponentiated privateExponent* Z) { CRYPT_RSA_VAR(M1); CRYPT_RSA_VAR(M2); CRYPT_RSA_VAR(M); CRYPT_RSA_VAR(H); // MakePgreaterThanQ(Z); // m1 = cdP mod p GOTO_ERROR_UNLESS(ExtMath_ModExp(M1, inOut, Z->dP, Z->P)); // m2 = cdQ mod q GOTO_ERROR_UNLESS(ExtMath_ModExp(M2, inOut, Z->dQ, Z->Q)); // h = qInv * (m1 - m2) mod p = qInv * (m1 + P - m2) mod P because Q < P // so m2 < P GOTO_ERROR_UNLESS(ExtMath_Subtract(H, Z->P, M2)); GOTO_ERROR_UNLESS(ExtMath_Add(H, H, M1)); GOTO_ERROR_UNLESS(ExtMath_ModMult(H, H, Z->qInv, Z->P)); // m = m2 + h * q GOTO_ERROR_UNLESS(ExtMath_Multiply(M, H, Z->Q)); GOTO_ERROR_UNLESS(ExtMath_Add(inOut, M2, M)); return TRUE; Error: return FALSE; } //*** RSAEP() // This function performs the RSAEP operation defined in PKCS#1v2.1. It is // an exponentiation of a value ('m') with the public exponent ('e'), modulo // the public ('n'). // // Return Type: TPM_RC // TPM_RC_VALUE number to exponentiate is larger than the modulus // static TPM_RC RSAEP(TPM2B* dInOut, // IN: size of the encrypted block and the size of // the encrypted value. It must be the size of // the modulus. // OUT: the encrypted data. Will receive the // decrypted value OBJECT* key // IN: the key to use ) { TPM2B_TYPE(4BYTES, 4); TPM2B_4BYTES e2B; UINT32 e = key->publicArea.parameters.rsaDetail.exponent; // if(e == 0) e = RSA_DEFAULT_PUBLIC_EXPONENT; UINT32_TO_BYTE_ARRAY(e, e2B.t.buffer); e2B.t.size = 4; return ModExpB(dInOut->size, dInOut->buffer, dInOut->size, dInOut->buffer, e2B.t.size, e2B.t.buffer, key->publicArea.unique.rsa.t.size, key->publicArea.unique.rsa.t.buffer); } //*** RSADP() // This function performs the RSADP operation defined in PKCS#1v2.1. It is // an exponentiation of a value ('c') with the private exponent ('d'), modulo // the public modulus ('n'). The decryption is in place. // // This function also checks the size of the private key. If the size indicates // that only a prime value is present, the key is converted to being a private // exponent. // // Return Type: TPM_RC // TPM_RC_SIZE the value to decrypt is larger than the modulus // static TPM_RC RSADP(TPM2B* inOut, // IN/OUT: the value to encrypt OBJECT* key // IN: the key ) { CRYPT_RSA_INITIALIZED(bnM, inOut); NEW_PRIVATE_EXPONENT(Z); if(UnsignedCompareB(inOut->size, inOut->buffer, key->publicArea.unique.rsa.t.size, key->publicArea.unique.rsa.t.buffer) >= 0) return TPM_RC_SIZE; // private key operation requires that private exponent be loaded // During self-test, this might not be the case so load it up if it hasn't // already done // been done if((key->sensitive.sensitive.rsa.t.size & RSA_prime_flag) == 0) { if(CryptRsaLoadPrivateExponent(&key->publicArea, &key->sensitive) != TPM_RC_SUCCESS) return TPM_RC_BINDING; } GOTO_ERROR_UNLESS(UnpackExponent(&key->sensitive.sensitive.rsa, Z)); GOTO_ERROR_UNLESS(RsaPrivateKeyOp(bnM, Z)); GOTO_ERROR_UNLESS(TpmMath_IntTo2B(bnM, inOut, inOut->size)); return TPM_RC_SUCCESS; Error: return TPM_RC_FAILURE; } //*** OaepEncode() // This function performs OAEP padding. The size of the buffer to receive the // OAEP padded data must equal the size of the modulus // // Return Type: TPM_RC // TPM_RC_VALUE 'hashAlg' is not valid or message size is too large // static TPM_RC OaepEncode( TPM2B* padded, // OUT: the pad data TPM_ALG_ID hashAlg, // IN: algorithm to use for padding const TPM2B* label, // IN: null-terminated string (may be NULL) TPM2B* message, // IN: the message being padded RAND_STATE* rand // IN: the random number generator to use ) { INT32 padLen; INT32 dbSize; INT32 i; BYTE mySeed[MAX_DIGEST_SIZE]; BYTE* seed = mySeed; UINT16 hLen = CryptHashGetDigestSize(hashAlg); BYTE mask[MAX_RSA_KEY_BYTES]; BYTE* pp; BYTE* pm; TPM_RC retVal = TPM_RC_SUCCESS; pAssert(padded != NULL && message != NULL); // A value of zero is not allowed because the KDF can't produce a result // if the digest size is zero. if(hLen == 0) return TPM_RC_VALUE; // Basic size checks // make sure digest isn't too big for key size if(padded->size < (2 * hLen) + 2) ERROR_EXIT(TPM_RC_HASH); // and that message will fit messageSize <= k - 2hLen - 2 if(message->size > (padded->size - (2 * hLen) - 2)) ERROR_EXIT(TPM_RC_VALUE); // Hash L even if it is null // Offset into padded leaving room for masked seed and byte of zero pp = &padded->buffer[hLen + 1]; if(CryptHashBlock(hashAlg, label->size, (BYTE*)label->buffer, hLen, pp) != hLen) ERROR_EXIT(TPM_RC_FAILURE); // concatenate PS of k mLen 2hLen 2 padLen = padded->size - message->size - (2 * hLen) - 2; MemorySet(&pp[hLen], 0, padLen); pp[hLen + padLen] = 0x01; padLen += 1; memcpy(&pp[hLen + padLen], message->buffer, message->size); // The total size of db = hLen + pad + mSize; dbSize = hLen + padLen + message->size; DRBG_Generate(rand, mySeed, (UINT16)hLen); if(g_inFailureMode) ERROR_EXIT(TPM_RC_FAILURE); // mask = MGF1 (seed, nSize hLen 1) CryptMGF_KDF(dbSize, mask, hashAlg, hLen, seed, 0); // Create the masked db pm = mask; for(i = dbSize; i > 0; i--) *pp++ ^= *pm++; pp = &padded->buffer[hLen + 1]; // Run the masked data through MGF1 if(CryptMGF_KDF(hLen, &padded->buffer[1], hashAlg, dbSize, pp, 0) != (unsigned)hLen) ERROR_EXIT(TPM_RC_VALUE); // Now XOR the seed to create masked seed pp = &padded->buffer[1]; pm = seed; for(i = hLen; i > 0; i--) *pp++ ^= *pm++; // Set the first byte to zero padded->buffer[0] = 0x00; Exit: return retVal; } //*** OaepDecode() // This function performs OAEP padding checking. The size of the buffer to receive // the recovered data. If the padding is not valid, the 'dSize' size is set to zero // and the function returns TPM_RC_VALUE. // // The 'dSize' parameter is used as an input to indicate the size available in the // buffer. // If insufficient space is available, the size is not changed and the return code // is TPM_RC_VALUE. // // Return Type: TPM_RC // TPM_RC_VALUE the value to decode was larger than the modulus, or // the padding is wrong or the buffer to receive the // results is too small // // static TPM_RC OaepDecode( TPM2B* dataOut, // OUT: the recovered data TPM_ALG_ID hashAlg, // IN: algorithm to use for padding const TPM2B* label, // IN: null-terminated string (may be NULL) TPM2B* padded // IN: the padded data ) { UINT32 i; BYTE seedMask[MAX_DIGEST_SIZE]; UINT32 hLen = CryptHashGetDigestSize(hashAlg); BYTE mask[MAX_RSA_KEY_BYTES]; BYTE* pp; BYTE* pm; TPM_RC retVal = TPM_RC_SUCCESS; // Strange size (anything smaller can't be an OAEP padded block) // Also check for no leading 0 if((padded->size < (unsigned)((2 * hLen) + 2)) || (padded->buffer[0] != 0)) ERROR_EXIT(TPM_RC_VALUE); // Use the hash size to determine what to put through MGF1 in order // to recover the seedMask CryptMGF_KDF(hLen, seedMask, hashAlg, padded->size - hLen - 1, &padded->buffer[hLen + 1], 0); // Recover the seed into seedMask pAssert(hLen <= sizeof(seedMask)); pp = &padded->buffer[1]; pm = seedMask; for(i = hLen; i > 0; i--) *pm++ ^= *pp++; // Use the seed to generate the data mask CryptMGF_KDF(padded->size - hLen - 1, mask, hashAlg, hLen, seedMask, 0); // Use the mask generated from seed to recover the padded data pp = &padded->buffer[hLen + 1]; pm = mask; for(i = (padded->size - hLen - 1); i > 0; i--) *pm++ ^= *pp++; // Make sure that the recovered data has the hash of the label // Put trial value in the seed mask if((CryptHashBlock(hashAlg, label->size, (BYTE*)label->buffer, hLen, seedMask)) != hLen) FAIL(FATAL_ERROR_INTERNAL); if(memcmp(seedMask, mask, hLen) != 0) ERROR_EXIT(TPM_RC_VALUE); // find the start of the data pm = &mask[hLen]; for(i = (UINT32)padded->size - (2 * hLen) - 1; i > 0; i--) { if(*pm++ != 0) break; } // If we ran out of data or didn't end with 0x01, then return an error if(i == 0 || pm[-1] != 0x01) ERROR_EXIT(TPM_RC_VALUE); // pm should be pointing at the first part of the data // and i is one greater than the number of bytes to move i--; if(i > dataOut->size) // Special exit to preserve the size of the output buffer return TPM_RC_VALUE; memcpy(dataOut->buffer, pm, i); dataOut->size = (UINT16)i; Exit: if(retVal != TPM_RC_SUCCESS) dataOut->size = 0; return retVal; } //*** PKCS1v1_5Encode() // This function performs the encoding for RSAES-PKCS1-V1_5-ENCRYPT as defined in // PKCS#1V2.1 // Return Type: TPM_RC // TPM_RC_VALUE message size is too large // static TPM_RC RSAES_PKCS1v1_5Encode(TPM2B* padded, // OUT: the pad data TPM2B* message, // IN: the message being padded RAND_STATE* rand) { UINT32 ps = padded->size - message->size - 3; // if(message->size > padded->size - 11) return TPM_RC_VALUE; // move the message to the end of the buffer memcpy(&padded->buffer[padded->size - message->size], message->buffer, message->size); // Set the first byte to 0x00 and the second to 0x02 padded->buffer[0] = 0; padded->buffer[1] = 2; // Fill with random bytes DRBG_Generate(rand, &padded->buffer[2], (UINT16)ps); if(g_inFailureMode) return TPM_RC_FAILURE; // Set the delimiter for the random field to 0 padded->buffer[2 + ps] = 0; // Now, the only messy part. Make sure that all the 'ps' bytes are non-zero // In this implementation, use the value of the current index for(ps++; ps > 1; ps--) { if(padded->buffer[ps] == 0) padded->buffer[ps] = 0x55; // In the < 0.5% of the cases that the // random value is 0, just pick a value to // put into the spot. } return TPM_RC_SUCCESS; } //*** RSAES_Decode() // This function performs the decoding for RSAES-PKCS1-V1_5-ENCRYPT as defined in // PKCS#1V2.1 // // Return Type: TPM_RC // TPM_RC_FAIL decoding error or results would no fit into provided buffer // static TPM_RC RSAES_Decode(TPM2B* message, // OUT: the recovered message TPM2B* coded // IN: the encoded message ) { BOOL fail = FALSE; UINT16 pSize; fail = (coded->size < 11); fail = (coded->buffer[0] != 0x00) | fail; fail = (coded->buffer[1] != 0x02) | fail; for(pSize = 2; pSize < coded->size; pSize++) { if(coded->buffer[pSize] == 0) break; } pSize++; // Make sure that pSize has not gone over the end and that there are at least 8 // bytes of pad data. fail = (pSize > coded->size) | fail; fail = ((pSize - 2) <= 8) | fail; if((message->size < (UINT16)(coded->size - pSize)) || fail) return TPM_RC_VALUE; message->size = coded->size - pSize; memcpy(message->buffer, &coded->buffer[pSize], coded->size - pSize); return TPM_RC_SUCCESS; } //*** CryptRsaPssSaltSize() // This function computes the salt size used in PSS. It is broken out so that // the X509 code can get the same value that is used by the encoding function in this // module. INT16 CryptRsaPssSaltSize(INT16 hashSize, INT16 outSize) { INT16 saltSize; // // (Mask Length) = (outSize - hashSize - 1); // Max saltSize is (Mask Length) - 1 saltSize = (outSize - hashSize - 1) - 1; // Use the maximum salt size allowed by FIPS 186-4 if(saltSize > hashSize) saltSize = hashSize; else if(saltSize < 0) saltSize = 0; return saltSize; } //*** PssEncode() // This function creates an encoded block of data that is the size of modulus. // The function uses the maximum salt size that will fit in the encoded block. // // Returns TPM_RC_SUCCESS or goes into failure mode. static TPM_RC PssEncode(TPM2B* out, // OUT: the encoded buffer TPM_ALG_ID hashAlg, // IN: hash algorithm for the encoding TPM2B* digest, // IN: the digest RAND_STATE* rand // IN: random number source ) { UINT32 hLen = CryptHashGetDigestSize(hashAlg); BYTE salt[MAX_RSA_KEY_BYTES - 1]; UINT16 saltSize; BYTE* ps = salt; BYTE* pOut; UINT16 mLen; HASH_STATE hashState; // These are fatal errors indicating bad TPM firmware pAssert(out != NULL && hLen > 0 && digest != NULL); // Get the size of the mask mLen = (UINT16)(out->size - hLen - 1); // Set the salt size saltSize = CryptRsaPssSaltSize((INT16)hLen, (INT16)out->size); //using eOut for scratch space // Set the first 8 bytes to zero pOut = out->buffer; memset(pOut, 0, 8); // Get set the salt DRBG_Generate(rand, salt, saltSize); if(g_inFailureMode) return TPM_RC_FAILURE; // Create the hash of the pad || input hash || salt CryptHashStart(&hashState, hashAlg); CryptDigestUpdate(&hashState, 8, pOut); CryptDigestUpdate2B(&hashState, digest); CryptDigestUpdate(&hashState, saltSize, salt); CryptHashEnd(&hashState, hLen, &pOut[out->size - hLen - 1]); // Create a mask if(CryptMGF_KDF(mLen, pOut, hashAlg, hLen, &pOut[mLen], 0) != mLen) FAIL(FATAL_ERROR_INTERNAL); // Since this implementation uses key sizes that are all even multiples of // 8, just need to make sure that the most significant bit is CLEAR *pOut &= 0x7f; // Before we mess up the pOut value, set the last byte to 0xbc pOut[out->size - 1] = 0xbc; // XOR a byte of 0x01 at the position just before where the salt will be XOR'ed pOut = &pOut[mLen - saltSize - 1]; *pOut++ ^= 0x01; // XOR the salt data into the buffer for(; saltSize > 0; saltSize--) *pOut++ ^= *ps++; // and we are done return TPM_RC_SUCCESS; } //*** PssDecode() // This function checks that the PSS encoded block was built from the // provided digest. If the check is successful, TPM_RC_SUCCESS is returned. // Any other value indicates an error. // // This implementation of PSS decoding is intended for the reference TPM // implementation and is not at all generalized. It is used to check // signatures over hashes and assumptions are made about the sizes of values. // Those assumptions are enforce by this implementation. // This implementation does allow for a variable size salt value to have been // used by the creator of the signature. // // Return Type: TPM_RC // TPM_RC_SCHEME 'hashAlg' is not a supported hash algorithm // TPM_RC_VALUE decode operation failed // static TPM_RC PssDecode( TPM_ALG_ID hashAlg, // IN: hash algorithm to use for the encoding TPM2B* dIn, // In: the digest to compare TPM2B* eIn // IN: the encoded data ) { UINT32 hLen = CryptHashGetDigestSize(hashAlg); BYTE mask[MAX_RSA_KEY_BYTES]; BYTE* pm = mask; BYTE* pe; BYTE pad[8] = {0}; UINT32 i; UINT32 mLen; BYTE fail; TPM_RC retVal = TPM_RC_SUCCESS; HASH_STATE hashState; // These errors are indicative of failures due to programmer error pAssert(dIn != NULL && eIn != NULL); pe = eIn->buffer; // check the hash scheme if(hLen == 0) ERROR_EXIT(TPM_RC_SCHEME); // most significant bit must be zero fail = pe[0] & 0x80; // last byte must be 0xbc fail |= pe[eIn->size - 1] ^ 0xbc; // Use the hLen bytes at the end of the buffer to generate a mask // Doesn't start at the end which is a flag byte mLen = eIn->size - hLen - 1; CryptMGF_KDF(mLen, mask, hashAlg, hLen, &pe[mLen], 0); // Clear the MSO of the mask to make it consistent with the encoding. mask[0] &= 0x7F; pAssert(mLen <= sizeof(mask)); // XOR the data into the mask to recover the salt. This sequence // advances eIn so that it will end up pointing to the seed data // which is the hash of the signature data for(i = mLen; i > 0; i--) *pm++ ^= *pe++; // Find the first byte of 0x01 after a string of all 0x00 for(pm = mask, i = mLen; i > 0; i--) { if(*pm == 0x01) break; else fail |= *pm++; } // i should not be zero fail |= (i == 0); // if we have failed, will continue using the entire mask as the salt value so // that the timing attacks will not disclose anything (I don't think that this // is a problem for TPM applications but, usually, we don't fail so this // doesn't cost anything). if(fail) { i = mLen; pm = mask; } else { pm++; i--; } // i contains the salt size and pm points to the salt. Going to use the input // hash and the seed to recreate the hash in the lower portion of eIn. CryptHashStart(&hashState, hashAlg); // add the pad of 8 zeros CryptDigestUpdate(&hashState, 8, pad); // add the provided digest value CryptDigestUpdate(&hashState, dIn->size, dIn->buffer); // and the salt CryptDigestUpdate(&hashState, i, pm); // get the result fail |= (CryptHashEnd(&hashState, hLen, mask) != hLen); // Compare all bytes for(pm = mask; hLen > 0; hLen--) // don't use fail = because that could skip the increment and compare // operations after the first failure and that gives away timing // information. fail |= *pm++ ^ *pe++; retVal = (fail != 0) ? TPM_RC_VALUE : TPM_RC_SUCCESS; Exit: return retVal; } //*** MakeDerTag() // Construct the DER value that is used in RSASSA // Return Type: INT16 // > 0 size of value // <= 0 no hash exists INT16 MakeDerTag(TPM_ALG_ID hashAlg, INT16 sizeOfBuffer, BYTE* buffer) { // 0x30, 0x31, // SEQUENCE (2 elements) 1st // 0x30, 0x0D, // SEQUENCE (2 elements) // 0x06, 0x09, // HASH OID // 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, // 0x05, 0x00, // NULL // 0x04, 0x20 // OCTET STRING HASH_DEF* info = CryptGetHashDef(hashAlg); INT16 oidSize; // If no OID, can't do encode GOTO_ERROR_UNLESS(info != NULL); oidSize = 2 + (info->OID)[1]; // make sure this fits in the buffer GOTO_ERROR_UNLESS(sizeOfBuffer >= (oidSize + 8)); *buffer++ = 0x30; // 1st SEQUENCE // Size of the 1st SEQUENCE is 6 bytes + size of the hash OID + size of the // digest size *buffer++ = (BYTE)(6 + oidSize + info->digestSize); // *buffer++ = 0x30; // 2nd SEQUENCE // size is 4 bytes of overhead plus the side of the OID *buffer++ = (BYTE)(2 + oidSize); MemoryCopy(buffer, info->OID, oidSize); buffer += oidSize; *buffer++ = 0x05; // Add a NULL *buffer++ = 0x00; *buffer++ = 0x04; *buffer++ = (BYTE)(info->digestSize); return oidSize + 8; Error: return 0; } //*** RSASSA_Encode() // Encode a message using PKCS1v1.5 method. // // Return Type: TPM_RC // TPM_RC_SCHEME 'hashAlg' is not a supported hash algorithm // TPM_RC_SIZE 'eOutSize' is not large enough // TPM_RC_VALUE 'hInSize' does not match the digest size of hashAlg static TPM_RC RSASSA_Encode(TPM2B* pOut, // IN:OUT on in, the size of the public key // on out, the encoded area TPM_ALG_ID hashAlg, // IN: hash algorithm for PKCS1v1_5 TPM2B* hIn // IN: digest value to encode ) { BYTE DER[20]; BYTE* der = DER; INT32 derSize = MakeDerTag(hashAlg, sizeof(DER), DER); BYTE* eOut; INT32 fillSize; TPM_RC retVal = TPM_RC_SUCCESS; // Can't use this scheme if the algorithm doesn't have a DER string defined. if(derSize == 0) ERROR_EXIT(TPM_RC_SCHEME); // If the digest size of 'hashAl' doesn't match the input digest size, then // the DER will misidentify the digest so return an error if(CryptHashGetDigestSize(hashAlg) != hIn->size) ERROR_EXIT(TPM_RC_VALUE); fillSize = pOut->size - derSize - hIn->size - 3; eOut = pOut->buffer; // Make sure that this combination will fit in the provided space if(fillSize < 8) ERROR_EXIT(TPM_RC_SIZE); // Start filling *eOut++ = 0; // initial byte of zero *eOut++ = 1; // byte of 0x01 for(; fillSize > 0; fillSize--) *eOut++ = 0xff; // bunch of 0xff *eOut++ = 0; // another 0 for(; derSize > 0; derSize--) *eOut++ = *der++; // copy the DER der = hIn->buffer; for(fillSize = hIn->size; fillSize > 0; fillSize--) *eOut++ = *der++; // copy the hash Exit: return retVal; } //*** RSASSA_Decode() // This function performs the RSASSA decoding of a signature. // // Return Type: TPM_RC // TPM_RC_VALUE decode unsuccessful // TPM_RC_SCHEME 'haslAlg' is not supported // static TPM_RC RSASSA_Decode( TPM_ALG_ID hashAlg, // IN: hash algorithm to use for the encoding TPM2B* hIn, // In: the digest to compare TPM2B* eIn // IN: the encoded data ) { BYTE fail; BYTE DER[20]; BYTE* der = DER; INT32 derSize = MakeDerTag(hashAlg, sizeof(DER), DER); BYTE* pe; INT32 hashSize = CryptHashGetDigestSize(hashAlg); INT32 fillSize; TPM_RC retVal; BYTE* digest; UINT16 digestSize; pAssert(hIn != NULL && eIn != NULL); pe = eIn->buffer; // Can't use this scheme if the algorithm doesn't have a DER string // defined or if the provided hash isn't the right size if(derSize == 0 || (unsigned)hashSize != hIn->size) ERROR_EXIT(TPM_RC_SCHEME); // Make sure that this combination will fit in the provided space // Since no data movement takes place, can just walk though this // and accept nearly random values. This can only be called from // CryptValidateSignature() so eInSize is known to be in range. fillSize = eIn->size - derSize - hashSize - 3; // Start checking (fail will become non-zero if any of the bytes do not have // the expected value. fail = *pe++; // initial byte of zero fail |= *pe++ ^ 1; // byte of 0x01 for(; fillSize > 0; fillSize--) fail |= *pe++ ^ 0xff; // bunch of 0xff fail |= *pe++; // another 0 for(; derSize > 0; derSize--) fail |= *pe++ ^ *der++; // match the DER digestSize = hIn->size; digest = hIn->buffer; for(; digestSize > 0; digestSize--) fail |= *pe++ ^ *digest++; // match the hash retVal = (fail != 0) ? TPM_RC_VALUE : TPM_RC_SUCCESS; Exit: return retVal; } //** Externally Accessible Functions //*** CryptRsaSelectScheme() // This function is used by TPM2_RSA_Decrypt and TPM2_RSA_Encrypt. It sets up // the rules to select a scheme between input and object default. // This function assume the RSA object is loaded. // If a default scheme is defined in object, the default scheme should be chosen, // otherwise, the input scheme should be chosen. // In the case that both the object and 'scheme' are not TPM_ALG_NULL, then // if the schemes are the same, the input scheme will be chosen. // if the scheme are not compatible, a NULL pointer will be returned. // // The return pointer may point to a TPM_ALG_NULL scheme. TPMT_RSA_DECRYPT* CryptRsaSelectScheme( TPMI_DH_OBJECT rsaHandle, // IN: handle of an RSA key TPMT_RSA_DECRYPT* scheme // IN: a sign or decrypt scheme ) { OBJECT* rsaObject; TPMT_ASYM_SCHEME* keyScheme; TPMT_RSA_DECRYPT* retVal = NULL; // Get sign object pointer rsaObject = HandleToObject(rsaHandle); keyScheme = &rsaObject->publicArea.parameters.asymDetail.scheme; // if the default scheme of the object is TPM_ALG_NULL, then select the // input scheme if(keyScheme->scheme == TPM_ALG_NULL) { retVal = scheme; } // if the object scheme is not TPM_ALG_NULL and the input scheme is // TPM_ALG_NULL, then select the default scheme of the object. else if(scheme->scheme == TPM_ALG_NULL) { // if input scheme is NULL retVal = (TPMT_RSA_DECRYPT*)keyScheme; } // get here if both the object scheme and the input scheme are // not TPM_ALG_NULL. Need to insure that they are the same. // IMPLEMENTATION NOTE: This could cause problems if future versions have // schemes that have more values than just a hash algorithm. A new function // (IsSchemeSame()) might be needed then. else if(keyScheme->scheme == scheme->scheme && keyScheme->details.anySig.hashAlg == scheme->details.anySig.hashAlg) { retVal = scheme; } // two different, incompatible schemes specified will return NULL return retVal; } //*** CryptRsaLoadPrivateExponent() // This function is called to generate the private exponent of an RSA key. // Return Type: TPM_RC // TPM_RC_BINDING public and private parts of 'rsaKey' are not matched TPM_RC CryptRsaLoadPrivateExponent(TPMT_PUBLIC* publicArea, TPMT_SENSITIVE* sensitive) { // if((sensitive->sensitive.rsa.t.size & RSA_prime_flag) == 0) { if((sensitive->sensitive.rsa.t.size * 2) == publicArea->unique.rsa.t.size) { NEW_PRIVATE_EXPONENT(Z); CRYPT_RSA_INITIALIZED(bnN, &publicArea->unique.rsa); CRYPT_RSA_VAR(bnQr); CRYPT_INT_VAR(bnE, RADIX_BITS); TPM_DO_SELF_TEST(TPM_ALG_NULL); GOTO_ERROR_UNLESS((sensitive->sensitive.rsa.t.size * 2) == publicArea->unique.rsa.t.size); // Initialize the exponent ExtMath_SetWord(bnE, publicArea->parameters.rsaDetail.exponent); if(ExtMath_IsZero(bnE)) ExtMath_SetWord(bnE, RSA_DEFAULT_PUBLIC_EXPONENT); // Convert first prime to 2B GOTO_ERROR_UNLESS( TpmMath_IntFrom2B(Z->P, &sensitive->sensitive.rsa.b) != NULL); // Find the second prime by division. This uses 'bQ' rather than Z->Q // because the division could make the quotient larger than a prime during // some intermediate step. GOTO_ERROR_UNLESS(ExtMath_Divide(Z->Q, bnQr, bnN, Z->P)); GOTO_ERROR_UNLESS(ExtMath_IsZero(bnQr)); // Compute the private exponent and return it if found GOTO_ERROR_UNLESS(ComputePrivateExponent(bnE, Z)); GOTO_ERROR_UNLESS(PackExponent(&sensitive->sensitive.rsa, Z)); } else GOTO_ERROR_UNLESS(((sensitive->sensitive.rsa.t.size / 5) * 2) == publicArea->unique.rsa.t.size); sensitive->sensitive.rsa.t.size |= RSA_prime_flag; } return TPM_RC_SUCCESS; Error: return TPM_RC_BINDING; } //*** CryptRsaEncrypt() // This is the entry point for encryption using RSA. Encryption is // use of the public exponent. The padding parameter determines what // padding will be used. // // The 'cOutSize' parameter must be at least as large as the size of the key. // // If the padding is RSA_PAD_NONE, 'dIn' is treated as a number. It must be // lower in value than the key modulus. // NOTE: If dIn has fewer bytes than cOut, then we don't add low-order zeros to // dIn to make it the size of the RSA key for the call to RSAEP. This is // because the high order bytes of dIn might have a numeric value that is // greater than the value of the key modulus. If this had low-order zeros // added, it would have a numeric value larger than the modulus even though // it started out with a lower numeric value. // // Return Type: TPM_RC // TPM_RC_VALUE 'cOutSize' is too small (must be the size // of the modulus) // TPM_RC_SCHEME 'padType' is not a supported scheme // LIB_EXPORT TPM_RC CryptRsaEncrypt( TPM2B_PUBLIC_KEY_RSA* cOut, // OUT: the encrypted data TPM2B* dIn, // IN: the data to encrypt OBJECT* key, // IN: the key used for encryption TPMT_RSA_DECRYPT* scheme, // IN: the type of padding and hash // if needed const TPM2B* label, // IN: in case it is needed RAND_STATE* rand // IN: random number generator // state (mostly for testing) ) { TPM_RC retVal = TPM_RC_SUCCESS; TPM2B_PUBLIC_KEY_RSA dataIn; // // if the input and output buffers are the same, copy the input to a scratch // buffer so that things don't get messed up. if(dIn == &cOut->b) { MemoryCopy2B(&dataIn.b, dIn, sizeof(dataIn.t.buffer)); dIn = &dataIn.b; } // All encryption schemes return the same size of data cOut->t.size = key->publicArea.unique.rsa.t.size; TPM_DO_SELF_TEST(scheme->scheme); switch(scheme->scheme) { case TPM_ALG_NULL: // 'raw' encryption { INT32 i; INT32 dSize = dIn->size; // dIn can have more bytes than cOut as long as the extra bytes // are zero. Note: the more significant bytes of a number in a byte // buffer are the bytes at the start of the array. for(i = 0; (i < dSize) && (dIn->buffer[i] == 0); i++) ; dSize -= i; if(dSize > cOut->t.size) ERROR_EXIT(TPM_RC_VALUE); // Pad cOut with zeros if dIn is smaller memset(cOut->t.buffer, 0, cOut->t.size - dSize); // And copy the rest of the value memcpy(&cOut->t.buffer[cOut->t.size - dSize], &dIn->buffer[i], dSize); // If the size of dIn is the same as cOut dIn could be larger than // the modulus. If it is, then RSAEP() will catch it. } break; case TPM_ALG_RSAES: retVal = RSAES_PKCS1v1_5Encode(&cOut->b, dIn, rand); break; case TPM_ALG_OAEP: retVal = OaepEncode(&cOut->b, scheme->details.oaep.hashAlg, label, dIn, rand); break; default: ERROR_EXIT(TPM_RC_SCHEME); break; } // All the schemes that do padding will come here for the encryption step // Check that the Encoding worked if(retVal == TPM_RC_SUCCESS) // Padding OK so do the encryption retVal = RSAEP(&cOut->b, key); Exit: return retVal; } //*** CryptRsaDecrypt() // This is the entry point for decryption using RSA. Decryption is // use of the private exponent. The 'padType' parameter determines what // padding was used. // // Return Type: TPM_RC // TPM_RC_SIZE 'cInSize' is not the same as the size of the public // modulus of 'key'; or numeric value of the encrypted // data is greater than the modulus // TPM_RC_VALUE 'dOutSize' is not large enough for the result // TPM_RC_SCHEME 'padType' is not supported // LIB_EXPORT TPM_RC CryptRsaDecrypt( TPM2B* dOut, // OUT: the decrypted data TPM2B* cIn, // IN: the data to decrypt OBJECT* key, // IN: the key to use for decryption TPMT_RSA_DECRYPT* scheme, // IN: the padding scheme const TPM2B* label // IN: in case it is needed for the scheme ) { TPM_RC retVal; // Make sure that the necessary parameters are provided pAssert(cIn != NULL && dOut != NULL && key != NULL); // Size is checked to make sure that the encrypted value is the right size if(cIn->size != key->publicArea.unique.rsa.t.size) ERROR_EXIT(TPM_RC_SIZE); TPM_DO_SELF_TEST(scheme->scheme); // For others that do padding, do the decryption in place and then // go handle the decoding. retVal = RSADP(cIn, key); if(retVal == TPM_RC_SUCCESS) { // Remove padding switch(scheme->scheme) { case TPM_ALG_NULL: if(dOut->size < cIn->size) return TPM_RC_VALUE; MemoryCopy2B(dOut, cIn, dOut->size); break; case TPM_ALG_RSAES: retVal = RSAES_Decode(dOut, cIn); break; case TPM_ALG_OAEP: retVal = OaepDecode(dOut, scheme->details.oaep.hashAlg, label, cIn); break; default: retVal = TPM_RC_SCHEME; break; } } Exit: return retVal; } //*** CryptRsaSign() // This function is used to generate an RSA signature of the type indicated in // 'scheme'. // // Return Type: TPM_RC // TPM_RC_SCHEME 'scheme' or 'hashAlg' are not supported // TPM_RC_VALUE 'hInSize' does not match 'hashAlg' (for RSASSA) // LIB_EXPORT TPM_RC CryptRsaSign(TPMT_SIGNATURE* sigOut, OBJECT* key, // IN: key to use TPM2B_DIGEST* hIn, // IN: the digest to sign RAND_STATE* rand // IN: the random number generator // to use (mostly for testing) ) { TPM_RC retVal = TPM_RC_SUCCESS; UINT16 modSize; // parameter checks pAssert(sigOut != NULL && key != NULL && hIn != NULL); modSize = key->publicArea.unique.rsa.t.size; // for all non-null signatures, the size is the size of the key modulus sigOut->signature.rsapss.sig.t.size = modSize; TPM_DO_SELF_TEST(sigOut->sigAlg); switch(sigOut->sigAlg) { case TPM_ALG_NULL: sigOut->signature.rsapss.sig.t.size = 0; return TPM_RC_SUCCESS; case TPM_ALG_RSAPSS: retVal = PssEncode(&sigOut->signature.rsapss.sig.b, sigOut->signature.rsapss.hash, &hIn->b, rand); break; case TPM_ALG_RSASSA: retVal = RSASSA_Encode(&sigOut->signature.rsassa.sig.b, sigOut->signature.rsassa.hash, &hIn->b); break; default: retVal = TPM_RC_SCHEME; } if(retVal == TPM_RC_SUCCESS) { // Do the encryption using the private key retVal = RSADP(&sigOut->signature.rsapss.sig.b, key); } return retVal; } //*** CryptRsaValidateSignature() // This function is used to validate an RSA signature. If the signature is valid // TPM_RC_SUCCESS is returned. If the signature is not valid, TPM_RC_SIGNATURE is // returned. Other return codes indicate either parameter problems or fatal errors. // // Return Type: TPM_RC // TPM_RC_SIGNATURE the signature does not check // TPM_RC_SCHEME unsupported scheme or hash algorithm // LIB_EXPORT TPM_RC CryptRsaValidateSignature( TPMT_SIGNATURE* sig, // IN: signature OBJECT* key, // IN: public modulus TPM2B_DIGEST* digest // IN: The digest being validated ) { TPM_RC retVal; // // Fatal programming errors pAssert(key != NULL && sig != NULL && digest != NULL); switch(sig->sigAlg) { case TPM_ALG_RSAPSS: case TPM_ALG_RSASSA: break; default: return TPM_RC_SCHEME; } // Errors that might be caused by calling parameters if(sig->signature.rsassa.sig.t.size != key->publicArea.unique.rsa.t.size) ERROR_EXIT(TPM_RC_SIGNATURE); TPM_DO_SELF_TEST(sig->sigAlg); // Decrypt the block retVal = RSAEP(&sig->signature.rsassa.sig.b, key); if(retVal == TPM_RC_SUCCESS) { switch(sig->sigAlg) { case TPM_ALG_RSAPSS: retVal = PssDecode(sig->signature.any.hashAlg, &digest->b, &sig->signature.rsassa.sig.b); break; case TPM_ALG_RSASSA: retVal = RSASSA_Decode(sig->signature.any.hashAlg, &digest->b, &sig->signature.rsassa.sig.b); break; default: return TPM_RC_SCHEME; } } Exit: return (retVal != TPM_RC_SUCCESS) ? TPM_RC_SIGNATURE : TPM_RC_SUCCESS; } # if SIMULATION && USE_RSA_KEY_CACHE extern int s_rsaKeyCacheEnabled; int GetCachedRsaKey( TPMT_PUBLIC* publicArea, TPMT_SENSITIVE* sensitive, RAND_STATE* rand); # define GET_CACHED_KEY(publicArea, sensitive, rand) \ (s_rsaKeyCacheEnabled && GetCachedRsaKey(publicArea, sensitive, rand)) # else # define GET_CACHED_KEY(key, rand) # endif //*** CryptRsaGenerateKey() // Generate an RSA key from a provided seed /*(See part 1 specification) // The formulation is: // KDFa(hash, seed, label, Name, Counter, bits) // Where: // hash the nameAlg from the public template // seed a seed (will be a primary seed for a primary key) // label a distinguishing label including vendor ID and // vendor-assigned part number for the TPM. // Name the nameAlg from the template and the hash of the template // using nameAlg. // Counter a 32-bit integer that is incremented each time the KDF is // called in order to produce a specific key. This value // can be a 32-bit integer in host format and does not need // to be put in canonical form. // bits the number of bits needed for the key. // The following process is implemented to find a RSA key pair: // 1. pick a random number with enough bits from KDFa as a prime candidate // 2. set the first two significant bits and the least significant bit of the // prime candidate // 3. check if the number is a prime. if not, pick another random number // 4. Make sure the difference between the two primes are more than 2^104. // Otherwise, restart the process for the second prime // 5. If the counter has reached its maximum but we still can not find a valid // RSA key pair, return an internal error. This is an artificial bound. // Other implementation may choose a smaller number to indicate how many // times they are willing to try. */ // Return Type: TPM_RC // TPM_RC_CANCELED operation was canceled // TPM_RC_RANGE public exponent is not supported // TPM_RC_VALUE could not find a prime using the provided parameters LIB_EXPORT TPM_RC CryptRsaGenerateKey( TPMT_PUBLIC* publicArea, TPMT_SENSITIVE* sensitive, RAND_STATE* rand // IN: if not NULL, the deterministic // RNG state ) { UINT32 i; CRYPT_RSA_VAR(bnD); CRYPT_RSA_VAR(bnN); CRYPT_INT_WORD(bnPubExp); UINT32 e = publicArea->parameters.rsaDetail.exponent; int keySizeInBits; TPM_RC retVal = TPM_RC_NO_RESULT; NEW_PRIVATE_EXPONENT(Z); // // Need to make sure that the caller did not specify an exponent that is // not supported e = publicArea->parameters.rsaDetail.exponent; if(e == 0) e = RSA_DEFAULT_PUBLIC_EXPONENT; else { if(e < 65537) ERROR_EXIT(TPM_RC_RANGE); // Check that e is prime if(!IsPrimeInt(e)) ERROR_EXIT(TPM_RC_RANGE); } ExtMath_SetWord(bnPubExp, e); // check for supported key size. keySizeInBits = publicArea->parameters.rsaDetail.keyBits; if(((keySizeInBits % 1024) != 0) || (keySizeInBits > MAX_RSA_KEY_BITS) // this might be redundant, but... || (keySizeInBits == 0)) ERROR_EXIT(TPM_RC_VALUE); // Set the prime size for instrumentation purposes INSTRUMENT_SET(PrimeIndex, PRIME_INDEX(keySizeInBits / 2)); # if SIMULATION && USE_RSA_KEY_CACHE if(GET_CACHED_KEY(publicArea, sensitive, rand)) return TPM_RC_SUCCESS; # endif // Make sure that key generation has been tested TPM_DO_SELF_TEST(TPM_ALG_NULL); // The prime is computed in P. When a new prime is found, Q is checked to // see if it is zero. If so, P is copied to Q and a new P is found. // When both P and Q are non-zero, the modulus and // private exponent are computed and a trial encryption/decryption is // performed. If the encrypt/decrypt fails, assume that at least one of the // primes is composite. Since we don't know which one, set Q to zero and start // over and find a new pair of primes. for(i = 1; (retVal == TPM_RC_NO_RESULT) && (i != 100); i++) { if(_plat__IsCanceled()) ERROR_EXIT(TPM_RC_CANCELED); if(TpmRsa_GeneratePrimeForRSA(Z->P, keySizeInBits / 2, e, rand) == TPM_RC_FAILURE) { retVal = TPM_RC_FAILURE; goto Exit; } INSTRUMENT_INC(PrimeCounts[PrimeIndex]); // If this is the second prime, make sure that it differs from the // first prime by at least 2^100 if(ExtMath_IsZero(Z->Q)) { // copy p to q and compute another prime in p ExtMath_Copy(Z->Q, Z->P); continue; } // Make sure that the difference is at least 100 bits. Need to do it this // way because the big numbers are only positive values if(ExtMath_UnsignedCmp(Z->P, Z->Q) < 0) ExtMath_Subtract(bnD, Z->Q, Z->P); else ExtMath_Subtract(bnD, Z->P, Z->Q); if(ExtMath_MostSigBitNum(bnD) < 100) continue; //Form the public modulus and set the unique value ExtMath_Multiply(bnN, Z->P, Z->Q); TpmMath_IntTo2B( bnN, &publicArea->unique.rsa.b, (NUMBYTES)BITS_TO_BYTES(keySizeInBits)); // Make sure everything came out right. The MSb of the values must be one if(((publicArea->unique.rsa.t.buffer[0] & 0x80) == 0) || (publicArea->unique.rsa.t.size != (NUMBYTES)BITS_TO_BYTES(keySizeInBits))) FAIL(FATAL_ERROR_INTERNAL); // Make sure that we can form the private exponent values if(ComputePrivateExponent(bnPubExp, Z) != TRUE) { // If ComputePrivateExponent could not find an inverse for // Q, then copy P and recompute P. This might // cause both to be recomputed if P is also zero if(ExtMath_IsZero(Z->Q)) ExtMath_Copy(Z->Q, Z->P); continue; } // Pack the private exponent into the sensitive area PackExponent(&sensitive->sensitive.rsa, Z); // Make sure everything came out right. The MSb of the values must be one if(((publicArea->unique.rsa.t.buffer[0] & 0x80) == 0) || ((sensitive->sensitive.rsa.t.buffer[0] & 0x80) == 0)) FAIL(FATAL_ERROR_INTERNAL); retVal = TPM_RC_SUCCESS; // Do a trial encryption decryption if this is a signing key if(IS_ATTRIBUTE(publicArea->objectAttributes, TPMA_OBJECT, sign)) { CRYPT_RSA_VAR(temp1); CRYPT_RSA_VAR(temp2); TpmMath_GetRandomInRange(temp1, bnN, rand); // Encrypt with public exponent... ExtMath_ModExp(temp2, temp1, bnPubExp, bnN); // ... then decrypt with private exponent RsaPrivateKeyOp(temp2, Z); // If the starting and ending values are not the same, // start over )-; if(ExtMath_UnsignedCmp(temp2, temp1) != 0) { ExtMath_SetWord(Z->Q, 0); retVal = TPM_RC_NO_RESULT; } } } Exit: return retVal; } #endif // ALG_RSA