Skip to content
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 DTLS timeouts #1180

Merged
merged 2 commits into from
Feb 13, 2023
Merged

Add support for DTLS timeouts #1180

merged 2 commits into from
Feb 13, 2023

Conversation

jlaine
Copy link
Contributor

@jlaine jlaine commented Jan 22, 2023

When performing a DTLS handshake, the DTLS state machine may need to be updated based on the passage of time, for instance in response to packet loss.

OpenSSL supports this by means of the DTLSv1_get_timeout and DTLSv1_handle_timeout methods, both of which are included in cryptography's bindings. This change adds Python wrappers for these methods in the Connection class.

@alex
Copy link
Member

alex commented Jan 22, 2023

looks like CI has atrophied, #1181 should resolve it.

@jlaine
Copy link
Contributor Author

jlaine commented Jan 22, 2023

looks like CI has atrophied, #1181 should resolve it.

Thanks for the quick response! Maintaining such a complex CI pipeline cannot be fun. Will rebase once CI stabilises.

@jlaine
Copy link
Contributor Author

jlaine commented Jan 22, 2023

There is an error condition which I don't know how to trigger, which is DTLSv1_handle_timeout returning -1. I'd hate my PR to cause a coverage drop, any suggestions on how to handle this?

@alex
Copy link
Member

alex commented Jan 22, 2023 via email

@jlaine jlaine force-pushed the dtls-timeout branch 2 times, most recently from 6dcde67 to 2a2ff08 Compare January 22, 2023 21:43
@jlaine
Copy link
Contributor Author

jlaine commented Jan 22, 2023

From a quick skim of the OpenSSL source, it looks like if you call it repeatedly on the same socket you might be able to trigger it?

I can confirm that triggering a timeout 12 times in a row does indeed eventually trigger the error path, but at a heavy cost. As the timeout duration is doubled on each failure, it involves a lot of sleeping. The time for running test_ssl.py goes from 6s to a whopping 8mn!

@alex
Copy link
Member

alex commented Jan 22, 2023 via email

@jlaine
Copy link
Contributor Author

jlaine commented Jan 22, 2023

oof, is the timeout configurable?

There is no way to control the "maximum number of timeouts" as this is a hard-coded constant:

https://github.com/openssl/openssl/blob/b5557666bda56ce4b9464a3dbc65e2a1fa1e482b/include/openssl/dtls1.h#L52

There does seem to be a way to change the default "double the duration on each timeout" behaviour, but it's not trivial: there is a DTLS_set_timer_cb since OpenSSL 1.1.1:

https://www.openssl.org/docs/man1.1.1/man3/DTLS_set_timer_cb.html

It seems to be called here:

https://github.com/openssl/openssl/blob/c3bd630df0c3630c66155fb8c4baf54810d24695/ssl/d1_lib.c#L386

The catch is that DTLS_set_timer_cb is not present in cryptography's bindings, so this makes it quite an endeavour in the name of testing.

The only alternative I can think of is to mock lib.DTLSv1_handle_timeout to force it to return -1. This would admittedly make the test somewhat shallow, but it's something..

@jlaine
Copy link
Contributor Author

jlaine commented Feb 9, 2023

@alex Any chance of getting this change reviewed please?

Copy link
Member

@mhils mhils left a comment

Choose a reason for hiding this comment

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

This looks reasonable to me so far. I guess the question @alex or @reaperhulk need to answer is if they want to expose DTLS_set_timer_cb for tests or if the current mocking there is good enough. I feel like the current mocking approach is good enough, but YMMV. :)

assert seconds is not None

# Handle the timeout and check there is data to send.
time.sleep(seconds)
Copy link
Member

Choose a reason for hiding this comment

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

How long are we sleeping here roughly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jlaine
Copy link
Contributor Author

jlaine commented Feb 9, 2023

FYI my usecase for this change is to finish moving aiortc (an implementation of WebRTC in Python) on top of pyOpenSSL and stop relying on the bindings provided by cryptography, which is bound to break at some point. Handling DTLS timeouts is the only bit missing.

@reaperhulk
Copy link
Member

@mhils never feel like you can't merge things on your own recognizance here 😄 You're a maintainer just like we are!

Given that DTLS_set_timer_cb isn't in cryptography (and it doesn't appear we want to bind it) then the mock is sufficient for now. I'm all for work that avoids relying directly on cryptography's bindings. We do a good job of avoiding breaking pyOpenSSL, but make no such promises for anyone else!

# Testing this directly is prohibitively time consuming as the timeout
# duration is doubled on each retry, so the best we can do is to mock
# this condition.
with patch(
Copy link
Member

Choose a reason for hiding this comment

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

You can do this with pytest's monkeypatch by adding that arg to the test args and then doing something like:

monkeypatch.setattr(OpenSSL._util.lib, 'DTLSv1_handle_timeout', lambda x: -1)

This way you can avoid importing unittest.patch

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah thanks I should have spotted the monkeypatch uses in other tests. Fixed.

@jlaine jlaine force-pushed the dtls-timeout branch 2 times, most recently from cea3eed to 14b3f69 Compare February 13, 2023 08:06
mhils
mhils previously approved these changes Feb 13, 2023
Copy link
Member

@mhils mhils left a comment

Choose a reason for hiding this comment

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

Thanks! :)

This looks good % formatting stuff flagged by CI.

@jlaine
Copy link
Contributor Author

jlaine commented Feb 13, 2023

Thanks! :)

This looks good % formatting stuff flagged by CI.

The formatting errors seem to be due to the release of black 23. I've included an additional commit which fixes these, but now CI seems to fail for some unrelated reason.. ?

@mhils
Copy link
Member

mhils commented Feb 13, 2023

Thanks. No further action required from your end here, I'll take care of this after #1185 is in.

When performing a DTLS handshake, the DTLS state machine may need to be
updated based on the passage of time, for instance in response to packet
loss.

OpenSSL supports this by means of the `DTLSv1_get_timeout` and
`DTLSv1_handle_timeout` methods, both of which are included in
cryptography's bindings. This change adds Python wrappers for these
methods in the `Connection` class.
mhils
mhils previously approved these changes Feb 13, 2023
@mhils mhils merged commit 3517764 into pyca:main Feb 13, 2023
@jlaine
Copy link
Contributor Author

jlaine commented Feb 13, 2023

@mhils fantastic, thank you very much!

@jlaine jlaine deleted the dtls-timeout branch February 13, 2023 13:28
jlaine added a commit to jlaine/aiortc that referenced this pull request Apr 1, 2023
Support for DTLS timeouts was contributed upstream in PR
pyca/pyopenssl#1180 which was released in
version 23.1.0, so we can remove our local implementation.
jlaine added a commit to aiortc/aiortc that referenced this pull request Apr 1, 2023
Support for DTLS timeouts was contributed upstream in PR
pyca/pyopenssl#1180 which was released in
version 23.1.0, so we can remove our local implementation.
PAN-Chuwen pushed a commit to PAN-Chuwen/StreamPose that referenced this pull request Sep 15, 2023
Support for DTLS timeouts was contributed upstream in PR
pyca/pyopenssl#1180 which was released in
version 23.1.0, so we can remove our local implementation.
17852833820 pushed a commit to 17852833820/AioRTC that referenced this pull request Jan 20, 2024
Support for DTLS timeouts was contributed upstream in PR
pyca/pyopenssl#1180 which was released in
version 23.1.0, so we can remove our local implementation.
mametaro99 pushed a commit to mametaro99/image-recognition that referenced this pull request May 11, 2024
Support for DTLS timeouts was contributed upstream in PR
pyca/pyopenssl#1180 which was released in
version 23.1.0, so we can remove our local implementation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants