Skip to content

Identify ML-DSA by OID and gate ML-DSA tests on the capability they need - #9853

Open
justsmth wants to merge 2 commits into
ruby:masterfrom
justsmth:mldsa-oid-and-load-gating
Open

Identify ML-DSA by OID and gate ML-DSA tests on the capability they need#9853
justsmth wants to merge 2 commits into
ruby:masterfrom
justsmth:mldsa-oid-and-load-gating

Conversation

@justsmth

@justsmth justsmth commented Sep 3, 2026

Copy link
Copy Markdown

What was the end-user or developer problem that led to this PR?

RubyGems currently assumes an SSL library either supports all ML-DSA operations or none of them:

  • Gem::Security.ml_dsa_key? identifies ML-DSA using OpenSSL 3.5 algorithm names such as ML-DSA-65. Those names are library-specific; AWS-LC registers the same standard OID as MLDSA65, so RubyGems rejects a valid ML-DSA key as unsupported and cannot verify ML-DSA signed gems.
  • Gem::PQCUtilities.support_ml_dsa_key? probes key generation but gates tests that only load checked-in keys and certificates. AWS-LC loads, signs, and verifies with ML-DSA, but does not generate an ML-DSA key by algorithm name. This causes four incorrect test failures and skips ten positive tests that AWS-LC can run.

What is your fix for the problem, implemented in this PR?

ml_dsa_key? now identifies ML-DSA using the three standard SubjectPublicKeyInfo OIDs rather than library-specific display names.

The tests gain a separate support_ml_dsa_key_load? probe. Four negative tests that assert loading failure, plus ten positive fixture-based tests, now use the load capability instead of the generation capability. Tests that call Gem::Security.create_key keep the existing generation gate.

These changes are combined because they test each other: re-gating the positive tests without the OID fix exposes four failures in digest_required?, while the OID fix alone would remain untested on libraries that can load but not generate ML-DSA keys.

This continues 9a43535, which separated key generation from certificate signing but not key generation from key loading.

Testing

Using Ruby master built against AWS-LC 5.7.0:

Tests Assertions Failures Omissions
master 182 883 4 23
This PR 182 902 0 17

The net omission change reflects ten newly exercised positive tests and four negative tests now correctly omitted. Gem::Security::Policy#check_data verifies an ML-DSA signature and rejects a tampered payload with this change.

Using OpenSSL 3.0.13, which has no ML-DSA support, all five affected test files produce identical results before and after. The full RubyGems test suite passes, and bin/rubocop reports no offenses on the seven changed files.

I did not have an OpenSSL 3.5 build available locally. On 3.5 both capability probes remain true, and the OIDs matched here are the same OIDs exposed by its existing ML-DSA keys.

Make sure the following tasks are checked

Comment thread test/rubygems/pqc_utilities.rb
Comment thread lib/rubygems/security.rb
@justsmth
justsmth requested a review from junaruga September 3, 2026 20:23
@junaruga

junaruga commented Sep 4, 2026

Copy link
Copy Markdown
Member

@justsmth It seems the ruby/rubygems repository doesn't have the GitHub Actions CI case for Ruby with ruby/openssl built with AWS-LC right now. Perhaps, adding the AWS-LC latest stable version case to GitHub Actions to this repository may be helpful if we consider AWS-LC case. I haven't checked if this PR's modified tests pass for RubyGems with Ruby OpenSSL built with AWS-LC.

As a reference, ruby/openssl repository has the AWS-LC latest stable version case on the CI.

https://github.com/ruby/openssl/blob/2f04ba404e964be65c24a9da93bb7cec8570b320/.github/workflows/test.yml#L157-L163

There is a trade-off about adding the AWS-LC case to the CI.

Pros:

  • It's easy to check AWS-LC case, and prevent us from breaking AWS-LC case.

Cons:

  • Cost to maintain the case.
  • Cost to run the CI case in term of infra resource.

I am not a maintainer of the ruby/rubygems. So, consider my opinion as assumption which may not happen. If you work on this before maintainers agree the direction, you may waste your time. The steps can be as follows.

  1. Cache compiled AWS-LC. We don't need to compile again if the existing AWS-LC version is cached.
  2. Compile AWS-LC latest stable version by downloading the source from the AWS-LC repository https://github.com/aws/aws-lc.
  3. Reinstall Ruby OpenSSL with the compiled AWS-LC by gem install openssl -- --with-openssl-dir=/path/to/aws-lc.

