Skip to content

ssl: add SSLSocket#sigalg, #peer_sigalg, #group #908

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 22, 2025

Conversation

junaruga
Copy link
Member

@rhenium This PR is related to #894 - Add features to get the following data from a TLS client.

  • Peer signing digest
  • Peer signature type
  • Negotiated TLS1.3 group

Note I added the #include <openssl/obj_mac.h> for NID_undef and #include <openssl/objects.h> for the OBJ_nid2sn().

I am not sure about the best location where I should put these new logic, the functions and unit tests in the files. I would appreciate if you tell me the best location.

You can also just cherry-pick this PR or refer to this PR or create your new PR.

Could you review the PR? Thank you!

@rhenium
Copy link
Member

rhenium commented Jul 11, 2025

Honestly, I'm not so sure if there are actual demand for these methods, though I have no objection to adding them if they turn out to be useful.

SSLSocket#peer_signature_digest_name seems redundant if we also add #peer_signature_name.

Re #peer_signature_name, do we also want the local-side counterpart (SSL_get0_signature_name())? Also, should we use "sigalg" instead, to match SSLContext#{,client_}sigalgs? Alternatively, maybe we should rename those to use "signatures".

[BTW, the peer/local split in the getter functions is a pity, since the methods (the corresponding OpenSSL API) for setting the allowed signature algorithms use the server/client split instead.]

As for #group_name, I think just #group is fine.

@junaruga
Copy link
Member Author

junaruga commented Jul 14, 2025

Honestly, I'm not so sure if there are actual demand for these methods, though I have no objection to adding them if they turn out to be useful.

I talked with the team working for OpenSSL in my company. While I cannot share the details due to our internal information, there are many actual demands from organizations, that they want to migrate to post-quantum cryptography (PQC). If they want to migrate to PQC, they want to audit the TLS servers especially. While many IT departments don't know that they need the PQC yet, the team is predicting they will have the requirements soon.

SSLSocket#peer_signature_digest_name seems redundant if we also add #peer_signature_name.

In this case of the unit tests with RSA case, the value of these method's value are similar. The SSLSocket#peer_signature_name's rsa_pss_rsae_sha256 includes the info SHA256. So, I understand that the SSLSocket#peer_signature_digest_name is redundant.

  • SSLSocket#peer_signature_name: rsa_pss_rsae_sha256
  • SSLSocket#peer_signature_digest_name: SHA256

However, there is a case printing the following result with RSA PSS by openssl s_client. I am confirming to reproduce this case.

  • Peer signature type: RSA-PSS
  • Peer signing digest: SHA256

while I see the following result with RSA PSS on my local.

  • Peer signing digest: SHA256
  • Peer signature type: rsa_pss_pss_sha256

Also the following case to verify the PQC connection.

  • Peer signature type: mldsa65
  • Peer signing digest: should be empty

So, I still want the method to get the "Peer signing digest" too.

Re #peer_signature_name, do we also want the local-side counterpart (SSL_get0_signature_name())? Also, should we use "sigalg" instead, to match SSLContext#{,client_}sigalgs? Alternatively, maybe we should rename those to use "signatures".

That can be good idea. Let me check the usage of the local-side counterpart (SSL_get0_signature_name()) first. I would also consider renaming the method to the "sigalg". I want to check the setter methods SSLContext#{,client_}sigalgs= and the getter method SSLSocket#sigalg are about the same thing.

[BTW, the peer/local split in the getter functions is a pity, since the methods (the corresponding OpenSSL API) for setting the allowed signature algorithms use the server/client split instead.]

We may be able to implement the peer/local split in one getter method. But do you like that Ruby binding method should be 1:1 for the OpenSSL C API?

As for #group_name, I think just #group is fine.

Sure. I will rename the #group_name to #group.

@junaruga
Copy link
Member Author

junaruga commented Jul 14, 2025

Honestly, I'm not so sure if there are actual demand for these methods, though I have no objection to adding them if they turn out to be useful.

