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)
This commit is contained in:
11
CHANGES
11
CHANGES
@@ -4,6 +4,17 @@
|
|||||||
|
|
||||||
Changes between 0.9.8t and 0.9.8u [xx XXX xxxx]
|
Changes between 0.9.8t and 0.9.8u [xx XXX xxxx]
|
||||||
|
|
||||||
|
*) Fix MMA (Bleichenbacher's attack on PKCS #1 v1.5 RSA padding) weakness
|
||||||
|
in CMS and PKCS7 code. When RSA decryption fails use a random key for
|
||||||
|
content decryption and always return the same error. Note: this attack
|
||||||
|
needs on average 2^20 messages so it only affects automated senders. The
|
||||||
|
old behaviour can be reenabled in the CMS code by setting the
|
||||||
|
CMS_DEBUG_DECRYPT flag: this is useful for debugging and testing where
|
||||||
|
an MMA defence is not necessary.
|
||||||
|
Thanks to Ivan Nestlerode <inestlerode@us.ibm.com> for discovering
|
||||||
|
this issue. (CVE-2012-0884)
|
||||||
|
[Steve Henson]
|
||||||
|
|
||||||
*) Fix CVE-2011-4619: make sure we really are receiving a
|
*) Fix CVE-2011-4619: make sure we really are receiving a
|
||||||
client hello before rejecting multiple SGC restarts. Thanks to
|
client hello before rejecting multiple SGC restarts. Thanks to
|
||||||
Ivan Nestlerode <inestlerode@us.ibm.com> for discovering this bug.
|
Ivan Nestlerode <inestlerode@us.ibm.com> for discovering this bug.
|
||||||
|
@@ -226,6 +226,8 @@ int MAIN(int argc, char **argv)
|
|||||||
else if (!strcmp(*args,"-camellia256"))
|
else if (!strcmp(*args,"-camellia256"))
|
||||||
cipher = EVP_camellia_256_cbc();
|
cipher = EVP_camellia_256_cbc();
|
||||||
#endif
|
#endif
|
||||||
|
else if (!strcmp (*args, "-debug_decrypt"))
|
||||||
|
flags |= CMS_DEBUG_DECRYPT;
|
||||||
else if (!strcmp (*args, "-text"))
|
else if (!strcmp (*args, "-text"))
|
||||||
flags |= CMS_TEXT;
|
flags |= CMS_TEXT;
|
||||||
else if (!strcmp (*args, "-nointern"))
|
else if (!strcmp (*args, "-nointern"))
|
||||||
@@ -1013,6 +1015,8 @@ int MAIN(int argc, char **argv)
|
|||||||
ret = 4;
|
ret = 4;
|
||||||
if (operation == SMIME_DECRYPT)
|
if (operation == SMIME_DECRYPT)
|
||||||
{
|
{
|
||||||
|
if (flags & CMS_DEBUG_DECRYPT)
|
||||||
|
CMS_decrypt(cms, NULL, NULL, NULL, NULL, flags);
|
||||||
|
|
||||||
if (secret_key)
|
if (secret_key)
|
||||||
{
|
{
|
||||||
|
@@ -110,6 +110,7 @@ DECLARE_ASN1_FUNCTIONS_const(CMS_ReceiptRequest)
|
|||||||
#define CMS_PARTIAL 0x4000
|
#define CMS_PARTIAL 0x4000
|
||||||
#define CMS_REUSE_DIGEST 0x8000
|
#define CMS_REUSE_DIGEST 0x8000
|
||||||
#define CMS_USE_KEYID 0x10000
|
#define CMS_USE_KEYID 0x10000
|
||||||
|
#define CMS_DEBUG_DECRYPT 0x20000
|
||||||
|
|
||||||
const ASN1_OBJECT *CMS_get0_type(CMS_ContentInfo *cms);
|
const ASN1_OBJECT *CMS_get0_type(CMS_ContentInfo *cms);
|
||||||
|
|
||||||
|
@@ -73,6 +73,8 @@ BIO *cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec)
|
|||||||
const EVP_CIPHER *ciph;
|
const EVP_CIPHER *ciph;
|
||||||
X509_ALGOR *calg = ec->contentEncryptionAlgorithm;
|
X509_ALGOR *calg = ec->contentEncryptionAlgorithm;
|
||||||
unsigned char iv[EVP_MAX_IV_LENGTH], *piv = NULL;
|
unsigned char iv[EVP_MAX_IV_LENGTH], *piv = NULL;
|
||||||
|
unsigned char *tkey = NULL;
|
||||||
|
size_t tkeylen;
|
||||||
|
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
|
|
||||||
@@ -137,32 +139,57 @@ BIO *cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec)
|
|||||||
CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR);
|
CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
/* Generate random session key */
|
||||||
|
if (!enc || !ec->key)
|
||||||
if (enc && !ec->key)
|
|
||||||
{
|
{
|
||||||
/* Generate random key */
|
tkeylen = EVP_CIPHER_CTX_key_length(ctx);
|
||||||
if (!ec->keylen)
|
tkey = OPENSSL_malloc(tkeylen);
|
||||||
ec->keylen = EVP_CIPHER_CTX_key_length(ctx);
|
if (!tkey)
|
||||||
ec->key = OPENSSL_malloc(ec->keylen);
|
|
||||||
if (!ec->key)
|
|
||||||
{
|
{
|
||||||
CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO,
|
CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO,
|
||||||
ERR_R_MALLOC_FAILURE);
|
ERR_R_MALLOC_FAILURE);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
if (EVP_CIPHER_CTX_rand_key(ctx, ec->key) <= 0)
|
if (EVP_CIPHER_CTX_rand_key(ctx, tkey) <= 0)
|
||||||
goto err;
|
goto err;
|
||||||
keep_key = 1;
|
|
||||||
}
|
}
|
||||||
else if (ec->keylen != (unsigned int)EVP_CIPHER_CTX_key_length(ctx))
|
|
||||||
|
if (!ec->key)
|
||||||
|
{
|
||||||
|
ec->key = tkey;
|
||||||
|
ec->keylen = tkeylen;
|
||||||
|
tkey = NULL;
|
||||||
|
if (enc)
|
||||||
|
keep_key = 1;
|
||||||
|
else
|
||||||
|
ERR_clear_error();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ec->keylen != tkeylen)
|
||||||
{
|
{
|
||||||
/* If necessary set key length */
|
/* If necessary set key length */
|
||||||
if (EVP_CIPHER_CTX_set_key_length(ctx, ec->keylen) <= 0)
|
if (EVP_CIPHER_CTX_set_key_length(ctx, ec->keylen) <= 0)
|
||||||
{
|
{
|
||||||
CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO,
|
/* Only reveal failure if debugging so we don't
|
||||||
CMS_R_INVALID_KEY_LENGTH);
|
* leak information which may be useful in MMA.
|
||||||
goto err;
|
*/
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,6 +225,11 @@ BIO *cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec)
|
|||||||
OPENSSL_free(ec->key);
|
OPENSSL_free(ec->key);
|
||||||
ec->key = NULL;
|
ec->key = NULL;
|
||||||
}
|
}
|
||||||
|
if (tkey)
|
||||||
|
{
|
||||||
|
OPENSSL_cleanse(tkey, tkeylen);
|
||||||
|
OPENSSL_free(tkey);
|
||||||
|
}
|
||||||
if (ok)
|
if (ok)
|
||||||
return b;
|
return b;
|
||||||
BIO_free(b);
|
BIO_free(b);
|
||||||
|
@@ -352,6 +352,8 @@ static int cms_RecipientInfo_ktri_decrypt(CMS_ContentInfo *cms,
|
|||||||
unsigned char *ek = NULL;
|
unsigned char *ek = NULL;
|
||||||
int eklen;
|
int eklen;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
CMS_EncryptedContentInfo *ec;
|
||||||
|
ec = cms->d.envelopedData->encryptedContentInfo;
|
||||||
|
|
||||||
if (ktri->pkey == NULL)
|
if (ktri->pkey == NULL)
|
||||||
{
|
{
|
||||||
@@ -382,8 +384,14 @@ static int cms_RecipientInfo_ktri_decrypt(CMS_ContentInfo *cms,
|
|||||||
|
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
|
||||||
cms->d.envelopedData->encryptedContentInfo->key = ek;
|
if (ec->key)
|
||||||
cms->d.envelopedData->encryptedContentInfo->keylen = eklen;
|
{
|
||||||
|
OPENSSL_cleanse(ec->key, ec->keylen);
|
||||||
|
OPENSSL_free(ec->key);
|
||||||
|
}
|
||||||
|
|
||||||
|
ec->key = ek;
|
||||||
|
ec->keylen = eklen;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
if (!ret && ek)
|
if (!ret && ek)
|
||||||
|
@@ -175,6 +175,8 @@ struct CMS_EncryptedContentInfo_st
|
|||||||
const EVP_CIPHER *cipher;
|
const EVP_CIPHER *cipher;
|
||||||
unsigned char *key;
|
unsigned char *key;
|
||||||
size_t keylen;
|
size_t keylen;
|
||||||
|
/* Set to 1 if we are debugging decrypt and don't fake keys for MMA */
|
||||||
|
int debug;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CMS_RecipientInfo_st
|
struct CMS_RecipientInfo_st
|
||||||
|
@@ -622,7 +622,10 @@ int CMS_decrypt_set1_pkey(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert)
|
|||||||
STACK_OF(CMS_RecipientInfo) *ris;
|
STACK_OF(CMS_RecipientInfo) *ris;
|
||||||
CMS_RecipientInfo *ri;
|
CMS_RecipientInfo *ri;
|
||||||
int i, r;
|
int i, r;
|
||||||
|
int debug = 0;
|
||||||
ris = CMS_get0_RecipientInfos(cms);
|
ris = CMS_get0_RecipientInfos(cms);
|
||||||
|
if (ris)
|
||||||
|
debug = cms->d.envelopedData->encryptedContentInfo->debug;
|
||||||
for (i = 0; i < sk_CMS_RecipientInfo_num(ris); i++)
|
for (i = 0; i < sk_CMS_RecipientInfo_num(ris); i++)
|
||||||
{
|
{
|
||||||
ri = sk_CMS_RecipientInfo_value(ris, i);
|
ri = sk_CMS_RecipientInfo_value(ris, i);
|
||||||
@@ -636,17 +639,38 @@ int CMS_decrypt_set1_pkey(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert)
|
|||||||
CMS_RecipientInfo_set0_pkey(ri, pk);
|
CMS_RecipientInfo_set0_pkey(ri, pk);
|
||||||
r = CMS_RecipientInfo_decrypt(cms, ri);
|
r = CMS_RecipientInfo_decrypt(cms, ri);
|
||||||
CMS_RecipientInfo_set0_pkey(ri, NULL);
|
CMS_RecipientInfo_set0_pkey(ri, NULL);
|
||||||
if (r > 0)
|
|
||||||
return 1;
|
|
||||||
if (cert)
|
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,
|
CMSerr(CMS_F_CMS_DECRYPT_SET1_PKEY,
|
||||||
CMS_R_DECRYPT_ERROR);
|
CMS_R_DECRYPT_ERROR);
|
||||||
return 0;
|
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);
|
CMSerr(CMS_F_CMS_DECRYPT_SET1_PKEY, CMS_R_NO_MATCHING_RECIPIENT);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -705,9 +729,14 @@ int CMS_decrypt(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert,
|
|||||||
}
|
}
|
||||||
if (!dcont && !check_content(cms))
|
if (!dcont && !check_content(cms))
|
||||||
return 0;
|
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))
|
if (pk && !CMS_decrypt_set1_pkey(cms, pk, cert))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
cont = CMS_dataInit(cms, dcont);
|
cont = CMS_dataInit(cms, dcont);
|
||||||
if (!cont)
|
if (!cont)
|
||||||
return 0;
|
return 0;
|
||||||
|
@@ -420,6 +420,8 @@ BIO *PKCS7_dataDecode(PKCS7 *p7, EVP_PKEY *pkey, BIO *in_bio, X509 *pcert)
|
|||||||
int max;
|
int max;
|
||||||
X509_OBJECT ret;
|
X509_OBJECT ret;
|
||||||
#endif
|
#endif
|
||||||
|
unsigned char *tkey = NULL;
|
||||||
|
int tkeylen;
|
||||||
int jj;
|
int jj;
|
||||||
|
|
||||||
if ((etmp=BIO_new(BIO_f_cipher())) == NULL)
|
if ((etmp=BIO_new(BIO_f_cipher())) == NULL)
|
||||||
@@ -461,36 +463,42 @@ BIO *PKCS7_dataDecode(PKCS7 *p7, EVP_PKEY *pkey, BIO *in_bio, X509 *pcert)
|
|||||||
|
|
||||||
if (pcert == NULL)
|
if (pcert == NULL)
|
||||||
{
|
{
|
||||||
|
/* Temporary storage in case EVP_PKEY_decrypt
|
||||||
|
* overwrites output buffer on error.
|
||||||
|
*/
|
||||||
|
unsigned char *tmp2;
|
||||||
|
tmp2 = OPENSSL_malloc(jj);
|
||||||
|
if (!tmp2)
|
||||||
|
goto err;
|
||||||
|
jj = -1;
|
||||||
|
/* Always attempt to decrypt all cases to avoid
|
||||||
|
* leaking timing information about a successful
|
||||||
|
* decrypt.
|
||||||
|
*/
|
||||||
for (i=0; i<sk_PKCS7_RECIP_INFO_num(rsk); i++)
|
for (i=0; i<sk_PKCS7_RECIP_INFO_num(rsk); i++)
|
||||||
{
|
{
|
||||||
|
int tret;
|
||||||
ri=sk_PKCS7_RECIP_INFO_value(rsk,i);
|
ri=sk_PKCS7_RECIP_INFO_value(rsk,i);
|
||||||
jj=EVP_PKEY_decrypt(tmp,
|
tret=EVP_PKEY_decrypt(tmp2,
|
||||||
M_ASN1_STRING_data(ri->enc_key),
|
M_ASN1_STRING_data(ri->enc_key),
|
||||||
M_ASN1_STRING_length(ri->enc_key),
|
M_ASN1_STRING_length(ri->enc_key),
|
||||||
pkey);
|
pkey);
|
||||||
if (jj > 0)
|
if (tret > 0)
|
||||||
break;
|
{
|
||||||
|
memcpy(tmp, tmp2, tret);
|
||||||
|
OPENSSL_cleanse(tmp2, tret);
|
||||||
|
jj = tret;
|
||||||
|
}
|
||||||
ERR_clear_error();
|
ERR_clear_error();
|
||||||
ri = NULL;
|
|
||||||
}
|
|
||||||
if (ri == NULL)
|
|
||||||
{
|
|
||||||
PKCS7err(PKCS7_F_PKCS7_DATADECODE,
|
|
||||||
PKCS7_R_NO_RECIPIENT_MATCHES_KEY);
|
|
||||||
goto err;
|
|
||||||
}
|
}
|
||||||
|
OPENSSL_free(tmp2);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
jj=EVP_PKEY_decrypt(tmp,
|
jj=EVP_PKEY_decrypt(tmp,
|
||||||
M_ASN1_STRING_data(ri->enc_key),
|
M_ASN1_STRING_data(ri->enc_key),
|
||||||
M_ASN1_STRING_length(ri->enc_key), pkey);
|
M_ASN1_STRING_length(ri->enc_key), pkey);
|
||||||
if (jj <= 0)
|
ERR_clear_error();
|
||||||
{
|
|
||||||
PKCS7err(PKCS7_F_PKCS7_DATADECODE,
|
|
||||||
ERR_R_EVP_LIB);
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
evp_ctx=NULL;
|
evp_ctx=NULL;
|
||||||
@@ -499,24 +507,49 @@ BIO *PKCS7_dataDecode(PKCS7 *p7, EVP_PKEY *pkey, BIO *in_bio, X509 *pcert)
|
|||||||
goto err;
|
goto err;
|
||||||
if (EVP_CIPHER_asn1_to_param(evp_ctx,enc_alg->parameter) < 0)
|
if (EVP_CIPHER_asn1_to_param(evp_ctx,enc_alg->parameter) < 0)
|
||||||
goto err;
|
goto err;
|
||||||
|
/* Generate random key to counter MMA */
|
||||||
|
tkeylen = EVP_CIPHER_CTX_key_length(evp_ctx);
|
||||||
|
tkey = OPENSSL_malloc(tkeylen);
|
||||||
|
if (!tkey)
|
||||||
|
goto err;
|
||||||
|
if (EVP_CIPHER_CTX_rand_key(evp_ctx, tkey) <= 0)
|
||||||
|
goto err;
|
||||||
|
/* If we have no key use random key */
|
||||||
|
if (jj <= 0)
|
||||||
|
{
|
||||||
|
OPENSSL_free(tmp);
|
||||||
|
jj = tkeylen;
|
||||||
|
tmp = tkey;
|
||||||
|
tkey = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (jj != EVP_CIPHER_CTX_key_length(evp_ctx)) {
|
if (jj != tkeylen) {
|
||||||
/* Some S/MIME clients don't use the same key
|
/* Some S/MIME clients don't use the same key
|
||||||
* and effective key length. The key length is
|
* and effective key length. The key length is
|
||||||
* determined by the size of the decrypted RSA key.
|
* determined by the size of the decrypted RSA key.
|
||||||
*/
|
*/
|
||||||
if(!EVP_CIPHER_CTX_set_key_length(evp_ctx, jj))
|
if(!EVP_CIPHER_CTX_set_key_length(evp_ctx, jj))
|
||||||
{
|
{
|
||||||
PKCS7err(PKCS7_F_PKCS7_DATADECODE,
|
/* As MMA defence use random key instead */
|
||||||
PKCS7_R_DECRYPTED_KEY_IS_WRONG_LENGTH);
|
OPENSSL_cleanse(tmp, jj);
|
||||||
goto err;
|
OPENSSL_free(tmp);
|
||||||
|
jj = tkeylen;
|
||||||
|
tmp = tkey;
|
||||||
|
tkey = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ERR_clear_error();
|
||||||
if (EVP_CipherInit_ex(evp_ctx,NULL,NULL,tmp,NULL,0) <= 0)
|
if (EVP_CipherInit_ex(evp_ctx,NULL,NULL,tmp,NULL,0) <= 0)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
OPENSSL_cleanse(tmp,jj);
|
OPENSSL_cleanse(tmp,jj);
|
||||||
|
|
||||||
|
if (tkey)
|
||||||
|
{
|
||||||
|
OPENSSL_cleanse(tkey, tkeylen);
|
||||||
|
OPENSSL_free(tkey);
|
||||||
|
}
|
||||||
|
|
||||||
if (out == NULL)
|
if (out == NULL)
|
||||||
out=etmp;
|
out=etmp;
|
||||||
else
|
else
|
||||||
|
Reference in New Issue
Block a user