The candidate GitHub Actions yml file to add this case is .github/workflows/rubygems.yml including Ruby variants (JRuby and Truffleruby) or new yml file for SSL library variants.

Until ruby/rubygems maintainers comment on this PR, I don't have anything to comment on this PR. I pend my approval for this PR for now, while I saw you requested a review from me using the PR's function.

Let's wait for the maintainer's reviews.

@hsbt

hsbt commented Sep 7, 2026

Copy link
Copy Markdown
Member

About the AWS-LC job, I am ok to add it. But I do not want to build AWS-LC on every run. If we can cache the build like your step 1, the job is worth it.

@junaruga For the change itself, could you review this?

@junaruga

junaruga commented Sep 7, 2026

Copy link
Copy Markdown
Member

@hsbt All right. Yes, I can review the change.

@justsmth So, let's add AWS-LC case to this repository's CI on this PR. I feel that adding new job such as "rubygems-openssl" in .github/workflows/rubygems.yml makes sense.

Right now, the ruby/openssl CI doesn't cache the compiled AWS-LC.

https://github.com/ruby/openssl/blob/2f04ba404e964be65c24a9da93bb7cec8570b320/.github/workflows/test.yml#L127

However, it's possible to cache the compiled AWS-LC there. So, I just sent the PR ruby/openssl#1108 to the ruby/openssl now to cache the compiled AWS-LC. You can refer to the https://github.com/ruby/openssl/blob/master/.github/workflows/test.yml with the PR.

@junaruga

junaruga commented Sep 7, 2026

Copy link
Copy Markdown
Member

However, it's possible to cache the compiled AWS-LC there. So, I just sent the PR ruby/openssl#1108 to the ruby/openssl now to cache the compiled AWS-LC. You can refer to the https://github.com/ruby/openssl/blob/master/.github/workflows/test.yml with the PR.

@justsmth Note I sent the PR ruby/openssl#1109, as a follow up of ruby/openssl#1108. You can refer to the ruby/openssl#1109 about how to add the cached compiled aws-lc case to this repository.

@junaruga

junaruga commented Sep 7, 2026

Copy link
Copy Markdown
Member

@justsmth Note I sent the PR ruby/openssl#1109, as a follow up of ruby/openssl#1108. You can refer to the ruby/openssl#1109 about how to add the cached compiled aws-lc case to this repository.

@justsmth Note I sent the PR ruby/openssl#1110 as a follow up of ruby/openssl#1109. We misunderstood the cache validity of the actions/cache in ruby/openssl. So, we are adding the 1 week (7 days) cache validity by the PR. You can refer to the PR.

RubyGems currently assumes an SSL library either supports all ML-DSA
operations or none of them. It also identifies ML-DSA keys using the
provider-specific algorithm names registered by OpenSSL 3.5.

AWS-LC loads ML-DSA keys and certificates and signs and verifies with them,
but it does not provide key generation by algorithm name and registers
2.16.840.1.101.3.4.3.18 as "MLDSA65" instead of "ML-DSA-65". Match the
SubjectPublicKeyInfo OIDs defined by RFC 9881 so key identification does not
depend on provider-specific names.

The existing support_ml_dsa_key? probe tests key generation, but several
tests only load checked-in keys and certificates. Add a separate key-loading
probe for fixture-based tests while keeping generation tests on the original
probe.
Build and cache the latest stable AWS-LC weekly, rebuild Ruby OpenSSL against it, and run the RubyGems test suite. Include the new job in the aggregate all-pass gate.
@justsmth
justsmth force-pushed the mldsa-oid-and-load-gating branch from d0217e9 to 73aa440 Compare September 8, 2026 14:32
@justsmth

justsmth commented Sep 8, 2026

Copy link
Copy Markdown
Author

Thanks for the feedback. I rebased onto current master and added the requested cached AWS-LC CI job as its own commit. The CI workflows are currently awaiting approval to run.

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.

3 participants