// 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 <string.h> #include "OsslCryptoEngine.h" #ifdef TPM_ALG_RSA // // // Local Functions // // RsaPrivateExponent() // // This function computes the private exponent de = 1 mod (p-1)*(q-1) The inputs are the public modulus // and one of the primes. // The results are returned in the key->private structure. The size of that structure is expanded to hold the // private exponent. If the computed value is smaller than the public modulus, the private exponent is de- // normalized. // // Return Value Meaning // // CRYPT_SUCCESS private exponent computed // CRYPT_PARAMETER prime is not half the size of the modulus, or the modulus is not evenly // divisible by the prime, or no private exponent could be computed // from the input parameters // CRYPT_RESULT RsaPrivateExponent( RSA_KEY *key // IN: the key to augment with the private // exponent ) { BN_CTX *context; BIGNUM *bnD; BIGNUM *bnN; BIGNUM *bnP; BIGNUM *bnE; BIGNUM *bnPhi; BIGNUM *bnQ; BIGNUM *bnQr; UINT32 fill; CRYPT_RESULT retVal = CRYPT_SUCCESS; // Assume success pAssert(key != NULL && key->privateKey != NULL && key->publicKey != NULL); context = BN_CTX_new(); if(context == NULL) FAIL(FATAL_ERROR_ALLOCATION); BN_CTX_start(context); bnE = BN_CTX_get(context); bnD = BN_CTX_get(context); bnN = BN_CTX_get(context); bnP = BN_CTX_get(context); bnPhi = BN_CTX_get(context); bnQ = BN_CTX_get(context); bnQr = BN_CTX_get(context); if(bnQr == NULL) FAIL(FATAL_ERROR_ALLOCATION); // Assume the size of the public key value is within range pAssert(key->publicKey->size <= MAX_RSA_KEY_BYTES); if( BN_bin2bn(key->publicKey->buffer, key->publicKey->size, bnN) == NULL || BN_bin2bn(key->privateKey->buffer, key->privateKey->size, bnP) == NULL) FAIL(FATAL_ERROR_INTERNAL); // If P size is not 1/2 of n size, then this is not a valid value for this // implementation. This will also catch the case were P is input as zero. // This generates a return rather than an assert because the key being loaded // might be SW generated and wrong. if(BN_num_bits(bnP) < BN_num_bits(bnN)/2) { retVal = CRYPT_PARAMETER; goto Cleanup; } // Get q = n/p; if (BN_div(bnQ, bnQr, bnN, bnP, context) != 1) FAIL(FATAL_ERROR_INTERNAL); // If there is a remainder, then this is not a valid n if(BN_num_bytes(bnQr) != 0 || BN_num_bits(bnQ) != BN_num_bits(bnP)) { retVal = CRYPT_PARAMETER; // problem may be recoverable goto Cleanup; } // Get compute Phi = (p - 1)(q - 1) = pq - p - q + 1 = n - p - q + 1 if( BN_copy(bnPhi, bnN) == NULL || !BN_sub(bnPhi, bnPhi, bnP) || !BN_sub(bnPhi, bnPhi, bnQ) || !BN_add_word(bnPhi, 1)) FAIL(FATAL_ERROR_INTERNAL); // Compute the multiplicative inverse BN_set_word(bnE, key->exponent); if(BN_mod_inverse(bnD, bnE, bnPhi, context) == NULL) { // Going to assume that the error is caused by a bad // set of parameters. Specifically, an exponent that is // not compatible with the primes. In an implementation that // has better visibility to the error codes, this might be // refined so that failures in the library would return // a more informative value. Should not assume here that // the error codes will remain unchanged. retVal = CRYPT_PARAMETER; goto Cleanup; } fill = key->publicKey->size - BN_num_bytes(bnD); BN_bn2bin(bnD, &key->privateKey->buffer[fill]); memset(key->privateKey->buffer, 0, fill); // Change the size of the private key so that it is known to contain // a private exponent rather than a prime. key->privateKey->size = key->publicKey->size; Cleanup: BN_CTX_end(context); BN_CTX_free(context); return retVal; } // // // _cpri__TestKeyRSA() // // This function computes the private exponent de = 1 mod (p-1)*(q-1) The inputs are the public modulus // and one of the primes or two primes. // If both primes are provided, the public modulus is computed. If only one prime is provided, the second // prime is computed. In either case, a private exponent is produced and placed in d. // If no modular inverse exists, then CRYPT_PARAMETER is returned. // // Return Value Meaning // // CRYPT_SUCCESS private exponent (d) was generated // CRYPT_PARAMETER one or more parameters are invalid // LIB_EXPORT CRYPT_RESULT _cpri__TestKeyRSA( TPM2B *d, // OUT: the address to receive the private // exponent UINT32 exponent, // IN: the public modulu TPM2B *publicKey, // IN/OUT: an input if only one prime is // provided. an output if both primes are // provided TPM2B *prime1, // IN: a first prime TPM2B *prime2 // IN: an optional second prime ) { BN_CTX *context; BIGNUM *bnD; BIGNUM *bnN; BIGNUM *bnP; BIGNUM *bnE; BIGNUM *bnPhi; BIGNUM *bnQ; BIGNUM *bnQr; UINT32 fill; CRYPT_RESULT retVal = CRYPT_SUCCESS; // Assume success pAssert(publicKey != NULL && prime1 != NULL); // Make sure that the sizes are within range pAssert( prime1->size <= MAX_RSA_KEY_BYTES/2 && publicKey->size <= MAX_RSA_KEY_BYTES); pAssert( prime2 == NULL || prime2->size < MAX_RSA_KEY_BYTES/2); if(publicKey->size/2 != prime1->size) return CRYPT_PARAMETER; context = BN_CTX_new(); if(context == NULL) FAIL(FATAL_ERROR_ALLOCATION); BN_CTX_start(context); bnE = BN_CTX_get(context); // public exponent (e) bnD = BN_CTX_get(context); // private exponent (d) bnN = BN_CTX_get(context); // public modulus (n) bnP = BN_CTX_get(context); // prime1 (p) bnPhi = BN_CTX_get(context); // (p-1)(q-1) bnQ = BN_CTX_get(context); // prime2 (q) bnQr = BN_CTX_get(context); // n mod p if(bnQr == NULL) FAIL(FATAL_ERROR_ALLOCATION); if(BN_bin2bn(prime1->buffer, prime1->size, bnP) == NULL) FAIL(FATAL_ERROR_INTERNAL); // If prime2 is provided, then compute n if(prime2 != NULL) { // Two primes provided so use them to compute n if(BN_bin2bn(prime2->buffer, prime2->size, bnQ) == NULL) FAIL(FATAL_ERROR_INTERNAL); // Make sure that the sizes of the primes are compatible if(BN_num_bits(bnQ) != BN_num_bits(bnP)) { retVal = CRYPT_PARAMETER; goto Cleanup; } // Multiply the primes to get the public modulus if(BN_mul(bnN, bnP, bnQ, context) != 1) FAIL(FATAL_ERROR_INTERNAL); // if the space provided for the public modulus is large enough, // save the created value if(BN_num_bits(bnN) != (publicKey->size * 8)) { retVal = CRYPT_PARAMETER; goto Cleanup; } BN_bn2bin(bnN, publicKey->buffer); } else { // One prime provided so find the second prime by division BN_bin2bn(publicKey->buffer, publicKey->size, bnN); // Get q = n/p; if(BN_div(bnQ, bnQr, bnN, bnP, context) != 1) FAIL(FATAL_ERROR_INTERNAL); // If there is a remainder, then this is not a valid n if(BN_num_bytes(bnQr) != 0 || BN_num_bits(bnQ) != BN_num_bits(bnP)) { retVal = CRYPT_PARAMETER; // problem may be recoverable goto Cleanup; } } // Get compute Phi = (p - 1)(q - 1) = pq - p - q + 1 = n - p - q + 1 BN_copy(bnPhi, bnN); BN_sub(bnPhi, bnPhi, bnP); BN_sub(bnPhi, bnPhi, bnQ); BN_add_word(bnPhi, 1); // Compute the multiplicative inverse BN_set_word(bnE, exponent); if(BN_mod_inverse(bnD, bnE, bnPhi, context) == NULL) { // Going to assume that the error is caused by a bad set of parameters. // Specifically, an exponent that is not compatible with the primes. // In an implementation that has better visibility to the error codes, // this might be refined so that failures in the library would return // a more informative value. // Do not assume that the error codes will remain unchanged. retVal = CRYPT_PARAMETER; goto Cleanup; } // Return the private exponent. // Make sure it is normalized to have the correct size. d->size = publicKey->size; fill = d->size - BN_num_bytes(bnD); BN_bn2bin(bnD, &d->buffer[fill]); memset(d->buffer, 0, fill); Cleanup: BN_CTX_end(context); BN_CTX_free(context); return retVal; } // // // 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 Value Meaning // // CRYPT_SUCCESS encryption complete // CRYPT_PARAMETER number to exponentiate is larger than the modulus // static CRYPT_RESULT RSAEP ( UINT32 dInOutSize, // OUT size of the encrypted block BYTE *dInOut, // OUT: the encrypted data RSA_KEY *key // IN: the key to use ) { UINT32 e; BYTE exponent[4]; CRYPT_RESULT retVal; e = key->exponent; if(e == 0) e = RSA_DEFAULT_PUBLIC_EXPONENT; UINT32_TO_BYTE_ARRAY(e, exponent); //!!! Can put check for test of RSA here retVal = _math__ModExp(dInOutSize, dInOut, dInOutSize, dInOut, 4, exponent, key->publicKey->size, key->publicKey->buffer); // Exponentiation result is stored in-place, thus no space shortage is possible. pAssert(retVal != CRYPT_UNDERFLOW); return retVal; } // // // 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 Value Meaning // // CRYPT_SUCCESS decryption succeeded // CRYPT_PARAMETER the value to decrypt is larger than the modulus // static CRYPT_RESULT RSADP ( UINT32 dInOutSize, // IN/OUT: size of decrypted data BYTE *dInOut, // IN/OUT: the decrypted data RSA_KEY *key // IN: the key ) { CRYPT_RESULT retVal; //!!! Can put check for RSA tested here // Make sure that the pointers are provided and that the private key is present // If the private key is present it is assumed to have been created by // so is presumed good _cpri__PrivateExponent pAssert(key != NULL && dInOut != NULL && key->publicKey->size == key->publicKey->size); // make sure that the value to be decrypted is smaller than the modulus // note: this check is redundant as is also performed by _math__ModExp() // which is optimized for use in RSA operations if(_math__uComp(key->publicKey->size, key->publicKey->buffer, dInOutSize, dInOut) <= 0) return CRYPT_PARAMETER; // _math__ModExp can return CRYPT_PARAMTER or CRYPT_UNDERFLOW but actual // underflow is not possible because everything is in the same buffer. retVal = _math__ModExp(dInOutSize, dInOut, dInOutSize, dInOut, key->privateKey->size, key->privateKey->buffer, key->publicKey->size, key->publicKey->buffer); // Exponentiation result is stored in-place, thus no space shortage is possible. pAssert(retVal != CRYPT_UNDERFLOW); return retVal; } // // // 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 Value Meaning // // CRYPT_SUCCESS encode successful // CRYPT_PARAMETER hashAlg is not valid // CRYPT_FAIL message size is too large // static CRYPT_RESULT OaepEncode( UINT32 paddedSize, // IN: pad value size BYTE *padded, // OUT: the pad data TPM_ALG_ID hashAlg, // IN: algorithm to use for padding const char *label, // IN: null-terminated string (may be NULL) UINT32 messageSize, // IN: the message size BYTE *message // IN: the message being padded #ifdef TEST_RSA // , BYTE *testSeed // IN: optional seed used for testing. #endif // TEST_RSA // ) { UINT32 padLen; UINT32 dbSize; UINT32 i; BYTE mySeed[MAX_DIGEST_SIZE]; BYTE *seed = mySeed; INT32 hLen = _cpri__GetDigestSize(hashAlg); BYTE mask[MAX_RSA_KEY_BYTES]; BYTE *pp; BYTE *pm; UINT32 lSize = 0; CRYPT_RESULT retVal = CRYPT_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 CRYPT_PARAMETER; // If a label is provided, get the length of the string, including the // terminator if(label != NULL) lSize = (UINT32)strlen(label) + 1; // Basic size check // messageSize <= k 2hLen 2 if(messageSize > paddedSize - 2 * hLen - 2) return CRYPT_FAIL; // Hash L even if it is null // Offset into padded leaving room for masked seed and byte of zero pp = &padded[hLen + 1]; retVal = _cpri__HashBlock(hashAlg, lSize, (BYTE *)label, hLen, pp); // concatenate PS of k mLen 2hLen 2 padLen = paddedSize - messageSize - (2 * hLen) - 2; memset(&pp[hLen], 0, padLen); pp[hLen+padLen] = 0x01; padLen += 1; memcpy(&pp[hLen+padLen], message, messageSize); // The total size of db = hLen + pad + mSize; dbSize = hLen+padLen+messageSize; // If testing, then use the provided seed. Otherwise, use values // from the RNG #ifdef TEST_RSA if(testSeed != NULL) seed = testSeed; else #endif // TEST_RSA _cpri__GenerateRandom(hLen, mySeed); // mask = MGF1 (seed, nSize hLen 1) if((retVal = _cpri__MGF1(dbSize, mask, hashAlg, hLen, seed)) < 0) return retVal; // Don't expect an error because hash size is not zero // was detected in the call to _cpri__HashBlock() above. // Create the masked db pm = mask; for(i = dbSize; i > 0; i--) *pp++ ^= *pm++; pp = &padded[hLen + 1]; // Run the masked data through MGF1 if((retVal = _cpri__MGF1(hLen, &padded[1], hashAlg, dbSize, pp)) < 0) return retVal; // Don't expect zero here as the only case for zero // was detected in the call to _cpri__HashBlock() above. // Now XOR the seed to create masked seed pp = &padded[1]; pm = seed; for(i = hLen; i > 0; i--) *pp++ ^= *pm++; // Set the first byte to zero *padded = 0x00; return CRYPT_SUCCESS; } // // // 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 CRYPT_NO_RESULTS. // 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 CRYPT_FAIL. // // Return Value Meaning // // CRYPT_SUCCESS decode complete // CRYPT_PARAMETER the value to decode was larger than the modulus // CRYPT_FAIL the padding is wrong or the buffer to receive the results is too small // static CRYPT_RESULT OaepDecode( UINT32 *dataOutSize, // IN/OUT: the recovered data size BYTE *dataOut, // OUT: the recovered data TPM_ALG_ID hashAlg, // IN: algorithm to use for padding const char *label, // IN: null-terminated string (may be NULL) UINT32 paddedSize, // IN: the size of the padded data BYTE *padded // IN: the padded data ) { UINT32 dSizeSave; UINT32 i; BYTE seedMask[MAX_DIGEST_SIZE]; INT32 hLen = _cpri__GetDigestSize(hashAlg); BYTE mask[MAX_RSA_KEY_BYTES]; BYTE *pp; BYTE *pm; UINT32 lSize = 0; CRYPT_RESULT retVal = CRYPT_SUCCESS; // Unknown hash pAssert(hLen > 0 && dataOutSize != NULL && dataOut != NULL && padded != NULL); // If there is a label, get its size including the terminating 0x00 if(label != NULL) lSize = (UINT32)strlen(label) + 1; // Set the return size to zero so that it doesn't have to be done on each // failure dSizeSave = *dataOutSize; *dataOutSize = 0; // Strange size (anything smaller can't be an OAEP padded block) // Also check for no leading 0 if(paddedSize < (unsigned)((2 * hLen) + 2) || *padded != 0) return CRYPT_FAIL; // Use the hash size to determine what to put through MGF1 in order // to recover the seedMask if((retVal = _cpri__MGF1(hLen, seedMask, hashAlg, paddedSize-hLen-1, &padded[hLen+1])) < 0) return retVal; // Recover the seed into seedMask pp = &padded[1]; pm = seedMask; for(i = hLen; i > 0; i--) *pm++ ^= *pp++; // Use the seed to generate the data mask if((retVal = _cpri__MGF1(paddedSize-hLen-1, mask, hashAlg, hLen, seedMask)) < 0) return retVal; // Use the mask generated from seed to recover the padded data pp = &padded[hLen+1]; pm = mask; for(i = paddedSize-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((retVal=_cpri__HashBlock(hashAlg, lSize,(BYTE *)label, hLen, seedMask)) < 0) return retVal; if(memcmp(seedMask, mask, hLen) != 0) return CRYPT_FAIL; // find the start of the data pm = &mask[hLen]; for(i = paddedSize-(2*hLen)-1; i > 0; i--) { if(*pm++ != 0) break; } // Magic value in the end of the fill area must be 1, anything else must be // rejected. if (pm[-1] != 1) return CRYPT_FAIL; if(i == 0) return CRYPT_PARAMETER; // 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 > dSizeSave) { // Restore dSize *dataOutSize = dSizeSave; return CRYPT_FAIL; } memcpy(dataOut, pm, i); *dataOutSize = i; return CRYPT_SUCCESS; } // // // PKSC1v1_5Encode() // // This function performs the encoding for RSAES-PKCS1-V1_5-ENCRYPT as defined in PKCS#1V2.1 // // Return Value Meaning // // CRYPT_SUCCESS data encoded // CRYPT_PARAMETER message size is too large // static CRYPT_RESULT RSAES_PKSC1v1_5Encode( UINT32 paddedSize, // IN: pad value size BYTE *padded, // OUT: the pad data UINT32 messageSize, // IN: the message size BYTE *message // IN: the message being padded ) { UINT32 ps = paddedSize - messageSize - 3; if(messageSize > paddedSize - 11) return CRYPT_PARAMETER; // move the message to the end of the buffer memcpy(&padded[paddedSize - messageSize], message, messageSize); // Set the first byte to 0x00 and the second to 0x02 *padded = 0; padded[1] = 2; // Fill with random bytes _cpri__GenerateRandom(ps, &padded[2]); // Set the delimiter for the random field to 0 padded[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[ps] == 0) padded[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 CRYPT_SUCCESS; } // // // RSAES_Decode() // // This function performs the decoding for RSAES-PKCS1-V1_5-ENCRYPT as defined in PKCS#1V2.1 // // Return Value Meaning // // CRYPT_SUCCESS decode successful // CRYPT_FAIL decoding error or results would no fit into provided buffer // static CRYPT_RESULT RSAES_Decode( UINT32 *messageSize, // IN/OUT: recovered message size BYTE *message, // OUT: the recovered message UINT32 codedSize, // IN: the encoded message size BYTE *coded // IN: the encoded message ) { BOOL fail = FALSE; UINT32 ps; fail = (codedSize < 11); fail |= (coded[0] != 0x00) || (coded[1] != 0x02); for(ps = 2; ps < codedSize; ps++) { if(coded[ps] == 0) break; } ps++; // Make sure that ps has not gone over the end and that there are at least 8 // bytes of pad data. fail |= ((ps >= codedSize) || ((ps-2) < 8)); if((*messageSize < codedSize - ps) || fail) return CRYPT_FAIL; *messageSize = codedSize - ps; memcpy(message, &coded[ps], codedSize - ps); return CRYPT_SUCCESS; } // // // 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. // // Return Value Meaning // // CRYPT_SUCCESS encode successful // CRYPT_PARAMETER hashAlg is not a supported hash algorithm // static CRYPT_RESULT PssEncode ( UINT32 eOutSize, // IN: size of the encode data buffer BYTE *eOut, // OUT: encoded data buffer TPM_ALG_ID hashAlg, // IN: hash algorithm to use for the encoding UINT32 hashInSize, // IN: size of digest to encode BYTE *hashIn // IN: the digest #ifdef TEST_RSA // , BYTE *saltIn // IN: optional parameter for testing #endif // TEST_RSA // ) { INT32 hLen = _cpri__GetDigestSize(hashAlg); BYTE salt[MAX_RSA_KEY_BYTES - 1]; UINT16 saltSize; BYTE *ps = salt; CRYPT_RESULT retVal; UINT16 mLen; CPRI_HASH_STATE hashState; // These are fatal errors indicating bad TPM firmware pAssert(eOut != NULL && hLen > 0 && hashIn != NULL ); // Get the size of the mask mLen = (UINT16)(eOutSize - hLen - 1); // Maximum possible salt size is mask length - 1 saltSize = mLen - 1; // Use the maximum salt size allowed by FIPS 186-4 if(saltSize > hLen) saltSize = (UINT16)hLen; //using eOut for scratch space // Set the first 8 bytes to zero memset(eOut, 0, 8); // Get set the salt #ifdef TEST_RSA if(saltIn != NULL) { saltSize = hLen; memcpy(salt, saltIn, hLen); } else #endif // TEST_RSA _cpri__GenerateRandom(saltSize, salt); // Create the hash of the pad || input hash || salt _cpri__StartHash(hashAlg, FALSE, &hashState); _cpri__UpdateHash(&hashState, 8, eOut); _cpri__UpdateHash(&hashState, hashInSize, hashIn); _cpri__UpdateHash(&hashState, saltSize, salt); _cpri__CompleteHash(&hashState, hLen, &eOut[eOutSize - hLen - 1]); // Create a mask if((retVal = _cpri__MGF1(mLen, eOut, hashAlg, hLen, &eOut[mLen])) < 0) { // Currently _cpri__MGF1 is not expected to return a CRYPT_RESULT error. pAssert(0); } // 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 eOut[0] &= 0x7f; // Before we mess up the eOut value, set the last byte to 0xbc eOut[eOutSize - 1] = 0xbc; // XOR a byte of 0x01 at the position just before where the salt will be XOR'ed eOut = &eOut[mLen - saltSize - 1]; *eOut++ ^= 0x01; // XOR the salt data into the buffer for(; saltSize > 0; saltSize--) *eOut++ ^= *ps++; // and we are done return CRYPT_SUCCESS; } // // // PssDecode() // // This function checks that the PSS encoded block was built from the provided digest. If the check is // successful, CRYPT_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 Value Meaning // // CRYPT_SUCCESS decode successful // CRYPT_SCHEME hashAlg is not a supported hash algorithm // CRYPT_FAIL decode operation failed // static CRYPT_RESULT PssDecode( TPM_ALG_ID hashAlg, // IN: hash algorithm to use for the encoding UINT32 dInSize, // IN: size of the digest to compare BYTE *dIn, // In: the digest to compare UINT32 eInSize, // IN: size of the encoded data BYTE *eIn, // IN: the encoded data UINT32 saltSize // IN: the expected size of the salt ) { INT32 hLen = _cpri__GetDigestSize(hashAlg); BYTE mask[MAX_RSA_KEY_BYTES]; BYTE *pm = mask; BYTE pad[8] = {0}; UINT32 i; UINT32 mLen; BOOL fail = FALSE; CRYPT_RESULT retVal; CPRI_HASH_STATE hashState; // These errors are indicative of failures due to programmer error pAssert(dIn != NULL && eIn != NULL); // check the hash scheme if(hLen == 0) return CRYPT_SCHEME; // most significant bit must be zero fail = ((eIn[0] & 0x80) != 0); // last byte must be 0xbc fail |= (eIn[eInSize - 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 = eInSize - hLen - 1; if((retVal = _cpri__MGF1(mLen, mask, hashAlg, hLen, &eIn[mLen])) < 0) return retVal; if(retVal == 0) return CRYPT_FAIL; // Clear the MSO of the mask to make it consistent with the encoding. mask[0] &= 0x7F; // 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++ ^= *eIn++; // 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++ != 0); } 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--; } // If the salt size was provided, then the recovered size must match fail |= (saltSize != 0 && i != saltSize); // 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. _cpri__StartHash(hashAlg, FALSE, &hashState); // add the pad of 8 zeros _cpri__UpdateHash(&hashState, 8, pad); // add the provided digest value _cpri__UpdateHash(&hashState, dInSize, dIn); // and the salt _cpri__UpdateHash(&hashState, i, pm); // get the result retVal = _cpri__CompleteHash(&hashState, MAX_DIGEST_SIZE, mask); // retVal will be the size of the digest or zero. If not equal to the indicated // digest size, then the signature doesn't match fail |= (retVal != hLen); fail |= (memcmp(mask, eIn, hLen) != 0); if(fail) return CRYPT_FAIL; else return CRYPT_SUCCESS; } // // // PKSC1v1_5SignEncode() // // Encode a message using PKCS1v1().5 method. // // Return Value Meaning // // CRYPT_SUCCESS encode complete // CRYPT_SCHEME hashAlg is not a supported hash algorithm // CRYPT_PARAMETER eOutSize is not large enough or hInSize does not match the digest // size of hashAlg // static CRYPT_RESULT RSASSA_Encode( UINT32 eOutSize, // IN: the size of the resulting block BYTE *eOut, // OUT: the encoded block TPM_ALG_ID hashAlg, // IN: hash algorithm for PKSC1v1_5 UINT32 hInSize, // IN: size of hash to be signed BYTE *hIn // IN: hash buffer ) { const BYTE *der; INT32 derSize = _cpri__GetHashDER(hashAlg, &der); INT32 fillSize; pAssert(eOut != NULL && hIn != NULL); // Can't use this scheme if the algorithm doesn't have a DER string defined. if(derSize == 0 ) return CRYPT_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((unsigned)_cpri__GetDigestSize(hashAlg) != hInSize) return CRYPT_PARAMETER; fillSize = eOutSize - derSize - hInSize - 3; // Make sure that this combination will fit in the provided space if(fillSize < 8) return CRYPT_PARAMETER; // 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 for(; hInSize > 0; hInSize--) *eOut++ = *hIn++; // copy the hash return CRYPT_SUCCESS; } // // // RSASSA_Decode() // // This function performs the RSASSA decoding of a signature. // // Return Value Meaning // // CRYPT_SUCCESS decode successful // CRYPT_FAIL decode unsuccessful // CRYPT_SCHEME haslAlg is not supported // static CRYPT_RESULT RSASSA_Decode( TPM_ALG_ID hashAlg, // IN: hash algorithm to use for the encoding UINT32 hInSize, // IN: size of the digest to compare BYTE *hIn, // In: the digest to compare UINT32 eInSize, // IN: size of the encoded data BYTE *eIn // IN: the encoded data ) { BOOL fail = FALSE; const BYTE *der; INT32 derSize = _cpri__GetHashDER(hashAlg, &der); INT32 hashSize = _cpri__GetDigestSize(hashAlg); INT32 fillSize; pAssert(hIn != NULL && eIn != NULL); // 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 != hInSize) return CRYPT_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 // _cpri__ValidateSignature() so eInSize is known to be in range. fillSize = eInSize - derSize - hashSize - 3; // Start checking fail |= (*eIn++ != 0); // initial byte of zero fail |= (*eIn++ != 1); // byte of 0x01 for(; fillSize > 0; fillSize--) fail |= (*eIn++ != 0xff); // bunch of 0xff fail |= (*eIn++ != 0); // another 0 for(; derSize > 0; derSize--) fail |= (*eIn++ != *der++); // match the DER for(; hInSize > 0; hInSize--) fail |= (*eIn++ != *hIn++); // match the hash if(fail) return CRYPT_FAIL; return CRYPT_SUCCESS; } // // // Externally Accessible Functions // // _cpri__RsaStartup() // // Function that is called to initialize the hash service. In this implementation, this function does nothing but // it is called by the CryptUtilStartup() function and must be present. // LIB_EXPORT BOOL _cpri__RsaStartup( void ) { return TRUE; } // // // _cpri__EncryptRSA() // // 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 treaded 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 Value Meaning // // CRYPT_SUCCESS encryption complete // CRYPT_PARAMETER cOutSize is too small (must be the size of the modulus) // CRYPT_SCHEME padType is not a supported scheme // LIB_EXPORT CRYPT_RESULT _cpri__EncryptRSA( UINT32 *cOutSize, // OUT: the size of the encrypted data BYTE *cOut, // OUT: the encrypted data RSA_KEY *key, // IN: the key to use for encryption TPM_ALG_ID padType, // IN: the type of padding UINT32 dInSize, // IN: the amount of data to encrypt BYTE *dIn, // IN: the data to encrypt TPM_ALG_ID hashAlg, // IN: in case this is needed const char *label // IN: in case it is needed ) { CRYPT_RESULT retVal = CRYPT_SUCCESS; pAssert(cOutSize != NULL); // All encryption schemes return the same size of data if(*cOutSize < key->publicKey->size) return CRYPT_PARAMETER; *cOutSize = key->publicKey->size; switch (padType) { case TPM_ALG_NULL: // 'raw' encryption { // dIn can have more bytes than cOut as long as the extra bytes // are zero for(; dInSize > *cOutSize; dInSize--) { if(*dIn++ != 0) return CRYPT_PARAMETER; } // If dIn is smaller than cOut, fill cOut with zeros if(dInSize < *cOutSize) memset(cOut, 0, *cOutSize - dInSize); // Copy the rest of the value memcpy(&cOut[*cOutSize-dInSize], dIn, dInSize); // 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_PKSC1v1_5Encode(*cOutSize, cOut, dInSize, dIn); break; case TPM_ALG_OAEP: retVal = OaepEncode(*cOutSize, cOut, hashAlg, label, dInSize, dIn #ifdef TEST_RSA ,NULL #endif ); break; default: return CRYPT_SCHEME; } // All the schemes that do padding will come here for the encryption step // Check that the Encoding worked if(retVal != CRYPT_SUCCESS) return retVal; // Padding OK so do the encryption return RSAEP(*cOutSize, cOut, key); } // // // _cpri__DecryptRSA() // // 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 Value Meaning // // CRYPT_SUCCESS successful completion // CRYPT_PARAMETER 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 // CRYPT_FAIL dOutSize is not large enough for the result // CRYPT_SCHEME padType is not supported // LIB_EXPORT CRYPT_RESULT _cpri__DecryptRSA( UINT32 *dOutSize, // OUT: the size of the decrypted data BYTE *dOut, // OUT: the decrypted data RSA_KEY *key, // IN: the key to use for decryption TPM_ALG_ID padType, // IN: the type of padding UINT32 cInSize, // IN: the amount of data to decrypt BYTE *cIn, // IN: the data to decrypt TPM_ALG_ID hashAlg, // IN: in case this is needed for the scheme const char *label // IN: in case it is needed for the scheme ) { CRYPT_RESULT retVal; // Make sure that the necessary parameters are provided pAssert(cIn != NULL && dOut != NULL && dOutSize != NULL && key != NULL); // Size is checked to make sure that the decryption works properly if(cInSize != key->publicKey->size) return CRYPT_PARAMETER; // For others that do padding, do the decryption in place and then // go handle the decoding. if((retVal = RSADP(cInSize, cIn, key)) != CRYPT_SUCCESS) return retVal; // Decryption failed // Remove padding switch (padType) { case TPM_ALG_NULL: if(*dOutSize < key->publicKey->size) return CRYPT_FAIL; *dOutSize = key->publicKey->size; memcpy(dOut, cIn, *dOutSize); return CRYPT_SUCCESS; case TPM_ALG_RSAES: return RSAES_Decode(dOutSize, dOut, cInSize, cIn); break; case TPM_ALG_OAEP: return OaepDecode(dOutSize, dOut, hashAlg, label, cInSize, cIn); break; default: return CRYPT_SCHEME; break; } } // // // _cpri__SignRSA() // // This function is used to generate an RSA signature of the type indicated in scheme. // // Return Value Meaning // // CRYPT_SUCCESS sign operation completed normally // CRYPT_SCHEME scheme or hashAlg are not supported // CRYPT_PARAMETER hInSize does not match hashAlg (for RSASSA) // LIB_EXPORT CRYPT_RESULT _cpri__SignRSA( UINT32 *sigOutSize, // OUT: size of signature BYTE *sigOut, // OUT: signature RSA_KEY *key, // IN: key to use TPM_ALG_ID scheme, // IN: the scheme to use TPM_ALG_ID hashAlg, // IN: hash algorithm for PKSC1v1_5 UINT32 hInSize, // IN: size of digest to be signed BYTE *hIn // IN: digest buffer ) { CRYPT_RESULT retVal; // Parameter checks pAssert(sigOutSize != NULL && sigOut != NULL && key != NULL && hIn != NULL); // For all signatures the size is the size of the key modulus *sigOutSize = key->publicKey->size; switch (scheme) { case TPM_ALG_NULL: *sigOutSize = 0; return CRYPT_SUCCESS; case TPM_ALG_RSAPSS: // PssEncode can return CRYPT_PARAMETER retVal = PssEncode(*sigOutSize, sigOut, hashAlg, hInSize, hIn #ifdef TEST_RSA , NULL #endif ); break; case TPM_ALG_RSASSA: // RSASSA_Encode can return CRYPT_PARAMETER or CRYPT_SCHEME retVal = RSASSA_Encode(*sigOutSize, sigOut, hashAlg, hInSize, hIn); break; default: return CRYPT_SCHEME; } if(retVal != CRYPT_SUCCESS) return retVal; // Do the encryption using the private key // RSADP can return CRYPT_PARAMETR return RSADP(*sigOutSize,sigOut, key); } // // // _cpri__ValidateSignatureRSA() // // This function is used to validate an RSA signature. If the signature is valid CRYPT_SUCCESS is // returned. If the signature is not valid, CRYPT_FAIL is returned. Other return codes indicate either // parameter problems or fatal errors. // // Return Value Meaning // // CRYPT_SUCCESS the signature checks // CRYPT_FAIL the signature does not check // CRYPT_SCHEME unsupported scheme or hash algorithm // LIB_EXPORT CRYPT_RESULT _cpri__ValidateSignatureRSA( RSA_KEY *key, // IN: key to use TPM_ALG_ID scheme, // IN: the scheme to use TPM_ALG_ID hashAlg, // IN: hash algorithm UINT32 hInSize, // IN: size of digest to be checked BYTE *hIn, // IN: digest buffer UINT32 sigInSize, // IN: size of signature BYTE *sigIn, // IN: signature UINT16 saltSize // IN: salt size for PSS ) { CRYPT_RESULT retVal; // Fatal programming errors pAssert(key != NULL && sigIn != NULL && hIn != NULL); // Errors that might be caused by calling parameters if(sigInSize != key->publicKey->size) return CRYPT_FAIL; // Decrypt the block if((retVal = RSAEP(sigInSize, sigIn, key)) != CRYPT_SUCCESS) return CRYPT_FAIL; switch (scheme) { case TPM_ALG_NULL: return CRYPT_SCHEME; break; case TPM_ALG_RSAPSS: return PssDecode(hashAlg, hInSize, hIn, sigInSize, sigIn, saltSize); break; case TPM_ALG_RSASSA: return RSASSA_Decode(hashAlg, hInSize, hIn, sigInSize, sigIn); break; default: break; } return CRYPT_SCHEME; } #ifndef RSA_KEY_SIEVE // // // _cpri__GenerateKeyRSA() // // Generate an RSA key from a provided seed // // // // // Return Value Meaning // // CRYPT_FAIL exponent is not prime or is less than 3; or could not find a prime using // the provided parameters // CRYPT_CANCEL operation was canceled // LIB_EXPORT CRYPT_RESULT _cpri__GenerateKeyRSA( TPM2B *n, // OUT: The public modulu TPM2B *p, // OUT: One of the prime factors of n UINT16 keySizeInBits, // IN: Size of the public modulus in bit UINT32 e, // IN: The public exponent TPM_ALG_ID hashAlg, // IN: hash algorithm to use in the key // generation proce TPM2B *seed, // IN: the seed to use const char *label, // IN: A label for the generation process. TPM2B *extra, // IN: Party 1 data for the KDF UINT32 *counter // IN/OUT: Counter value to allow KFD iteration // to be propagated across multiple routine ) { UINT32 lLen; // length of the label // (counting the terminating 0); UINT16 digestSize = _cpri__GetDigestSize(hashAlg); TPM2B_HASH_BLOCK oPadKey; UINT32 outer; UINT32 inner; BYTE swapped[4]; CRYPT_RESULT retVal; int i, fill; const static char defaultLabel[] = "RSA key"; BYTE *pb; CPRI_HASH_STATE h1; // contains the hash of the // HMAC key w/ iPad CPRI_HASH_STATE h2; // contains the hash of the // HMAC key w/ oPad CPRI_HASH_STATE h; // the working hash context BIGNUM *bnP; BIGNUM *bnQ; BIGNUM *bnT; BIGNUM *bnE; BIGNUM *bnN; BN_CTX *context; UINT32 rem; // Make sure that hashAlg is valid hash pAssert(digestSize != 0); // if present, use externally provided counter if(counter != NULL) outer = *counter; else outer = 1; // Validate exponent UINT32_TO_BYTE_ARRAY(e, swapped); // Need to check that the exponent is prime and not less than 3 if( e != 0 && (e < 3 || !_math__IsPrime(e))) return CRYPT_FAIL; // Get structures for the big number representations context = BN_CTX_new(); if(context == NULL) FAIL(FATAL_ERROR_ALLOCATION); BN_CTX_start(context); bnP = BN_CTX_get(context); bnQ = BN_CTX_get(context); bnT = BN_CTX_get(context); bnE = BN_CTX_get(context); bnN = BN_CTX_get(context); if(bnN == NULL) FAIL(FATAL_ERROR_INTERNAL); // Set Q to zero. This is used as a flag. 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. BN_zero(bnQ); // Need to have some label if(label == NULL) label = (const char *)&defaultLabel; // Get the label size for(lLen = 0; label[lLen++] != 0;); // Start the hash using the seed and get the intermediate hash value _cpri__StartHMAC(hashAlg, FALSE, &h1, seed->size, seed->buffer, &oPadKey.b); _cpri__StartHash(hashAlg, FALSE, &h2); _cpri__UpdateHash(&h2, oPadKey.b.size, oPadKey.b.buffer); n->size = (keySizeInBits +7)/8; pAssert(n->size <= MAX_RSA_KEY_BYTES); p->size = n->size / 2; if(e == 0) e = RSA_DEFAULT_PUBLIC_EXPONENT; BN_set_word(bnE, e); // The first test will increment the counter from zero. for(outer += 1; outer != 0; outer++) { if(_plat__IsCanceled()) { retVal = CRYPT_CANCEL; goto Cleanup; } // Need to fill in the candidate with the hash fill = digestSize; pb = p->buffer; // Reset the inner counter inner = 0; for(i = p->size; i > 0; i -= digestSize) { inner++; // Initialize the HMAC with saved state _cpri__CopyHashState(&h, &h1); // Hash the inner counter (the one that changes on each HMAC iteration) UINT32_TO_BYTE_ARRAY(inner, swapped); _cpri__UpdateHash(&h, 4, swapped); _cpri__UpdateHash(&h, lLen, (BYTE *)label); // Is there any party 1 data if(extra != NULL) _cpri__UpdateHash(&h, extra->size, extra->buffer); // Include the outer counter (the one that changes on each prime // prime candidate generation UINT32_TO_BYTE_ARRAY(outer, swapped); _cpri__UpdateHash(&h, 4, swapped); _cpri__UpdateHash(&h, 2, (BYTE *)&keySizeInBits); if(i < fill) fill = i; _cpri__CompleteHash(&h, fill, pb); // Restart the oPad hash _cpri__CopyHashState(&h, &h2); // Add the last hashed data _cpri__UpdateHash(&h, fill, pb); // gives a completed HMAC _cpri__CompleteHash(&h, fill, pb); pb += fill; } // Set the Most significant 2 bits and the low bit of the candidate p->buffer[0] |= 0xC0; p->buffer[p->size - 1] |= 1; // Convert the candidate to a BN BN_bin2bn(p->buffer, p->size, bnP); // If this is the second prime, make sure that it differs from the // first prime by at least 2^100 if(!BN_is_zero(bnQ)) { // bnQ is non-zero if we already found it if(BN_ucmp(bnP, bnQ) < 0) BN_sub(bnT, bnQ, bnP); else BN_sub(bnT, bnP, bnQ); if(BN_num_bits(bnT) < 100) // Difference has to be at least 100 bits continue; } // Make sure that the prime candidate (p) is not divisible by the exponent // and that (p-1) is not divisible by the exponent // Get the remainder after dividing by the modulus rem = BN_mod_word(bnP, e); if(rem == 0) // evenly divisible so add two keeping the number odd and // making sure that 1 != p mod e BN_add_word(bnP, 2); else if(rem == 1) // leaves a remainder of 1 so subtract two keeping the // number odd and making (e-1) = p mod e BN_sub_word(bnP, 2); // Have a candidate, check for primality if((retVal = (CRYPT_RESULT)BN_is_prime_ex(bnP, BN_prime_checks, NULL, NULL)) < 0) FAIL(FATAL_ERROR_INTERNAL); if(retVal != 1) continue; // Found a prime, is this the first or second. if(BN_is_zero(bnQ)) { // copy p to q and compute another prime in p BN_copy(bnQ, bnP); continue; } //Form the public modulus BN_mul(bnN, bnP, bnQ, context); if(BN_num_bits(bnN) != keySizeInBits) FAIL(FATAL_ERROR_INTERNAL); // Save the public modulus BnTo2B(n, bnN, n->size); // Will pad the buffer to the correct size pAssert((n->buffer[0] & 0x80) != 0); // And one prime BnTo2B(p, bnP, p->size); pAssert((p->buffer[0] & 0x80) != 0); // Finish by making sure that we can form the modular inverse of PHI // with respect to the public exponent // Compute PHI = (p - 1)(q - 1) = n - p - q + 1 // Make sure that we can form the modular inverse BN_sub(bnT, bnN, bnP); BN_sub(bnT, bnT, bnQ); BN_add_word(bnT, 1); // find d such that (Phi * d) mod e ==1 // If there isn't then we are broken because we took the step // of making sure that the prime != 1 mod e so the modular inverse // must exist if(BN_mod_inverse(bnT, bnE, bnT, context) == NULL || BN_is_zero(bnT)) FAIL(FATAL_ERROR_INTERNAL); // And, finally, do a trial encryption decryption { TPM2B_TYPE(RSA_KEY, MAX_RSA_KEY_BYTES); TPM2B_RSA_KEY r; r.t.size = sizeof(n->size); // If we are using a seed, then results must be reproducible on each // call. Otherwise, just get a random number if(seed == NULL) _cpri__GenerateRandom(n->size, r.t.buffer); else { // this this version does not have a deterministic RNG, XOR the // public key and private exponent to get a deterministic value // for testing. int i; // Generate a random-ish number starting with the public modulus // XORed with the MSO of the seed for(i = 0; i < n->size; i++) r.t.buffer[i] = n->buffer[i] ^ seed->buffer[0]; } // Make sure that the number is smaller than the public modulus r.t.buffer[0] &= 0x7F; // Convert if( BN_bin2bn(r.t.buffer, r.t.size, bnP) == NULL // Encrypt with the public exponent || BN_mod_exp(bnQ, bnP, bnE, bnN, context) != 1 // Decrypt with the private exponent || BN_mod_exp(bnQ, bnQ, bnT, bnN, context) != 1) FAIL(FATAL_ERROR_INTERNAL); // If the starting and ending values are not the same, start over )-; if(BN_ucmp(bnP, bnQ) != 0) { BN_zero(bnQ); continue; } } retVal = CRYPT_SUCCESS; goto Cleanup; } retVal = CRYPT_FAIL; Cleanup: // Close out the hash sessions _cpri__CompleteHash(&h2, 0, NULL); _cpri__CompleteHash(&h1, 0, NULL); // Free up allocated BN values BN_CTX_end(context); BN_CTX_free(context); if(counter != NULL) *counter = outer; return retVal; } #endif // RSA_KEY_SIEVE #endif // TPM_ALG_RSA