Commit 146b52ed authored by Dr. Stephen Henson's avatar Dr. Stephen Henson
Browse files

Fix for CMS/PKCS7 MMA. If RSA decryption fails use a random key and

continue with symmetric decryption process to avoid leaking timing
information to an attacker.

Thanks to Ivan Nestlerode <inestlerode@us.ibm.com> for discovering
this issue. (CVE-2012-0884)
parent 13747c6f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -111,6 +111,7 @@ DECLARE_ASN1_PRINT_FUNCTION(CMS_ContentInfo)
#define CMS_PARTIAL			0x4000
#define CMS_REUSE_DIGEST		0x8000
#define CMS_USE_KEYID			0x10000
#define CMS_DEBUG_DECRYPT		0x20000

const ASN1_OBJECT *CMS_get0_type(CMS_ContentInfo *cms);

+46 −14
Original line number Diff line number Diff line
@@ -73,6 +73,8 @@ BIO *cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec)
	const EVP_CIPHER *ciph;
	X509_ALGOR *calg = ec->contentEncryptionAlgorithm;
	unsigned char iv[EVP_MAX_IV_LENGTH], *piv = NULL;
	unsigned char *tkey = NULL;
	size_t tkeylen;

	int ok = 0;

@@ -137,33 +139,58 @@ BIO *cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec)
				CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR);
		goto err;
		}


	if (enc && !ec->key)
	/* Generate random session key */
	if (!enc || !ec->key)
		{
		/* Generate random key */
		if (!ec->keylen)
			ec->keylen = EVP_CIPHER_CTX_key_length(ctx);
		ec->key = OPENSSL_malloc(ec->keylen);
		if (!ec->key)
		tkeylen = EVP_CIPHER_CTX_key_length(ctx);
		tkey = OPENSSL_malloc(tkeylen);
		if (!tkey)
			{
			CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO,
							ERR_R_MALLOC_FAILURE);
			goto err;
			}
		if (EVP_CIPHER_CTX_rand_key(ctx, ec->key) <= 0)
		if (EVP_CIPHER_CTX_rand_key(ctx, tkey) <= 0)
			goto err;
		}

	if (!ec->key)
		{
		ec->key = tkey;
		ec->keylen = tkeylen;
		tkey = NULL;
		if (enc)
			keep_key = 1;
		else
			ERR_clear_error();
		
		}
	else if (ec->keylen != (unsigned int)EVP_CIPHER_CTX_key_length(ctx))

	if (ec->keylen != tkeylen)
		{
		/* If necessary set key length */
		if (EVP_CIPHER_CTX_set_key_length(ctx, ec->keylen) <= 0)
			{
			/* Only reveal failure if debugging so we don't
			 * leak information which may be useful in MMA.
			 */
			if (ec->debug)
				{
				CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO,
						CMS_R_INVALID_KEY_LENGTH);
				goto err;
				}
			else
				{
				/* Use random key */
				OPENSSL_cleanse(ec->key, ec->keylen);
				OPENSSL_free(ec->key);
				ec->key = tkey;
				ec->keylen = tkeylen;
				tkey = NULL;
				ERR_clear_error();
				}
			}
		}

	if (EVP_CipherInit_ex(ctx, NULL, NULL, ec->key, piv, enc) <= 0)
@@ -198,6 +225,11 @@ BIO *cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec)
		OPENSSL_free(ec->key);
		ec->key = NULL;
		}
	if (tkey)
		{
		OPENSSL_cleanse(tkey, tkeylen);
		OPENSSL_free(tkey);
		}
	if (ok)
		return b;
	BIO_free(b);
+10 −2
Original line number Diff line number Diff line
@@ -370,6 +370,8 @@ static int cms_RecipientInfo_ktri_decrypt(CMS_ContentInfo *cms,
	unsigned char *ek = NULL;
	size_t eklen;
	int ret = 0;
	CMS_EncryptedContentInfo *ec;
	ec = cms->d.envelopedData->encryptedContentInfo;

	if (ktri->pkey == NULL)
		{
@@ -416,8 +418,14 @@ static int cms_RecipientInfo_ktri_decrypt(CMS_ContentInfo *cms,

	ret = 1;

	cms->d.envelopedData->encryptedContentInfo->key = ek;
	cms->d.envelopedData->encryptedContentInfo->keylen = eklen;
	if (ec->key)
		{
		OPENSSL_cleanse(ec->key, ec->keylen);
		OPENSSL_free(ec->key);
		}

	ec->key = ek;
	ec->keylen = eklen;

	err:
	if (pctx)
+2 −0
Original line number Diff line number Diff line
@@ -175,6 +175,8 @@ struct CMS_EncryptedContentInfo_st
	const EVP_CIPHER *cipher;
	unsigned char *key;
	size_t keylen;
	/* Set to 1 if we are debugging decrypt and don't fake keys for MMA */
	int debug;
	};

struct CMS_RecipientInfo_st
+33 −4
Original line number Diff line number Diff line
@@ -611,7 +611,10 @@ int CMS_decrypt_set1_pkey(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert)
	STACK_OF(CMS_RecipientInfo) *ris;
	CMS_RecipientInfo *ri;
	int i, r;
	int debug = 0;
	ris = CMS_get0_RecipientInfos(cms);
	if (ris)
		debug = cms->d.envelopedData->encryptedContentInfo->debug;
	for (i = 0; i < sk_CMS_RecipientInfo_num(ris); i++)
		{
		ri = sk_CMS_RecipientInfo_value(ris, i);
@@ -625,17 +628,38 @@ int CMS_decrypt_set1_pkey(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert)
			CMS_RecipientInfo_set0_pkey(ri, pk);
			r = CMS_RecipientInfo_decrypt(cms, ri);
			CMS_RecipientInfo_set0_pkey(ri, NULL);
			if (r > 0)
				return 1;
			if (cert)
				{
				/* If not debugging clear any error and
				 * return success to avoid leaking of
				 * information useful to MMA
				 */
				if (!debug)
					{
					ERR_clear_error();
					return 1;
					}
				if (r > 0)
					return 1;
				CMSerr(CMS_F_CMS_DECRYPT_SET1_PKEY,
						CMS_R_DECRYPT_ERROR);
				return 0;
				}
			ERR_clear_error();
			/* If no cert and not debugging don't leave loop
			 * after first successful decrypt. Always attempt
			 * to decrypt all recipients to avoid leaking timing
			 * of a successful decrypt.
			 */
			else if (r > 0 && debug)
				return 1;
			}
		}
	/* If no cert and not debugging always return success */
	if (!cert && !debug)
		{
		ERR_clear_error();
		return 1;
		}

	CMSerr(CMS_F_CMS_DECRYPT_SET1_PKEY, CMS_R_NO_MATCHING_RECIPIENT);
	return 0;
@@ -718,9 +742,14 @@ int CMS_decrypt(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert,
		}
	if (!dcont && !check_content(cms))
		return 0;
	if (flags & CMS_DEBUG_DECRYPT)
		cms->d.envelopedData->encryptedContentInfo->debug = 1;
	else
		cms->d.envelopedData->encryptedContentInfo->debug = 0;
	if (!pk && !cert && !dcont && !out)
		return 1;
	if (pk && !CMS_decrypt_set1_pkey(cms, pk, cert))
		return 0;

	cont = CMS_dataInit(cms, dcont);
	if (!cont)
		return 0;
Loading