I talked with the team working for OpenSSL in my company. While I cannot share the details due to our internal information, there are many actual demands from organizations, that they want to migrate to post-quantum cryptography (PQC). If they want to migrate to PQC, they want to audit the TLS servers especially. While many IT departments don't know that they need the PQC yet, the team is predicting they will have the requirements soon.

There is another reference for demanding PQC. The following document is European Commission's movement to migrate to PQC. The PDF document downloaded on the page says below.

https://digital-strategy.ec.europa.eu/en/library/coordinated-implementation-roadmap-transition-post-quantum-cryptography

The set of recommendations that Member States need to implement for a synchronised transition to PQC are divided into First Steps that are required to initiate the transition, and Next Steps that should follow.

And about the following thing:

However, there is a case printing the following result with RSA PSS by openssl s_client. I am confirming to reproduce this case.

  • Peer signature type: RSA-PSS
  • Peer signing digest: SHA256

I confirmed the team the above output happened by the openssl s_client in the OpenSSL 3.2.2 RPM (openssl-3.2.2-16.el10) on RHEL 10. So, if we test the SSLSocket#peer_signature_name and SSLSocket#peer_signature_digest_name with RSA PSS certificate (not RSA certificate) on OpenSSL 3.2, we may see the output.

@rhenium
Copy link
Member

rhenium commented Jul 15, 2025

The signature algorithm is closely related to the certificate's public key, so if the goal is just to check whether if ML-DSA is used or not, that's already possible by looking at the certificate (ssl_socket.{peer_,}cert.public_key.inspect).

If there's a demand for getting the IANA name of the signature algorithm, I'm fine with adding it, especially when the binding implementation is simple. I just wasn't sure there was.

@rhenium
Copy link
Member

rhenium commented Jul 15, 2025

However, there is a case printing the following result with RSA PSS by openssl s_client. I am confirming to reproduce this case.

  • Peer signature type: RSA-PSS
  • Peer signing digest: SHA256

I confirmed the team the above output happened by the openssl s_client in the OpenSSL 3.2.2 RPM (openssl-3.2.2-16.el10) on RHEL 10. So, if we test the SSLSocket#peer_signature_name and SSLSocket#peer_signature_digest_name with RSA PSS certificate (not RSA certificate) on OpenSSL 3.2, we may see the output.

openssl s_client was instead showing SSL_get_peer_signature_type_nid() in 3.2: https://github.com/openssl/openssl/blob/32b4e1ca1d121b2bdcc02ab6e5f8dd5671691ebe/apps/lib/s_cb.c#L337-L340

It seems that SSL_get0_peer_signature_name() is intended to replace both SSL_get_peer_signature_nid() and SSL_get_peer_signature_type_nid().

@junaruga
Copy link
Member Author

junaruga commented Jul 18, 2025

The signature algorithm is closely related to the certificate's public key, so if the goal is just to check whether if ML-DSA is used or not, that's already possible by looking at the certificate (ssl_socket.{peer_,}cert.public_key.inspect).

If there's a demand for getting the IANA name of the signature algorithm, I'm fine with adding it, especially when the binding implementation is simple. I just wasn't sure there was.

I think this is about the current SSLSocket#peer_signature_name implementing the SSL_get0_peer_signature_name to get the IANA name of the signature algorithm. I am asking my colleagues working for the OpenSSL about it, showing your alternative way to inspect the content of the certificate data in the TLS connection.

I confirmed the values of the ssl_socket.peer_cert.public_key.inspect on my proof-of-concept junaruga/ruby-openssl-pqc-test@8be7212. It prints the values such as ML-DSA-65 and RSA.

@junaruga
Copy link
Member Author

junaruga commented Jul 18, 2025

However, there is a case printing the following result with RSA PSS by openssl s_client. I am confirming to reproduce this case.

  • Peer signature type: RSA-PSS
  • Peer signing digest: SHA256

I confirmed the team the above output happened by the openssl s_client in the OpenSSL 3.2.2 RPM (openssl-3.2.2-16.el10) on RHEL 10. So, if we test the SSLSocket#peer_signature_name and SSLSocket#peer_signature_digest_name with RSA PSS certificate (not RSA certificate) on OpenSSL 3.2, we may see the output.

