diff --git a/RSA.xs b/RSA.xs index af12460..db4957d 100644 --- a/RSA.xs +++ b/RSA.xs @@ -73,9 +73,16 @@ typedef struct void croakSsl(char* p_file, int p_line) { const char* errorReason; - /* Just return the top error on the stack */ - errorReason = ERR_reason_error_string(ERR_get_error()); - ERR_clear_error(); + unsigned long last_err = 0; + unsigned long err; + /* Drain the error queue and use the last (most recent) error, + which is typically the most descriptive. This also prevents + stale errors from a previous eval-caught failure from leaking + into the next croak message. */ + while ((err = ERR_get_error()) != 0) { + last_err = err; + } + errorReason = ERR_reason_error_string(last_err); croak("%s:%d: OpenSSL error: %s", p_file, p_line, errorReason ? errorReason : "(unknown error)"); } diff --git a/t/bignum.t b/t/bignum.t index 883ebba..70aea5c 100644 --- a/t/bignum.t +++ b/t/bignum.t @@ -89,7 +89,7 @@ sub check_key_parameters # runs 8 tests $e, $d, undef, $q ); }; - like( $@, qr/OpenSSL error: p not prime/, "bad n with q triggers 'p not prime' error" ); + like( $@, qr/OpenSSL error: (?:p not prime|d e not congruent to 1)/, "bad n with q triggers key validation error" ); #try again, to make sure the error queue was properly flushed eval { @@ -98,5 +98,5 @@ sub check_key_parameters # runs 8 tests $e, $d, undef, $q ); }; - like( $@, qr/OpenSSL error: p not prime/, "error queue flushed: repeat triggers same error" ); + like( $@, qr/OpenSSL error: (?:p not prime|d e not congruent to 1)/, "error queue flushed: repeat triggers same error" ); } diff --git a/t/error_queue.t b/t/error_queue.t new file mode 100644 index 0000000..6cc4ce2 --- /dev/null +++ b/t/error_queue.t @@ -0,0 +1,32 @@ +use strict; +use warnings; +use Test::More; + +use Crypt::OpenSSL::RSA; + +plan tests => 4; + +# Test that eval-caught OpenSSL failures don't pollute subsequent error messages. +# Bug: croakSsl() used ERR_get_error() once (oldest error) instead of draining +# the queue to the last (most recent/descriptive) error. + +my $rsa = Crypt::OpenSSL::RSA->generate_key(2048); + +# Trigger a decrypt failure inside eval +eval { $rsa->decrypt("not valid ciphertext that is too short") }; +my $first_error = $@; +ok($first_error, "decrypt failure with short input caught in eval"); + +# Trigger a different decrypt failure +eval { $rsa->decrypt("x" x 256) }; +my $second_error = $@; +ok($second_error, "decrypt failure with full-length garbage caught in eval"); + +# The second error should be a real decryption error, not stale from the first +like($second_error, qr/OpenSSL error: \S/, "second error has a meaningful OpenSSL message"); + +# Trigger yet another failure after two eval-caught ones — error queue should be clean +eval { $rsa->encrypt("A" x 500) }; +my $third_error = $@; +like($third_error, qr/too large|data greater|asym cipher failure/i, + "third error reports actual problem (data too large), not stale from earlier failures");