Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 44 additions & 35 deletions RSA.xs
Original file line number Diff line number Diff line change
Expand Up @@ -401,15 +401,46 @@ EVP_PKEY* _load_rsa_key(SV* p_keyStringSv,
#endif
return rsa;
}

/* Pre-validate plaintext length before calling OpenSSL.
Called from encrypt() and private_encrypt() — not from decrypt/public_decrypt
where the input is ciphertext, not plaintext. */
static void check_max_message_length(rsaData* p_rsa, STRLEN from_length) {
int size;
int max_len = -1;
const char *pad_name = NULL;

size = EVP_PKEY_get_size(p_rsa->rsa);

if (p_rsa->padding == RSA_PKCS1_OAEP_PADDING) {
/* OAEP overhead is 2*hLen+2 where hLen is the OAEP hash length.
This module does not set EVP_PKEY_CTX_set_rsa_oaep_md(), so
OpenSSL always uses SHA-1 (hLen=20) regardless of hashMode. */
max_len = size - 42; /* 2 * SHA1_DIGEST_LENGTH + 2 */
pad_name = "OAEP";
} else if (p_rsa->padding == RSA_PKCS1_PADDING) {
max_len = size - 11; /* PKCS#1 v1.5 overhead */
pad_name = "PKCS#1 v1.5";
} else if (p_rsa->padding == RSA_NO_PADDING) {
max_len = size;
pad_name = "no";
}

if (max_len >= 0 && from_length > (STRLEN) max_len) {
croak("plaintext too long for key size with %s padding"
" (%d bytes max, got %d)", pad_name, max_len, (int)from_length);
}
}

#if OPENSSL_VERSION_NUMBER >= 0x30000000L

SV* rsa_crypt(rsaData* p_rsa, SV* p_from,
int (*p_crypt)(EVP_PKEY_CTX*, unsigned char*, size_t*, const unsigned char*, size_t),
int (*init_crypt)(EVP_PKEY_CTX*), int public, int is_encrypt)
int (*init_crypt)(EVP_PKEY_CTX*), int is_encrypt)
#else