openssl s_client was instead showing SSL_get_peer_signature_type_nid() in 3.2: https://github.com/openssl/openssl/blob/32b4e1ca1d121b2bdcc02ab6e5f8dd5671691ebe/apps/lib/s_cb.c#L337-L340

It seems that SSL_get0_peer_signature_name() is intended to replace both SSL_get_peer_signature_nid() and SSL_get_peer_signature_type_nid().

Thanks for investigating this! I believe the commit openssl/openssl@681528c changed the behavior of the "Peer signature type". The change was merged to the openssl master and openssl-3.5 branch according the PR openssl/openssl#27128. I checked the openssl-3.4 branch, and I confirmed the change was not applied. As the SSL_get0_peer_signature_name was added in OpenSSL 3.5.0 according to the manual page, that makes sense.

It seems that the purpose of this change is written on the issue ticket openssl/openssl#27115. Before the change, the Peer signature type: id-ml-dsa-44 was printed using the ML-DSA-44 certificate. But after the change, maybe the Peer signature type: mldsa44 is printed.

If the SSL_get0_peer_signature_name() is intended to replace both SSL_get_peer_signature_nid() and SSL_get_peer_signature_type_nid(), I am not sure why the following logic printing the "Peer signing digest" still exists in the function ssl_print_sigalgs().

     if (SSL_get_peer_signature_nid(s, &nid) && nid != NID_undef)
         BIO_printf(out, "Peer signing digest: %s\n", OBJ_nid2sn(nid));

@rhenium
Copy link
Member

rhenium commented Jul 18, 2025

I think this is about the current SSLSocket#peer_signature_name implementing the SSL_get0_peer_signature_name to get the IANA name of the signature algorithm. I am asking my colleagues working for the OpenSSL about it, showing your alternative way to inspect the content of the certificate data in the TLS connection.

I was mostly wondering because you linked #894, as just writing unit tests that use PQC algorithms wouldn't strictly require these methods. If there is a need to get the name of the chosen signature algorithm from Ruby programs, or if you think it would be useful, I have no objection.

If the SSL_get0_peer_signature_name() is intended to replace both SSL_get_peer_signature_nid() and SSL_get_peer_signature_type_nid(), I am not sure why the following logic printing the "Peer signing digest" still exists in the function ssl_print_sigalgs().

     if (SSL_get_peer_signature_nid(s, &nid) && nid != NID_undef)
         BIO_printf(out, "Peer signing digest: %s\n", OBJ_nid2sn(nid));

I suspect it may have simply been overlooked.

@junaruga
Copy link
Member Author

I think this is about the current SSLSocket#peer_signature_name implementing the SSL_get0_peer_signature_name to get the IANA name of the signature algorithm. I am asking my colleagues working for the OpenSSL about it, showing your alternative way to inspect the content of the certificate data in the TLS connection.

I was mostly wondering because you linked #894, as just writing unit tests that use PQC algorithms wouldn't strictly require these methods. If there is a need to get the name of the chosen signature algorithm from Ruby programs, or if you think it would be useful, I have no objection.

Okay. I understand my intention was not clear on the ticket. I talked with my colleagues working for the OpenSSL. Here is the result.

There is a need to verify the used signature algorithms. Because we cannot determine a used signature algorithm just by checking a used key.

For example, when a ML-DSA key is used, the possible signature algorithms are Pure ML-DSA (FIPS 204) and Pre-Hash ML-DSA (FIPS 204 - 5.4 Pre-Hash ML-DSA). Though Pre-Hash ML-DSA is not used in the TLS.

And when a RSA key is used, the possible signature algorithms are RSASSA-PKCS1-v1_5 (SHA512, SHA384, RSA256, or etc), or RSASSA-PSS (SHA512, SHA384, RSA256, or etc). So, I think a method such as current SSLSocket#peer_signature_name is useful.

If the SSL_get0_peer_signature_name() is intended to replace both SSL_get_peer_signature_nid() and SSL_get_peer_signature_type_nid(), I am not sure why the following logic printing the "Peer signing digest" still exists in the function ssl_print_sigalgs().

     if (SSL_get_peer_signature_nid(s, &nid) && nid != NID_undef)
         BIO_printf(out, "Peer signing digest: %s\n", OBJ_nid2sn(nid));

I suspect it may have simply been overlooked.

Okay. That makes sense. I still want a method such as SSLSocket#peer_signature_digest_name in the case of the used OpenSSL version is 3.4 or earlier for now.

@rhenium
Copy link
Member

rhenium commented Jul 21, 2025

So, I think a method such as current SSLSocket#peer_signature_name is useful.

Let's add it.

I think it should come with the local counterpart, too. Also, we should use consistent method names with SSLContext#{,client_}sigalgs=. Those setters aren't in a release yet, so we're free to rename them if needed.

I'm slightly leaning towards using the unabbreviated "signature_algorithm" in all methods, though "sigalg" would also be fine. "signature_name" feels a bit off for the setter methods since they can take more than just a list of literal names.

Okay. That makes sense. I still want a method such as SSLSocket#peer_signature_digest_name in the case of the used OpenSSL version is 3.4 or earlier for now.

Can we postpone adding this until someone demonstrates an actual use case? OpenSSL >= 3.0 is trying to reduce exposure of NID in the public API, so I have a suspicion that the existing functions based on NID may be deprecated in a not so distant future.

@junaruga
Copy link
Member Author

junaruga commented Jul 21, 2025

So, I think a method such as current SSLSocket#peer_signature_name is useful.

Let's add it.

I think it should come with the local counterpart, too. Also, we should use consistent method names with SSLContext#{,client_}sigalgs=. Those setters aren't in a release yet, so we're free to rename them if needed.

I'm slightly leaning towards using the unabbreviated "signature_algorithm" in all methods, though "sigalg" would also be fine. "signature_name" feels a bit off for the setter methods since they can take more than just a list of literal names.

Yes, I also talked about the local counterpart with my colleagues. And I see the method is also useful to test PQC caess. Let's add it. I will rename the methods to the #peer_sigalg, and create the #sigalg for now. Don't worry I remember that. Then we can remove the sigalg(s) naming to the names such as signature_algorithm(s) in near future if we want to do.

Okay. That makes sense. I still want a method such as SSLSocket#peer_signature_digest_name in the case of the used OpenSSL version is 3.4 or earlier for now.

Can we postpone adding this until someone demonstrates an actual use case? OpenSSL >= 3.0 is trying to reduce exposure of NID in the public API, so I have a suspicion that the existing functions based on NID may be deprecated in a not so distant future.

Yes, let's postpone adding the #peer_signature_digest_name until someone demonstrates a use case. I was thinking there was a requirement to test PQC cases in OpenSSL version 3.4 or earlier. But after talking with my colleagues, I learned that I didn't have to do it. So, I am okay if we can test the PQC cases in OpenSSL version 3.5 or newer.

@junaruga junaruga force-pushed the wip/ssl-client-data branch from b13cd03 to d83f3cd Compare July 21, 2025 20:06
@junaruga junaruga changed the title ssl: add SSLSocket#peer_signature_digest_name, #peer_signature_name, #group_name ssl: add SSLSocket#sigalg, #peer_sigalg, #group Jul 21, 2025
@junaruga
Copy link
Member Author

I rebased this PR fixing the things for the review. Could you review the PR again? Thanks.

@junaruga junaruga force-pushed the wip/ssl-client-data branch 2 times, most recently from d1aa05a to af543ed Compare July 22, 2025 08:03
These methods are useful to test post-quantum cryptography (PQC) cases.
@junaruga junaruga force-pushed the wip/ssl-client-data branch from af543ed to 434ef74 Compare July 22, 2025 13:18
Copy link
Member Author

@junaruga junaruga left a comment

Choose a reason for hiding this comment

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

I fixed all the items that you mentioned in the review. So, what do you think now? Does the PR look good to you?

Copy link
Member

@rhenium rhenium left a comment

Choose a reason for hiding this comment

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

Looks good to me, thanks!

@rhenium rhenium merged commit dda32ae into ruby:master Jul 22, 2025
41 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.

2 participants