/* Microsoft Reference Implementation for TPM 2.0 * * The copyright in this software is being made available under the BSD License, * included below. This software may be subject to other third party and * contributor rights, including patent rights, and no such rights are granted * under this license. * * Copyright (c) Microsoft Corporation * * All rights reserved. * * BSD License * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list * of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this * list of conditions and the following disclaimer in the documentation and/or * other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS"" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ //** 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" #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 ) { bigNum *bn = (bigNum *)&Z->P; int i; // for(i = 0; i < 5; i++) { bn[i] = (bigNum)&Z->entries[i]; BnInit(bn[i], BYTES_TO_CRYPT_WORDS(sizeof(Z->entries[0].d))); } 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(BnUnsignedCmp(Z->P, Z->Q) < 0) { bigNum 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(BnMsb(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(!BnToBytes((bigNum)&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; bigNum *bn = &Z->P; // VERIFY(b->t.size & RSA_prime_flag); RsaInitializeExponent(Z); VERIFY((primeSize % 5) == 0); primeSize /= 5; for(i = 0; i < 5; i++) VERIFY(BnFromBytes(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( bigNum 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; BN_PRIME(pT); // // make p the larger value so that m2 is always less than p MakePgreaterThanQ(Z); //dP = (1/e) mod (p-1) pOK = BnSubWord(pT, Z->P, 1); pOK = pOK && BnModInverse(Z->dP, pubExp, pT); //dQ = (1/e) mod (q-1) qOK = BnSubWord(pT, Z->Q, 1); qOK = qOK && BnModInverse(Z->dQ, pubExp, pT); // qInv = (1/q) mod p if(pOK && qOK) pOK = qOK = BnModInverse(Z->qInv, Z->Q, Z->P); if(!pOK) BnSetWord(Z->P, 0); if(!qOK) BnSetWord(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( bigNum inOut, // IN/OUT: number to be exponentiated privateExponent *Z ) { BN_RSA(M1); BN_RSA(M2); BN_RSA(M); BN_RSA(H); // MakePgreaterThanQ(Z); // m1 = cdP mod p VERIFY(BnModExp(M1, inOut, Z->dP, Z->P)); // m2 = cdQ mod q VERIFY(BnModExp(M2, inOut, Z->dQ, Z->Q)); // h = qInv * (m1 - m2) mod p = qInv * (m1 + P - m2) mod P because Q < P // so m2 < P VERIFY(BnSub(H, Z->P, M2)); VERIFY(BnAdd(H, H, M1)); VERIFY(BnModMult(H, H, Z->qInv, Z->P)); // m = m2 + h * q VERIFY(BnMult(M, H, Z->Q)); VERIFY(BnAdd(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 ) { BN_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; } VERIFY(UnpackExponent(&key->sensitive.sensitive.rsa, Z)); VERIFY(RsaPrivateKeyOp(bnM, Z)); VERIFY(BnTo2B(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_RETURN(TPM_RC_HASH); // and that message will fit messageSize <= k - 2hLen - 2 if(message->size > (padded->size - (2 * hLen) - 2)) ERROR_RETURN(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_RETURN(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; // If testing, then use the provided seed. Otherwise, use values // from the RNG CryptRandomGenerate(hLen, mySeed); DRBG_Generate(rand, mySeed, (UINT16)hLen); if(g_inFailureMode) ERROR_RETURN(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_RETURN(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_RETURN(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_RETURN(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_RETURN(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_RETURN(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 VERIFY(info != NULL); oidSize = 2 + (info->OID)[1]; // make sure this fits in the buffer VERIFY(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_RETURN(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_RETURN(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_RETURN(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_RETURN(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); BN_RSA_INITIALIZED(bnN, &publicArea->unique.rsa); BN_RSA(bnQr); BN_VAR(bnE, RADIX_BITS); TEST(TPM_ALG_NULL); VERIFY((sensitive->sensitive.rsa.t.size * 2) == publicArea->unique.rsa.t.size); // Initialize the exponent BnSetWord(bnE, publicArea->parameters.rsaDetail.exponent); if(BnEqualZero(bnE)) BnSetWord(bnE, RSA_DEFAULT_PUBLIC_EXPONENT); // Convert first prime to 2B VERIFY(BnFrom2B(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. VERIFY(BnDiv(Z->Q, bnQr, bnN, Z->P)); VERIFY(BnEqualZero(bnQr)); // Compute the private exponent and return it if found VERIFY(ComputePrivateExponent(bnE, Z)); VERIFY(PackExponent(&sensitive->sensitive.rsa, Z)); } else VERIFY(((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; 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_RETURN(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_RETURN(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_RETURN(TPM_RC_SIZE); 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; 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_RETURN(TPM_RC_SIGNATURE); 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; BN_RSA(bnD); BN_RSA(bnN); BN_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_RETURN(TPM_RC_RANGE); // Check that e is prime if(!IsPrimeInt(e)) ERROR_RETURN(TPM_RC_RANGE); } BnSetWord(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_RETURN(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 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_RETURN(TPM_RC_CANCELED); if(BnGeneratePrimeForRSA(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(BnEqualZero(Z->Q)) { // copy p to q and compute another prime in p BnCopy(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(BnUnsignedCmp(Z->P, Z->Q) < 0) BnSub(bnD, Z->Q, Z->P); else BnSub(bnD, Z->P, Z->Q); if(BnMsb(bnD) < 100) continue; //Form the public modulus and set the unique value BnMult(bnN, Z->P, Z->Q); BnTo2B(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(BnEqualZero(Z->Q)) BnCopy(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)) { BN_RSA(temp1); BN_RSA(temp2); BnGenerateRandomInRange(temp1, bnN, rand); // Encrypt with public exponent... BnModExp(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(BnUnsignedCmp(temp2, temp1) != 0) { BnSetWord(Z->Q, 0); retVal = TPM_RC_NO_RESULT; } } } Exit: return retVal; } #endif // ALG_RSA