SV* rsa_crypt(rsaData* p_rsa, SV* p_from,
int (*p_crypt)(int, const unsigned char*, unsigned char*, RSA*, int), int public, int is_encrypt)
int (*p_crypt)(int, const unsigned char*, unsigned char*, RSA*, int), int is_encrypt)
#endif
{
STRLEN from_length;
Expand All @@ -420,37 +451,12 @@ SV* rsa_crypt(rsaData* p_rsa, SV* p_from,
SV* sv;

from = (unsigned char*) SvPV(p_from, from_length);
size = EVP_PKEY_get_size(p_rsa->rsa);

if(is_encrypt && p_rsa->padding == RSA_PKCS1_PADDING) {
croak("PKCS#1 v1.5 padding for encryption is vulnerable to the Marvin attack. "
"Use use_pkcs1_oaep_padding() for encryption, or use_pkcs1_padding() with sign()/verify().");
}

/* Pre-validate plaintext length before calling OpenSSL.
Only applies to encryption direction (encrypt, private_encrypt),
not to decryption (decrypt, public_decrypt) where input is ciphertext. */
if (public == is_encrypt) {
int max_len = -1;
const char *pad_name = NULL;

if (p_rsa->padding == RSA_PKCS1_OAEP_PADDING) {
max_len = size - 42; /* 2 * SHA1_DIGEST_LENGTH + 2 */
pad_name = "OAEP";
} else if (p_rsa->padding == RSA_PKCS1_PADDING) {
max_len = size - 11; /* PKCS#1 v1.5 overhead */
pad_name = "PKCS#1 v1.5";
} else if (p_rsa->padding == RSA_NO_PADDING) {
max_len = size;
pad_name = "no";
}

if (max_len >= 0 && (int)from_length > max_len) {
croak("plaintext too long for key size with %s padding"
" (%d bytes max, got %d)", pad_name, max_len, (int)from_length);
}
}

#if OPENSSL_VERSION_NUMBER >= 0x30000000L

if(p_rsa->padding == RSA_PKCS1_PSS_PADDING) {
Expand Down Expand Up @@ -488,6 +494,7 @@ SV* rsa_crypt(rsaData* p_rsa, SV* p_from,
CHECK_OPEN_SSL(0);
crypt_done:
#else
size = EVP_PKEY_get_size(p_rsa->rsa);
CHECK_NEW(to, size, UNSIGNED_CHAR);
to_length = p_crypt(
from_length, from, (unsigned char*) to, p_rsa->rsa, p_rsa->padding);
Expand Down Expand Up @@ -1146,10 +1153,11 @@ encrypt(p_rsa, p_plaintext)
rsaData* p_rsa;
SV* p_plaintext;
CODE:
check_max_message_length(p_rsa, sv_len(p_plaintext));
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
RETVAL = rsa_crypt(p_rsa, p_plaintext, EVP_PKEY_encrypt, EVP_PKEY_encrypt_init, 1 /* public */, 1 /* is_encrypt */);
RETVAL = rsa_crypt(p_rsa, p_plaintext, EVP_PKEY_encrypt, EVP_PKEY_encrypt_init, 1 /* is_encrypt */);
#else
RETVAL = rsa_crypt(p_rsa, p_plaintext, RSA_public_encrypt, 1 /* public */, 1 /* is_encrypt */);
RETVAL = rsa_crypt(p_rsa, p_plaintext, RSA_public_encrypt, 1 /* is_encrypt */);
#endif
OUTPUT:
RETVAL
Expand All @@ -1164,9 +1172,9 @@ decrypt(p_rsa, p_ciphertext)
croak("Public keys cannot decrypt");
}
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
RETVAL = rsa_crypt(p_rsa, p_ciphertext, EVP_PKEY_decrypt, EVP_PKEY_decrypt_init, 0 /* private */, 1 /* is_encrypt */);
RETVAL = rsa_crypt(p_rsa, p_ciphertext, EVP_PKEY_decrypt, EVP_PKEY_decrypt_init, 1 /* is_encrypt */);
#else
RETVAL = rsa_crypt(p_rsa, p_ciphertext, RSA_private_decrypt, 0 /* private */, 1 /* is_encrypt */);
RETVAL = rsa_crypt(p_rsa, p_ciphertext, RSA_private_decrypt, 1 /* is_encrypt */);
#endif
OUTPUT:
RETVAL
Expand All @@ -1180,10 +1188,11 @@ private_encrypt(p_rsa, p_plaintext)
{
croak("Public keys cannot private_encrypt");
}
check_max_message_length(p_rsa, sv_len(p_plaintext));
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
RETVAL = rsa_crypt(p_rsa, p_plaintext, EVP_PKEY_sign, EVP_PKEY_sign_init, 0 /* private */, 0 /* is_encrypt */);
RETVAL = rsa_crypt(p_rsa, p_plaintext, EVP_PKEY_sign, EVP_PKEY_sign_init, 0 /* is_encrypt */);
#else
RETVAL = rsa_crypt(p_rsa, p_plaintext, RSA_private_encrypt, 0 /* private */, 0 /* is_encrypt */);
RETVAL = rsa_crypt(p_rsa, p_plaintext, RSA_private_encrypt, 0 /* is_encrypt */);
#endif
OUTPUT:
RETVAL
Expand All @@ -1194,9 +1203,9 @@ public_decrypt(p_rsa, p_ciphertext)
SV* p_ciphertext;
CODE:
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
RETVAL = rsa_crypt(p_rsa, p_ciphertext, EVP_PKEY_verify_recover, EVP_PKEY_verify_recover_init, 1 /*public */, 0 /* is_encrypt */);
RETVAL = rsa_crypt(p_rsa, p_ciphertext, EVP_PKEY_verify_recover, EVP_PKEY_verify_recover_init, 0 /* is_encrypt */);
#else
RETVAL = rsa_crypt(p_rsa, p_ciphertext, RSA_public_decrypt, 1 /* public */, 0 /* is_encrypt */);
RETVAL = rsa_crypt(p_rsa, p_ciphertext, RSA_public_decrypt, 0 /* is_encrypt */);
#endif
OUTPUT:
RETVAL
Expand Down
Loading