-
-
Notifications
You must be signed in to change notification settings - Fork 742
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 OpenSSL TLS 1.3 PSK callbacks #1993
base: master
Are you sure you want to change the base?
Add OpenSSL TLS 1.3 PSK callbacks #1993
Conversation
openssl/src/ssl/callbacks.rs
Outdated
+ Send, | ||
{ | ||
unsafe { | ||
let psk_idx = Ssl::cached_ex_index::<Vec<u8>>(); |
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'd prefer to use a newtype for this index to avoid potential collisions with other APIs that need to store a Vec<u8>
.
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.
From the documentation it sounds like the callback can be invoked twice for a single session. Does the PSK ID from the first call need to be preserved after the second call?
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'd prefer to use a newtype for this index to avoid potential collisions with other APIs that need to store a
Vec<u8>
.
OK
From the documentation it sounds like the callback can be invoked twice for a single session. Does the PSK ID from the first call need to be preserved after the second call?
I think, only until the handshake completes. My understanding is that I am keeping it for the lifetime of the SSL
struct which is not the best. If the SSL_CTX_set_info_callback
function were wrapped rust-openssl
could see the SSL_CB_HANDSHAKE_DONE
event and free the PSK ID, but that would need to be transparent to the end-user using the callback which seems like unwanted complexity
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.
From the documentation it sounds like the callback can be invoked twice for a single session. Does the PSK ID from the first call need to be preserved after the second call?
OpenSSL says:
The PSK returned by the callback is allowed to be different between the first and second time it is called.
So I think the flow is:
- First callback invocation,
md
isnull
. Returns PSK "P1", using cipher "C" - OpenSSL does stuff, finds cipher "C" is not acceptable, but "D" is acceptable
- Second callback invocation,
md
forces cipher "D". Returns PSK "P2" using cipher "D" - Handshake completes
Which I think means the "P1" PSK ID can be dropped. I haven't dug through any OpenSSL code yet to confirm.
I did add a newtype for the PSK storage in 6a772d1
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 found the callback is called in OpenSSL 1.1.1 when constructing the early data
It looks like the callback can be called twice if there is a "hello retry request".
A little further down I see the PSK id is copied into SSL *
with OPENSSL_memdup()
and is no longer mentioned in the function. The old PSK id is freed.
I suspect that the documentation is written as it is to prevent an FFI wrapper from freeing the memory before the OpenSSL side of the callback can complete.
Elsewhere OpenSSL notes in a comment than only one PSK can be sent, so together I think it is safe to replace the PSK id from the first invocation with the PSK id from the second invocation.
pub fn find<'a>(ssl: &'a SslRef, cipher_id: &[u8]) -> Result<&'a SslCipherRef, ErrorStack> { | ||
unsafe { | ||
let ssl = ssl.as_ptr(); | ||
let ptr = ffi::SSL_CIPHER_find(ssl, cipher_id.as_ptr()); |
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.
Something needs to ensure that cipher_id is (at least) two bytes.
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.
My understanding of the documentation was that OpenSSL would set an error if the cipher ID was invalid for any reason, I'll run some tests to check that assumption.
Can you point me toward an example of the type of error I should return if the cipher ID has an invalid format?
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.
Because SSL_CIPHER_find()
doesn't seem to set internal OpenSSL errors I switched the return type from a Result<>
to Option<>
. I also added a length check to prevent returning a cipher for a too-short or too-long ID.
openssl/src/ssl/mod.rs
Outdated
/// This could be used to set up a session-based PSK. You must ensure the length of the key is | ||
/// suitable for the ciphersuite associated with the session. | ||
#[corresponds(SSL_SESSION_set1_master_key)] | ||
pub fn set_master_key(&mut self, key: &[u8]) -> Result<(), ErrorStack> { |
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.
Sessions are reference counted so it's not sound to directly mutate them. We'll need to define a session builder like we have for SslContext instead.
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.
OK
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.
In 4565f0f I added SslContextBuilder
modeled on the other builders.
Previously input length was unchecked. OpenSSL still returned NULL on a too-short inuput, but returned a value on a too-long input. OpenSSL doesn't add an error internally if a SSL_CIPHER_find() fails so an Option<> seems better. SslCipherRef::find() now checks to ensure the length of the cipher ID is exactly 2 to avoid too-long cipher IDs matching.
4b6b7bc
to
eef1928
Compare
eef1928
to
5529c79
Compare
Can you give a clue on how to exclude extern "C" {
#[cfg(any(not(ossl102), libressl273, not(boringssl)))]
pub fn SSL_CIPHER_find(ssl: *mut SSL, ptr: *const c_uchar) -> *const SSL_CIPHER; |
Can you give me an idea of how to exclude |
For TLS 1.3 OpenSSL introduced new functions
SSL_CTX_set_psk_use_session_callback
andSSL_CTX_set_psk_find_session_callback
to configure a pre-shared key. I chose to wrap both theSSL_CTX_psk…
andSSL_psk…
versions of these functions.One thing I am not certain of is
SSL_CTX_set_psk_use_session_callback
says:I used
ssl.set_ex_data()
to store the ID in theSslRef
, but I don't know if this is the correct way to do that.The notes for the new functions state:
But this does not to be true with a minimally-configured session. Wireshark shows that the PSK is accepted and that only 1 round-trip is required to create a connection.
Output for
ssl::test::psk_tls13_callbacks
(4 total):Output for
ssl::test::ssl_verify_callback
(6 total, Application Data in frames 27 and 29 are the TLS 1.3 handshake):The above output can be reproduced by running Wireshark on the loopback interface then applying the
tls
filter.Per the description of the above callbacks this also requires wrapping:
SSL_SESSION_new
SSL_SESSION_set1_master_key
SSL_SESSION_set_cipher
SSL_SESSION_set_protocol_version
SSL_CIPHER_find