mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-17 20:19:29 +00:00
Harvest 176 isolated vuln-vs-fix functions (109 CVEs, 352 training rows) into benchmarks/pairs/vfc with provenance and a JSONL training manifest. Score the slice as an honesty dashboard (ousast pairs --slice vfc); keep the pair gate on the local fixtures only.
241 lines
7.3 KiB
C
241 lines
7.3 KiB
C
/* Provenance: openssl/openssl rsa_ossl_private_decrypt (fixed).
|
|
* repo: openssl/openssl
|
|
* commit: 7fc67e0a33102aa47bbaa56533eeecb98c0450f7
|
|
* parent: 1ca61aa56090356bbdbb16cf48916fbd9886c78d
|
|
* commit_url: https://github.com/openssl/openssl/commit/7fc67e0a33102aa47bbaa56533eeecb98c0450f7
|
|
* cve: CVE-2020-25659
|
|
* license: OpenSSL License
|
|
* function: rsa_ossl_private_decrypt
|
|
* relpath: crypto/rsa/rsa_ossl.c
|
|
*/
|
|
|
|
static int rsa_ossl_private_decrypt(int flen, const unsigned char *from,
|
|
unsigned char *to, RSA *rsa, int padding)
|
|
{
|
|
BIGNUM *f, *ret;
|
|
int j, num = 0, r = -1;
|
|
unsigned char *buf = NULL;
|
|
unsigned char d_hash[SHA256_DIGEST_LENGTH] = {0};
|
|
HMAC_CTX *hmac = NULL;
|
|
unsigned int md_len = SHA256_DIGEST_LENGTH;
|
|
unsigned char kdk[SHA256_DIGEST_LENGTH] = {0};
|
|
BN_CTX *ctx = NULL;
|
|
int local_blinding = 0;
|
|
EVP_MD *md = NULL;
|
|
/*
|
|
* Used only if the blinding structure is shared. A non-NULL unblind
|
|
* instructs rsa_blinding_convert() and rsa_blinding_invert() to store
|
|
* the unblinding factor outside the blinding structure.
|
|
*/
|
|
BIGNUM *unblind = NULL;
|
|
BN_BLINDING *blinding = NULL;
|
|
|
|
if ((ctx = BN_CTX_new_ex(rsa->libctx)) == NULL)
|
|
goto err;
|
|
BN_CTX_start(ctx);
|
|
f = BN_CTX_get(ctx);
|
|
ret = BN_CTX_get(ctx);
|
|
if (ret == NULL) {
|
|
ERR_raise(ERR_LIB_RSA, ERR_R_BN_LIB);
|
|
goto err;
|
|
}
|
|
num = BN_num_bytes(rsa->n);
|
|
buf = OPENSSL_malloc(num);
|
|
if (buf == NULL)
|
|
goto err;
|
|
|
|
/*
|
|
* This check was for equality but PGP does evil things and chops off the
|
|
* top '0' bytes
|
|
*/
|
|
if (flen > num) {
|
|
ERR_raise(ERR_LIB_RSA, RSA_R_DATA_GREATER_THAN_MOD_LEN);
|
|
goto err;
|
|
}
|
|
|
|
if (flen < 1) {
|
|
ERR_raise(ERR_LIB_RSA, RSA_R_DATA_TOO_SMALL);
|
|
goto err;
|
|
}
|
|
|
|
/* make data into a big number */
|
|
if (BN_bin2bn(from, (int)flen, f) == NULL)
|
|
goto err;
|
|
|
|
if (BN_ucmp(f, rsa->n) >= 0) {
|
|
ERR_raise(ERR_LIB_RSA, RSA_R_DATA_TOO_LARGE_FOR_MODULUS);
|
|
goto err;
|
|
}
|
|
|
|
if (!(rsa->flags & RSA_FLAG_NO_BLINDING)) {
|
|
blinding = rsa_get_blinding(rsa, &local_blinding, ctx);
|
|
if (blinding == NULL) {
|
|
ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
if (blinding != NULL) {
|
|
if (!local_blinding && ((unblind = BN_CTX_get(ctx)) == NULL)) {
|
|
ERR_raise(ERR_LIB_RSA, ERR_R_BN_LIB);
|
|
goto err;
|
|
}
|
|
if (!rsa_blinding_convert(blinding, f, unblind, ctx))
|
|
goto err;
|
|
}
|
|
|
|
/* do the decrypt */
|
|
if ((rsa->flags & RSA_FLAG_EXT_PKEY) ||
|
|
(rsa->version == RSA_ASN1_VERSION_MULTI) ||
|
|
((rsa->p != NULL) &&
|
|
(rsa->q != NULL) &&
|
|
(rsa->dmp1 != NULL) && (rsa->dmq1 != NULL) && (rsa->iqmp != NULL))) {
|
|
if (!rsa->meth->rsa_mod_exp(ret, f, rsa, ctx))
|
|
goto err;
|
|
} else {
|
|
BIGNUM *d = BN_new();
|
|
if (d == NULL) {
|
|
ERR_raise(ERR_LIB_RSA, ERR_R_BN_LIB);
|
|
goto err;
|
|
}
|
|
if (rsa->d == NULL) {
|
|
ERR_raise(ERR_LIB_RSA, RSA_R_MISSING_PRIVATE_KEY);
|
|
BN_free(d);
|
|
goto err;
|
|
}
|
|
BN_with_flags(d, rsa->d, BN_FLG_CONSTTIME);
|
|
|
|
if (rsa->flags & RSA_FLAG_CACHE_PUBLIC)
|
|
if (!BN_MONT_CTX_set_locked(&rsa->_method_mod_n, rsa->lock,
|
|
rsa->n, ctx)) {
|
|
BN_free(d);
|
|
goto err;
|
|
}
|
|
if (!rsa->meth->bn_mod_exp(ret, f, d, rsa->n, ctx,
|
|
rsa->_method_mod_n)) {
|
|
BN_free(d);
|
|
goto err;
|
|
}
|
|
/* We MUST free d before any further use of rsa->d */
|
|
BN_free(d);
|
|
}
|
|
|
|
if (blinding)
|
|
if (!rsa_blinding_invert(blinding, ret, unblind, ctx))
|
|
goto err;
|
|
|
|
/*
|
|
* derive the Key Derivation Key from private exponent and public
|
|
* ciphertext
|
|
*/
|
|
if (!(rsa->flags & RSA_FLAG_EXT_PKEY)) {
|
|
/*
|
|
* because we use d as a handle to rsa->d we need to keep it local and
|
|
* free before any further use of rsa->d
|
|
*/
|
|
BIGNUM *d = BN_new();
|
|
if (d == NULL) {
|
|
ERR_raise(ERR_LIB_RSA, ERR_R_MALLOC_FAILURE);
|
|
goto err;
|
|
}
|
|
if (rsa->d == NULL) {
|
|
ERR_raise(ERR_LIB_RSA, RSA_R_MISSING_PRIVATE_KEY);
|
|
BN_free(d);
|
|
goto err;
|
|
}
|
|
BN_with_flags(d, rsa->d, BN_FLG_CONSTTIME);
|
|
if (BN_bn2binpad(d, buf, num) < 0) {
|
|
ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
|
|
BN_free(d);
|
|
goto err;
|
|
}
|
|
BN_free(d);
|
|
|
|
/*
|
|
* we use hardcoded hash so that migrating between versions that use
|
|
* different hash doesn't provide a Bleichenbacher oracle:
|
|
* if the attacker can see that different versions return different
|
|
* messages for the same ciphertext, they'll know that the message is
|
|
* syntethically generated, which means that the padding check failed
|
|
*/
|
|
md = EVP_MD_fetch(rsa->libctx, "sha256", NULL);
|
|
if (md == NULL) {
|
|
ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
|
|
goto err;
|
|
}
|
|
|
|
if (EVP_Digest(buf, num, d_hash, NULL, md, NULL) <= 0) {
|
|
ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
|
|
goto err;
|
|
}
|
|
|
|
hmac = HMAC_CTX_new();
|
|
if (hmac == NULL) {
|
|
ERR_raise(ERR_LIB_RSA, ERR_R_MALLOC_FAILURE);
|
|
goto err;
|
|
}
|
|
|
|
if (HMAC_Init_ex(hmac, d_hash, sizeof(d_hash), md, NULL) <= 0) {
|
|
ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
|
|
goto err;
|
|
}
|
|
|
|
if (flen < num) {
|
|
memset(buf, 0, num - flen);
|
|
if (HMAC_Update(hmac, buf, num - flen) <= 0) {
|
|
ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
|
|
goto err;
|
|
}
|
|
}
|
|
if (HMAC_Update(hmac, from, flen) <= 0) {
|
|
ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
|
|
goto err;
|
|
}
|
|
|
|
md_len = SHA256_DIGEST_LENGTH;
|
|
if (HMAC_Final(hmac, kdk, &md_len) <= 0) {
|
|
ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR);
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
j = BN_bn2binpad(ret, buf, num);
|
|
if (j < 0)
|
|
goto err;
|
|
|
|
switch (padding) {
|
|
case RSA_PKCS1_PADDING:
|
|
if (rsa->flags & RSA_FLAG_EXT_PKEY)
|
|
r = RSA_padding_check_PKCS1_type_2(to, num, buf, j, num);
|
|
else
|
|
r = ossl_rsa_padding_check_PKCS1_type_2(rsa->libctx, to, num, buf, j, num, kdk);
|
|
break;
|
|
case RSA_PKCS1_OAEP_PADDING:
|
|
r = RSA_padding_check_PKCS1_OAEP(to, num, buf, j, num, NULL, 0);
|
|
break;
|
|
case RSA_NO_PADDING:
|
|
memcpy(to, buf, (r = j));
|
|
break;
|
|
default:
|
|
ERR_raise(ERR_LIB_RSA, RSA_R_UNKNOWN_PADDING_TYPE);
|
|
goto err;
|
|
}
|
|
#ifndef FIPS_MODULE
|
|
/*
|
|
* This trick doesn't work in the FIPS provider because libcrypto manages
|
|
* the error stack. Instead we opt not to put an error on the stack at all
|
|
* in case of padding failure in the FIPS provider.
|
|
*/
|
|
ERR_raise(ERR_LIB_RSA, RSA_R_PADDING_CHECK_FAILED);
|
|
err_clear_last_constant_time(1 & ~constant_time_msb(r));
|
|
#endif
|
|
|
|
err:
|
|
HMAC_CTX_free(hmac);
|
|
EVP_MD_free(md);
|
|
BN_CTX_end(ctx);
|
|
BN_CTX_free(ctx);
|
|
OPENSSL_clear_free(buf, num);
|
|
return r;
|
|
}
|