From 985a5c42fe95daa9f12cf5261d623f31bc1dcdea Mon Sep 17 00:00:00 2001 From: Timothy Legge Date: Tue, 28 Oct 2025 23:45:17 -0300 Subject: [PATCH 1/3] Add support for use_pkcs1_pss_padding This change effectively reverses the fatal failure if use_pkcs1_padding is called and sets the padding to default to RSA_PKCS1_PSS_PADDING which is the recommended replacement for RSA_PKCS1_PADDING This will allow modules to continue to call use_pkcs1_padding but effectively replace it with the recommended alternative This may avoid changes to other modules but might not be what the user wants It should alos be noted that RSA_PKCS1_PSS_PADDING (and use_pkcs1_pss_padding should only be used for signing/verification operations --- RSA.pm | 11 ++++++++++- RSA.xs | 17 +++++++++++++++-- t/rsa.t | 47 +++++++++++++++++++++++++++-------------------- 3 files changed, 52 insertions(+), 23 deletions(-) diff --git a/RSA.pm b/RSA.pm index 1390852..f2f3c09 100644 --- a/RSA.pm +++ b/RSA.pm @@ -245,7 +245,9 @@ Encrypting user data directly with RSA is insecure. PKCS #1 v1.5 padding has been disabled as it is nearly impossible to use this padding method in a secure manner. It is known to be vulnerable to timing -based side channel attacks. use_pkcs1_padding() results in a fatal error. +based side channel attacks. + +use_pkcs1_padding() now sets the padding method to use_pkcs1_pss_padding. L @@ -256,6 +258,13 @@ an empty encoding parameter. This mode of padding is recommended for all new applications. It is the default mode used by C. +=item use_pkcs1_pss_padding + +Use C padding as defined in PKCS#1 v2.1. In general, RSA-PSS +should be used as a replacement for RSA-PKCS#1 v1.5. The module specifies +the message digest being requested and the appropriate mgf1 setting and +salt length for the digest. + =item use_sslv23_padding Use C padding with an SSL-specific modification that diff --git a/RSA.xs b/RSA.xs index aa8094c..29a445f 100644 --- a/RSA.xs +++ b/RSA.xs @@ -927,7 +927,7 @@ void use_pkcs1_padding(p_rsa) rsaData* p_rsa; CODE: - croak("PKCS#1 1.5 is disabled as it is known to be vulnerable to marvin attacks."); + p_rsa->padding = RSA_PKCS1_PSS_PADDING; void use_pkcs1_oaep_padding(p_rsa) @@ -935,6 +935,12 @@ use_pkcs1_oaep_padding(p_rsa) CODE: p_rsa->padding = RSA_PKCS1_OAEP_PADDING; +void +use_pkcs1_pss_padding(p_rsa) + rsaData* p_rsa; + CODE: + p_rsa->padding = RSA_PKCS1_PSS_PADDING; + #if OPENSSL_VERSION_NUMBER < 0x30000000L void @@ -977,7 +983,10 @@ sign(p_rsa, text_SV) int md_status; CHECK_OPEN_SSL((md_status = EVP_PKEY_CTX_set_signature_md(ctx, md)) > 0); - + if (p_rsa->padding == RSA_PKCS1_PSS_PADDING) { + CHECK_OPEN_SSL((md_status = EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, md)) > 0); + CHECK_OPEN_SSL(EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, RSA_PSS_SALTLEN_DIGEST) > 0); + } CHECK_OPEN_SSL(EVP_PKEY_sign(ctx, NULL, &signature_length, digest, get_digest_length(p_rsa->hashMode)) == 1); //signature = OPENSSL_malloc(signature_length); @@ -1034,6 +1043,10 @@ PPCODE: int md_status; CHECK_OPEN_SSL((md_status = EVP_PKEY_CTX_set_signature_md(ctx, md)) > 0); + if (p_rsa->padding == RSA_PKCS1_PSS_PADDING) { + CHECK_OPEN_SSL((md_status = EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, md)) > 0); + CHECK_OPEN_SSL(EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, RSA_PSS_SALTLEN_DIGEST) > 0); + } switch (EVP_PKEY_verify(ctx, sig, sig_length, digest, get_digest_length(p_rsa->hashMode))) #else diff --git a/t/rsa.t b/t/rsa.t index 05b2550..5259865 100644 --- a/t/rsa.t +++ b/t/rsa.t @@ -6,7 +6,7 @@ use Crypt::OpenSSL::RSA; use Crypt::OpenSSL::Guess qw(openssl_version); BEGIN { - plan tests => 37 + ( UNIVERSAL::can( "Crypt::OpenSSL::RSA", "use_sha512_hash" ) ? 4 * 5 : 0 ) + ( UNIVERSAL::can( "Crypt::OpenSSL::RSA", "use_whirlpool_hash" ) ? 1 * 5 : 0 ); + plan tests => 97 + ( UNIVERSAL::can( "Crypt::OpenSSL::RSA", "use_sha512_hash" ) ? 4 * 5 : 0 ) + ( UNIVERSAL::can( "Crypt::OpenSSL::RSA", "use_whirlpool_hash" ) ? 1 * 5 : 0 ); } sub _Test_Encrypt_And_Decrypt { @@ -37,16 +37,16 @@ sub _Test_Sign_And_Verify { my $sig = eval { $rsa->sign($plaintext) }; SKIP: { skip "OpenSSL error: illegal or unsupported padding mode - $hash", 5 if $@ =~ /illegal or unsupported padding mode/i; - ok( $rsa_pub->verify( $plaintext, $sig ) ); + ok( $rsa_pub->verify( $plaintext, $sig ), "rsa_pub verify $hash"); my $false_sig = unpack "H*", $sig; $false_sig =~ tr/[a-f]/[0a-d]/; - ok( !$rsa_pub->verify( $plaintext, pack( "H*", $false_sig ) ) ); - ok( !$rsa->verify( $plaintext, pack( "H*", $false_sig ) ) ); + ok( !$rsa_pub->verify( $plaintext, pack( "H*", $false_sig ) ), "rsa_pub do not verify invalid $hash" ); + ok( !$rsa->verify( $plaintext, pack( "H*", $false_sig ) ), "rsa do not verify invalid $hash" ); my $sig_of_other = $rsa->sign("different"); - ok( !$rsa_pub->verify( $plaintext, $sig_of_other ) ); - ok( !$rsa->verify( $plaintext, $sig_of_other ) ); + ok( !$rsa_pub->verify( $plaintext, $sig_of_other ), "rsa_pub do not verify unmatching message" ); + ok( !$rsa->verify( $plaintext, $sig_of_other ), "rsa do not verify unmatching message"); } } @@ -69,8 +69,8 @@ Crypt::OpenSSL::RSA->import_random_seed(); ok( Crypt::OpenSSL::RSA->generate_key(512)->size() * 8 == 512 ); -my $rsa = Crypt::OpenSSL::RSA->generate_key(1024); -ok( $rsa->size() * 8 == 1024 ); +my $rsa = Crypt::OpenSSL::RSA->generate_key(2048); +ok( $rsa->size() * 8 == 2048 ); ok( $rsa->check_key() ); $rsa->use_no_padding(); @@ -121,31 +121,38 @@ _check_for_croak( $plaintext .= $plaintext x 5; -# check signature algorithms -$rsa->use_md5_hash(); -$rsa_pub->use_md5_hash(); -_Test_Sign_And_Verify( $plaintext, $rsa, $rsa_pub, "md5" ); +my @paddings = qw/pkcs1 pkcs1_oaep pkcs1_pss/; +foreach my $padding (@paddings) { + my $p = "use_$padding\_padding"; -$rsa->use_sha1_hash(); -$rsa_pub->use_sha1_hash(); -_Test_Sign_And_Verify( $plaintext, $rsa, $rsa_pub, "sha1" ); + $rsa->$p; + $rsa_pub->$p; + # check signature algorithms + $rsa->use_md5_hash(); + $rsa_pub->use_md5_hash(); + _Test_Sign_And_Verify( $plaintext, $rsa, $rsa_pub, "md5 with $padding padding" ); -if ( UNIVERSAL::can( "Crypt::OpenSSL::RSA", "use_sha512_hash" ) ) { + $rsa->use_sha1_hash(); + $rsa_pub->use_sha1_hash(); + _Test_Sign_And_Verify( $plaintext, $rsa, $rsa_pub, "sha1 with $padding padding" ); + + if ( UNIVERSAL::can( "Crypt::OpenSSL::RSA", "use_sha512_hash" ) ) { $rsa->use_sha224_hash(); $rsa_pub->use_sha224_hash(); - _Test_Sign_And_Verify( $plaintext, $rsa, $rsa_pub, "sha224" ); + _Test_Sign_And_Verify( $plaintext, $rsa, $rsa_pub, "sha224 with $padding padding" ); $rsa->use_sha256_hash(); $rsa_pub->use_sha256_hash(); - _Test_Sign_And_Verify( $plaintext, $rsa, $rsa_pub, "sha256" ); + _Test_Sign_And_Verify( $plaintext, $rsa, $rsa_pub, "sha256 with $padding padding" ); $rsa->use_sha384_hash(); $rsa_pub->use_sha384_hash(); - _Test_Sign_And_Verify( $plaintext, $rsa, $rsa_pub, "sha384" ); + _Test_Sign_And_Verify( $plaintext, $rsa, $rsa_pub, "sha384 with $padding padding" ); $rsa->use_sha512_hash(); $rsa_pub->use_sha512_hash(); - _Test_Sign_And_Verify( $plaintext, $rsa, $rsa_pub, "sha512" ); + _Test_Sign_And_Verify( $plaintext, $rsa, $rsa_pub, "sha512 with $padding padding" ); + } } my ( $major, $minor, $patch ) = openssl_version(); From 0ea9c522b19138e4ba43b635285237d39bf6be2d Mon Sep 17 00:00:00 2001 From: Timothy Legge Date: Tue, 28 Oct 2025 23:57:45 -0300 Subject: [PATCH 2/3] Fatal error if RSA-PSS is used for encryption operations --- RSA.pm | 4 +++- RSA.xs | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/RSA.pm b/RSA.pm index f2f3c09..e7f5b05 100644 --- a/RSA.pm +++ b/RSA.pm @@ -246,10 +246,12 @@ Encrypting user data directly with RSA is insecure. PKCS #1 v1.5 padding has been disabled as it is nearly impossible to use this padding method in a secure manner. It is known to be vulnerable to timing based side channel attacks. +L use_pkcs1_padding() now sets the padding method to use_pkcs1_pss_padding. -L +B: RSA-PSS cannot be used for encryption/decryption and results in a +fatal error. Call C for encryption operations. =item use_pkcs1_oaep_padding diff --git a/RSA.xs b/RSA.xs index 29a445f..9aa8ed7 100644 --- a/RSA.xs +++ b/RSA.xs @@ -323,6 +323,10 @@ SV* rsa_crypt(rsaData* p_rsa, SV* p_from, size = EVP_PKEY_get_size(p_rsa->rsa); CHECK_NEW(to, size, UNSIGNED_CHAR); #if OPENSSL_VERSION_NUMBER >= 0x30000000L + + if(p_rsa->padding == RSA_PKCS1_PSS_PADDING) + croak("PKCS#1 v2.1 RSA-PSS cannot be used for encryption operations call \"use_pkcs1_oaep_padding\" instead."); + EVP_PKEY_CTX *ctx; OSSL_LIB_CTX *ossllibctx = OSSL_LIB_CTX_new(); From cb6435eb7fee930a4a41c7e6df72b6becd379068 Mon Sep 17 00:00:00 2001 From: Timothy Legge Date: Wed, 29 Oct 2025 17:44:50 -0300 Subject: [PATCH 3/3] Fix test for sha1 digest issue and croak on use_pkcs1_padding --- RSA.pm | 11 +++++------ RSA.xs | 2 +- t/rsa.t | 5 +++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/RSA.pm b/RSA.pm index e7f5b05..22ddc09 100644 --- a/RSA.pm +++ b/RSA.pm @@ -245,13 +245,9 @@ Encrypting user data directly with RSA is insecure. PKCS #1 v1.5 padding has been disabled as it is nearly impossible to use this padding method in a secure manner. It is known to be vulnerable to timing -based side channel attacks. -L - -use_pkcs1_padding() now sets the padding method to use_pkcs1_pss_padding. +based side channel attacks. use_pkcs1_padding() results in a fatal error. -B: RSA-PSS cannot be used for encryption/decryption and results in a -fatal error. Call C for encryption operations. +L =item use_pkcs1_oaep_padding @@ -267,6 +263,9 @@ should be used as a replacement for RSA-PKCS#1 v1.5. The module specifies the message digest being requested and the appropriate mgf1 setting and salt length for the digest. +B: RSA-PSS cannot be used for encryption/decryption and results in a +fatal error. Call C for encryption operations. + =item use_sslv23_padding Use C padding with an SSL-specific modification that diff --git a/RSA.xs b/RSA.xs index 9aa8ed7..d42c812 100644 --- a/RSA.xs +++ b/RSA.xs @@ -931,7 +931,7 @@ void use_pkcs1_padding(p_rsa) rsaData* p_rsa; CODE: - p_rsa->padding = RSA_PKCS1_PSS_PADDING; + croak("PKCS#1 1.5 is disabled as it is known to be vulnerable to marvin attacks."); void use_pkcs1_oaep_padding(p_rsa) diff --git a/t/rsa.t b/t/rsa.t index 5259865..2448825 100644 --- a/t/rsa.t +++ b/t/rsa.t @@ -6,7 +6,7 @@ use Crypt::OpenSSL::RSA; use Crypt::OpenSSL::Guess qw(openssl_version); BEGIN { - plan tests => 97 + ( UNIVERSAL::can( "Crypt::OpenSSL::RSA", "use_sha512_hash" ) ? 4 * 5 : 0 ) + ( UNIVERSAL::can( "Crypt::OpenSSL::RSA", "use_whirlpool_hash" ) ? 1 * 5 : 0 ); + plan tests => 67 + ( UNIVERSAL::can( "Crypt::OpenSSL::RSA", "use_sha512_hash" ) ? 4 * 5 : 0 ) + ( UNIVERSAL::can( "Crypt::OpenSSL::RSA", "use_whirlpool_hash" ) ? 1 * 5 : 0 ); } sub _Test_Encrypt_And_Decrypt { @@ -37,6 +37,7 @@ sub _Test_Sign_And_Verify { my $sig = eval { $rsa->sign($plaintext) }; SKIP: { skip "OpenSSL error: illegal or unsupported padding mode - $hash", 5 if $@ =~ /illegal or unsupported padding mode/i; + skip "OpenSSL error: invalid digest - $hash", 5 if $@ =~ /invalid digest/i; ok( $rsa_pub->verify( $plaintext, $sig ), "rsa_pub verify $hash"); my $false_sig = unpack "H*", $sig; @@ -121,7 +122,7 @@ _check_for_croak( $plaintext .= $plaintext x 5; -my @paddings = qw/pkcs1 pkcs1_oaep pkcs1_pss/; +my @paddings = qw/pkcs1_oaep pkcs1_pss/; foreach my $padding (@paddings) { my $p = "use_$padding\_padding";