Skip to content

fix: reject non-RSA keys on OpenSSL 3.x#141

Merged
atoomic merged 6 commits intocpan-authors:mainfrom
toddr-bot:koan.toddr.bot/fix-key-type-validation
Apr 5, 2026
Merged

fix: reject non-RSA keys on OpenSSL 3.x#141
atoomic merged 6 commits intocpan-authors:mainfrom
toddr-bot:koan.toddr.bot/fix-key-type-validation

Conversation

@toddr-bot
Copy link
Copy Markdown
Contributor

@toddr-bot toddr-bot commented Mar 24, 2026

Summary

  • Validates that loaded keys are actually RSA after PEM parsing on OpenSSL 3.x
  • Rejects EC, DSA, and other non-RSA keys with a clear error message
  • Adds test coverage for non-RSA key rejection

Why

On OpenSSL 3.x, the compatibility macros remap PEM_read_bio_RSAPrivateKey to the generic PEM_read_bio_PrivateKey, which accepts any key type. This means loading an EC key as RSA silently "succeeds" — but is_private() returns false (because EVP_PKEY_get_bn_param for RSA's d fails), and crypto operations produce confusing errors like "Public keys cannot sign messages" instead of telling the user the key isn't RSA.

Pre-3.x used RSA-specific loaders that rejected non-RSA keys at parse time.

How

Added EVP_PKEY_get_base_id() != EVP_PKEY_RSA check in _load_rsa_key() after successful PEM loading on 3.x. This is the single loading function used by all three key loaders (new_private_key, _new_public_key_pkcs1, _new_public_key_x509), so one check covers all entry points.

Testing

  • Verified EC private/public keys are rejected with "not an RSA key"
  • Verified RSA keys in all formats (private, PKCS1 public, X509 public) still load correctly
  • Full test suite passes: 511 tests (507 existing + 4 new)
  • Built and tested on OpenSSL 3.5.1

🤖 Generated with Claude Code


Quality Report

Changes: 2 files changed, 30 insertions(+), 1 deletion(-)

Code scan: clean

Tests: passed (OK)

Branch hygiene: clean

Generated by Kōan post-mission quality pipeline

Comment thread t/format.t
@atoomic
Copy link
Copy Markdown
Collaborator

atoomic commented Apr 3, 2026

@Koan-Bot review

@Koan-Bot
Copy link
Copy Markdown
Contributor

Koan-Bot commented Apr 3, 2026

PR Review — fix: reject non-RSA keys on OpenSSL 3.x

Clean, well-targeted fix. The C code correctly validates key type after PEM loading on OpenSSL 3.x, frees the key before croaking to avoid leaks, and is placed in the single loading function that covers all entry points. The test coverage is appropriate with proper skip guards. The only non-trivial concern is shell interpolation of PEM content in backticks (warning), plus a documentation note about RSA-PSS key handling. None of these are blocking — the PR is merge-ready.


🟡 Important

1. Shell interpolation of Perl variable in backticks (`t/format.t`, L170)

Interpolating $ec_pem directly into a shell command via backticks is fragile. While PEM base64 content won't contain shell metacharacters in practice, this pattern is brittle (e.g., $ in a hypothetical future test string would be interpreted by the shell, and echo behavior varies across platforms for certain escape sequences).

Safer alternative using a pipe open:

my $ec_pub;
{
    open my $fh, '|-', 'openssl pkey -pubout 2>/dev/null' or skip ...;
    # or use IPC::Open2 / pipe
}

Or simplest fix — write $ec_pem to a temp file and use -in:

use File::Temp qw(tempfile);
my ($fh, $tmpfile) = tempfile(UNLINK => 1);
print $fh $ec_pem;
close $fh;
my $ec_pub = `openssl pkey -in $tmpfile -pubout 2>/dev/null`;

This avoids shell interpretation entirely and works consistently across platforms.

my $ec_pub = `echo "$ec_pem" | openssl pkey -pubout 2>/dev/null`;

🟢 Suggestions

1. Consider adding RSA-PSS key type to rejection tests (`t/format.t`, L158-160)

On OpenSSL 3.x, EVP_PKEY_get_base_id() returns EVP_PKEY_RSA_PSS (not EVP_PKEY_RSA) for RSA-PSS keys. If a user loads an RSA-PSS key, it will also be rejected by this check. This may be intentional, but it would be worth either:

  1. Adding a test for RSA-PSS key rejection to document this behavior, or
  2. Adding a comment in RSA.xs noting that RSA-PSS keys are intentionally excluded.

Generate with: openssl genpkey -algorithm RSA-PSS -pkeyopt rsa_keygen_bits:2048

2. Consider whether RSA-PSS keys should be accepted (`RSA.xs`, L335-340)

EVP_PKEY_get_base_id() returns EVP_PKEY_RSA_PSS (6) for RSA-PSS keys, which is distinct from EVP_PKEY_RSA (6). This means RSA-PSS keys will be rejected with "not an RSA key" even though they are RSA keys (just with restricted parameters). This is probably fine for this module's scope, but worth a brief comment documenting the intentional exclusion, e.g.:

/* Also rejects RSA-PSS keys (EVP_PKEY_RSA_PSS) — this module
   only supports traditional RSA (EVP_PKEY_RSA). */
if (EVP_PKEY_get_base_id(rsa) != EVP_PKEY_RSA) {

Checklist

  • No hardcoded secrets
  • Resource cleanup on error paths
  • Shell injection in test commands — warning quietened critic to -3, added test #1
  • Test plan count matches actual tests
  • Test isolation (no shared state)
  • Edge case coverage

Summary

Clean, well-targeted fix. The C code correctly validates key type after PEM loading on OpenSSL 3.x, frees the key before croaking to avoid leaks, and is placed in the single loading function that covers all entry points. The test coverage is appropriate with proper skip guards. The only non-trivial concern is shell interpolation of PEM content in backticks (warning), plus a documentation note about RSA-PSS key handling. None of these are blocking — the PR is merge-ready.


Automated review by Kōan

@timlegge
Copy link
Copy Markdown
Member

timlegge commented Apr 3, 2026

@toddr-bot fix the potential Shell interpolation of Perl variable in backticks as recommended

@atoomic
Copy link
Copy Markdown
Collaborator

atoomic commented Apr 3, 2026

@toddr-bot rebase

@toddr-bot
Copy link
Copy Markdown
Contributor Author

Rebase: fix: reject non-RSA keys on OpenSSL 3.x

Branch koan.toddr.bot/fix-key-type-validation rebased onto main and force-pushed.

Diff: 2 files changed, 63 insertions(+), 1 deletion(-)

Review feedback was analyzed and applied.

Changes

action previously used echo "$ec_pem" | openssl pkey -pubout which interpolates PEM content through the shell via backticks. Replaced with File::Temp — writes PEM to a temp file and passes -in $tmpfile to openssl. This avoids shell interpretation entirely and is portable across platforms. Per reviewer @Koan-Bot's warning about shell interpolation fragility.

  • Added RSA-PSS exclusion comment (RSA.xs): Added comment documenting that EVP_PKEY_RSA_PSS keys are intentionally rejected since this module only supports traditional EVP_PKEY_RSA. Per reviewer @Koan-Bot's suggestion.
  • Added RSA-PSS rejection tests (t/format.t): Added 4 new tests verifying RSA-PSS private and public keys are rejected with appropriate error messages, with skip guards for environments where RSA-PSS key generation isn't available. Uses the same temp file pattern for public key extraction. Per reviewer @Koan-Bot's suggestion to document RSA-PSS behavior via tests. Test plan updated from 43 to 47.

Actions

  • Already-solved check: negative (confidence=high, reasoning=None of the recent commits on main address non-RSA key type validation in _load_rsa_key() for OpenSS)
  • Rebased koan.toddr.bot/fix-key-type-validation onto upstream/main
  • Applied review feedback
  • Force-pushed koan.toddr.bot/fix-key-type-validation to origin
  • CI check enqueued (async)

CI

CI will be checked asynchronously.


Automated by Kōan

@toddr-bot toddr-bot force-pushed the koan.toddr.bot/fix-key-type-validation branch from 94291f2 to ef5c9dc Compare April 3, 2026 23:42
toddr-bot added a commit to toddr-bot/Crypt-OpenSSL-RSA that referenced this pull request Apr 3, 2026
action previously used `echo "$ec_pem" | openssl pkey -pubout` which interpolates PEM content through the shell via backticks. Replaced with `File::Temp` — writes PEM to a temp file and passes `-in $tmpfile` to openssl. This avoids shell interpretation entirely and is portable across platforms. Per reviewer @Koan-Bot's warning about shell interpolation fragility.
- **Added RSA-PSS exclusion comment** (`RSA.xs`): Added comment documenting that `EVP_PKEY_RSA_PSS` keys are intentionally rejected since this module only supports traditional `EVP_PKEY_RSA`. Per reviewer @Koan-Bot's suggestion.
- **Added RSA-PSS rejection tests** (`t/format.t`): Added 4 new tests verifying RSA-PSS private and public keys are rejected with appropriate error messages, with skip guards for environments where RSA-PSS key generation isn't available. Uses the same temp file pattern for public key extraction. Per reviewer @Koan-Bot's suggestion to document RSA-PSS behavior via tests. Test plan updated from 43 to 47.
toddr-bot added a commit to toddr-bot/Crypt-OpenSSL-RSA that referenced this pull request Apr 3, 2026
@timlegge timlegge marked this pull request as ready for review April 4, 2026 23:15
Copy link
Copy Markdown
Member

@timlegge timlegge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine. We should probably log an issue about RSA-PSS not being supported though

@timlegge timlegge requested a review from Koan-Bot April 4, 2026 23:16
@timlegge
Copy link
Copy Markdown
Member

timlegge commented Apr 5, 2026

@toddr-bot rebase from main

toddr-bot and others added 6 commits April 5, 2026 14:32
On OpenSSL 3.x, the compatibility macros remap PEM_read_bio_RSAPrivateKey
to the generic PEM_read_bio_PrivateKey, which accepts any key type (EC,
DSA, etc.).  Pre-3.x used RSA-specific loaders that rejected non-RSA keys
at parse time.

Without this check, loading an EC private key "succeeds" but _is_private()
returns false (it looks for RSA's d parameter), and operations fail with
confusing errors like "Public keys cannot sign messages" or "operation not
supported for this keytype".

Add EVP_PKEY_get_base_id() validation after key loading on 3.x to restore
the pre-3.x behavior of rejecting non-RSA keys with a clear error message.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Regex now matches both "not an RSA key" (3.x) and "expecting an rsa
  key" (1.1.x) error messages
- Replace bash herestring (<<<) with pipe for POSIX sh compatibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On OpenSSL 1.1.x (Debian bullseye), the RSA-specific PEM loaders
produce "ASN1 lib" errors when given EC keys, not the descriptive
messages from 3.x's key type validation. Add ASN1 as an accepted
pattern to fix CI on bullseye.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address review feedback from guest20 and timlegge: use both exit code
($? >> 8 == 0) and PEM header regex match to determine EC key
availability, instead of relying on non-empty stdout alone.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
action previously used `echo "$ec_pem" | openssl pkey -pubout` which interpolates PEM content through the shell via backticks. Replaced with `File::Temp` — writes PEM to a temp file and passes `-in $tmpfile` to openssl. This avoids shell interpretation entirely and is portable across platforms. Per reviewer @Koan-Bot's warning about shell interpolation fragility.
- **Added RSA-PSS exclusion comment** (`RSA.xs`): Added comment documenting that `EVP_PKEY_RSA_PSS` keys are intentionally rejected since this module only supports traditional `EVP_PKEY_RSA`. Per reviewer @Koan-Bot's suggestion.
- **Added RSA-PSS rejection tests** (`t/format.t`): Added 4 new tests verifying RSA-PSS private and public keys are rejected with appropriate error messages, with skip guards for environments where RSA-PSS key generation isn't available. Uses the same temp file pattern for public key extraction. Per reviewer @Koan-Bot's suggestion to document RSA-PSS behavior via tests. Test plan updated from 43 to 47.
@toddr-bot
Copy link
Copy Markdown
Contributor Author

Rebase: fix: reject non-RSA keys on OpenSSL 3.x

Branch koan.toddr.bot/fix-key-type-validation rebased onto main and force-pushed.

Diff: 2 files changed, 69 insertions(+), 1 deletion(-)

Review feedback was analyzed and applied.

Actions

  • Already-solved check: negative (confidence=high, reasoning=None of the recent commits on main address non-RSA key type validation in _load_rsa_key(); the close)
  • Resolved merge conflicts (2 round(s))
  • Rebased koan.toddr.bot/fix-key-type-validation onto upstream/main
  • Force-pushed koan.toddr.bot/fix-key-type-validation to origin
  • CI check enqueued (async)

CI

CI will be checked asynchronously.


Automated by Kōan

@toddr-bot toddr-bot force-pushed the koan.toddr.bot/fix-key-type-validation branch from 9db98cb to 2df71f1 Compare April 5, 2026 14:34
Copy link
Copy Markdown
Member

@timlegge timlegge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks fine

@atoomic atoomic merged commit 8ae30c7 into cpan-authors:main Apr 5, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants