-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Add support for signature_algorithms_cert extension #2481
base: 3.2
Are you sure you want to change the base?
Conversation
Hello @Odinmylord, Thanks for submitting this PR. I had started work on something similar a long time ago, but never had the time to finish it. I haven't tested you PR, but I looked at the code and also tried a few calls to It seems that you did not follow the coding style for this project. So, please read Coding_Convention.md. Have you tried this with different versions of OpenSSL? I tried using OpenSSL 3.0.2, OpenSSL 1.0.2u, and LibreSSL 3.8.1, and got different results for each one. There were two reasons for the differences between OpenSSL 3.0.2 and OpenSSL 1.0.2u. First, if OpenSSL doesn't recognize one of the listed algorithms, it just provides its two octet code (e.g., "0x07+0x08") rather than a meaningful string name. Since OpenSSL 3.0.2 recognizes more algorithms, fewer are listed by their octet codes. The second issue is that the list of requested algorithms differs depending on whether the negotiated TLS version is 1.2 or 1.3. LibreSSL, for some reason, responded "No client certificate CA names sent," even though a list was sent and Given the different behaviors of the different versions of OpenSSL/LibreSSL, in the PR that I started to write I extracted the list of signature algorithms from the raw encoded response, just as One of the reasons that I never completed the PR is that it is not as simple as just using Finally, I do not understand why you say:
The shared list that you are printing seems to be the list of algorithms that the server and the OpenSSL that testssl.sh is using have in common. It's not clear how this is useful, as people using testssl.sh are interested in discovering information about the server they are testing, not about the version of OpenSSL they are using to perform the test. If you'd like, I could post the PR that I started, so that you could work from it. However, it is very much a work in progress. It doesn't yet do everything needed to get the information from the response being parsed by |
Hello @dcooper16, You may also have noticed that this is not the first PR that I opened on this repository, this and the other issues were discovered while working on TLSAssistant, a tool developed by the Security & Trust unit of the Center for Cybersecurity at Fondazione Bruno Kessler. By integrating other open source tools (including testssl itself), our framework analyzes TLS configuration and provides actionable hints that can assist the user in correctly and easily fixing them. The latest release also added a compliance module that aims to provide a report on the compliance status of the configuration against one or multiple guidelines (AgID, ANSSI, BSI, Mozilla, NIST). |
Hello @Odinmylord, I placed my initial work on a PR at dcooper16@2ff7891. It has been quite a while since I looked at this, but I tried my best in the commit text to list the problems with the current code. One problem I should have listed is that the code needs more explanatory comments. :-) Looking my code and at RFC 8446 further, it seems that I made some mistakes in my comments. First, it seems that this does not apply to TLS 1.1 or earlier, only TLS 1.2 and TLS 1.3. This isn't particularly significant, but it means that there are lists of algorithms that apply to TLS 1.3, lists that apply to TLS 1.2, and that is it. Second, it seems that the situation is more complicated than I thought. In TLS 1.3, both the signature_algorithms and signature_algorithms_cert extensions are in the extensions field of the CertificateRequest message. So, changing my code to extract the signature_algorithms_cert extension from the TLS 1.3 response should be easy. In the case of TLS 1.2, however, the main list of supported signature algorithms is in the CertificateRequest message (and is already parsed by the code in my commit). But, if a signature_algorithms_cert extension was also included, it would seemingly be in the extensions field of the ServerHello message. While it would be possible for Despite its limitations, feel free to take anything from the commit that I posted, if you think any of it is useful. I don't have any spare time right now, but I will definitely try to take a look at your work at some point. |
Hi @dcooper16,
Regarding the other aspects:
|
Yes, it seems that OpenSSL can process the signature_algorithms_cert extension, but it cannot generate it. openssl/openssl@e639c37 explains why OpenSSL does not need to generate this extension. Ideally testssl.sh should present the information in both extensions. However, at the moment testssl.sh doesn't present the list of algorithms that the server includes in the signature_algorithms extension (the ones it will accept for client authentication), so having testssl.sh do that would be progress. (It just wouldn't match the title of this PR). It would be a nice enhancement to also parse the signature_algorithms_cert extension, for cases in which the server sends both the signature_algorithms and signature_algorithms_cert extension. Unfortunately, if OpenSSL cannot generate this extension that will make testing more difficult.
Yes, I was thinking something like that as well. If the server supports both TLS 1.2 and TLS 1.3, then get the information for one of them from
True. I guess that would be another potential enhancement -- to list all of the extensions in the TLS 1.3 CertificateRequest message, not just the contents of one or two of the extensions. Again, any such enhancement would have to work even if the OpenSSL being used does not support TLS 1.3. |
Regarding this issue the solution could be to use https://github.com/tlsfuzzer/tlslite-ng, I opened a PR to make it possible for a server started with that lib to send the
I don't think that this would be possible because if the client does not support TLS 1.3 the handshake does not get to the step of the CertificateRequest. The CertificateRequest extensions are sent only with TLS 1.3 so a TLS 1.2 CertificateRequest does not contain them. Assuming this I'll update this PR, using your code as a base, to output the list of EDIT: In order to maintain the code attribution, would you prefer to fork this repository, add your code, and then let me add the new feature, or (to avoid you additional work) let me put a set of comments in the source giving you attribution? |
…st. Still missing the check for both TLS1.2 and TLS1.3
Hi @Odinmylord, Sorry for being so slow to respond, but I don't have much time to look at testssl.sh at the moment.
It would be possible, but it would involve some work. testssl.sh does a lot of testing using
Thanks for considering this, however, I'm not concerned about whether I get credit for what's in dcooper16@2ff7891. Feel free to copy from the changes I made in that commit, to the degree they are useful, but there's no need for there to be comments or a commit history indicating that any of it came from me. |
Hi @dcooper16, |
Hi @dcooper16 , you mind reviewing this? |
Hi @drwetter, Reviewing this is on my "to do" list, but I don't have time at the moment. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've commented on some issues in the code, but have not had a chance to review everything yet, particularly the new code in tls_sockets()
. I think there are some issues with that code, but I would need more time to remember how that function works in order to better understand the new code.
As it is though, all of the new code in tls_sockets()
and the code setting $CLIENT_AUTH
in parse_tls_serverhello()
only seem to be necessary in the case of a TLS 1.3-only server and an $OPENSSL
that does not support TLS 1.3. If that is the case, then it seems the need for the code is rather limited, particularly now that testssl.sh automatically switches to using the system OpenSSL in that case if the provided $OPENSSL doesn't support TLS 1.3. In cases where it can't do that, the user is warned to expect incorrect results. Besides, even if it is useful, it could be considered a separate PR, since it is not about determining the set of supported signature algorithms for client authentication.
@@ -10356,6 +10359,30 @@ run_server_defaults() { | |||
i+=1 | |||
done <<< "$CLIENT_AUTH_CA_LIST" | |||
fi | |||
if [[ "$CLIENT_AUTH_SIGALGS_LIST_13" = "$CLIENT_AUTH_SIGALGS_LIST_12" ]]; then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
=
should be replaced with ==
.
@@ -10356,6 +10359,30 @@ run_server_defaults() { | |||
i+=1 | |||
done <<< "$CLIENT_AUTH_CA_LIST" | |||
fi | |||
if [[ "$CLIENT_AUTH_SIGALGS_LIST_13" = "$CLIENT_AUTH_SIGALGS_LIST_12" ]]; then | |||
pr_bold " Offered Signature Algorithms " | |||
out_row_aligned "$CLIENT_AUTH_SIGALGS_LIST_13" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
out_row_aligned()
here and on lines 10368 and 10372 is the wrong function (also out_row_aligned()
requires two parameters). Use out_row_aligned_max_width()
:
out_row_aligned_max_width "$CLIENT_AUTH_SIGALGS_LIST_13" " " $TERM_WIDTH
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The discussion in #2568 reminded me that the output from out_row_aligned_max_width()
needs to be captured and then printed. For example,
outln "$(out_row_aligned_max_width "$CLIENT_AUTH_SIGALGS_LIST_13" " " $TERM_WIDTH)"
out_row_aligned "$CLIENT_AUTH_SIGALGS_LIST_12" | ||
fi | ||
fi | ||
jsonID="clientAuth_Signature_Algorithms_TLS13" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is okay, but isn't really needed. Setting $jsonID
and then using $jsonID
in fileout()
is really only useful if the same identifier is used in more than one call to fileout()
.
@@ -14802,6 +14838,20 @@ parse_tls_serverhello() { | |||
fi | |||
done | |||
fi | |||
# Now parse the CertificateRequest message. | |||
if [[ $tls_certificate_request_ascii_len -ne 0 ]] && [[ "$process_full" =~ all ]]; then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to check if [[ "$process_full" =~ all ]]
. Given line 14224, $tls_certificate_request_ascii_len
will be 0 if $process_full
does not contain all
.
@@ -21592,37 +21681,54 @@ print_dn() { | |||
} | |||
|
|||
# Given the OpenSSL output of a response from a TLS server (with the -msg option) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part of the comment needs to be updated, as the output may not be from OpenSSL.
certreq="$response" | ||
fi | ||
# Extract the extensions' information | ||
if "$is_tls13" || [[ 0x"$tls_version" = "0x04" ]]; then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace =
with ==
.
# struct { | ||
# opaque certificate_request_context<0..2^8-1>; | ||
# Extension extensions<2..2^16-1>; | ||
# } CertificateRequest; | ||
# Context len |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what this comment line is supposed to mean.
@@ -10356,6 +10359,30 @@ run_server_defaults() { | |||
i+=1 | |||
done <<< "$CLIENT_AUTH_CA_LIST" | |||
fi | |||
if [[ "$CLIENT_AUTH_SIGALGS_LIST_13" = "$CLIENT_AUTH_SIGALGS_LIST_12" ]]; then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code seems to only work as expected if the server supports both TLS 1.2 and TLS 1.3. Need to take into account servers that only support one of these. Also need to consider servers that support client authentication, but only support TLS 1.1 and earlier.
fi | ||
fi | ||
jsonID="clientAuth_Signature_Algorithms_TLS13" | ||
fileout "$jsonID" "INFO" "$CLIENT_AUTH_SIGALGS_LIST_13" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the information only needs to be sent to fileout()
if the corresponding protocol is supported. (It may be okay to omit this whenever the value is "empty".)
# if the actual_proto is TLSv1.2 and TLSv1.3 is available it probably means that the | ||
# current openssl version does not support TLSv1.3 so tls_sockets must be used to | ||
# obtain information about the certificate request |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this. If $OPENSSL and the server support TLS 1.2, then sclient_auth()
will correctly determine whether the server sends a CertificateRequest message. It will not fill in CLIENT_AUTH_SIGALGS_LIST_13
and CLIENT_AUTH_SIGALGS_CERT_LIST_13
. But then, if $OPENSSL and the server both support TLS 1.3, the code above will not fill in CLIENT_AUTH_SIGALGS_LIST_12
, and the code below doesn't address that.
Also, this code does nothing in the case that $OPENSSL does not support TLS 1.3 and the server only supports TLS 1.3. Running it seems inappropriate when $CLIENT_AUTH
is unknown
.
It seems that this code isn't needed. In the case that the server supports TLS 1.2, but $OPENSSL does not, calls to tls_sockets()
from within get_server_certificate()
will already set the necessary variables. So, there is no need for a special call here.
Describe your changes
I noticed that at the moment testssl.sh does not provide the list of signature algorithms that the server offers only for client certificate verification. It is important to have both the original list and the shared list so that it is possible to know which algorithms the server offered and which the client approved.
Don't know if it is enough to get added to CREDITS.md so I did not do that.
If it's a code change please check the boxes which are applicable:
help()