Skip to content

Commit

Permalink
Allow TLS PSK without server certificate (#2083)
Browse files Browse the repository at this point in the history
While we currently support TLS [pre-shared keys (PSKs)][1] for TLS 1.2,
our
python integration tests have shown that we don't currently support TLS
1.2
PSKs on the server side unless a certificate has been configured. PSKs
can be
used for both secret establishment and authentication, so there likely
exist
legitimate use-cases for establishing PSK connections without
certificates.
CPython's integration tests [expect this][2] behavior.

This gap appears to be incidental, not intentional. Unrelated [OCSP
work][3]
introduced a requirement that a valid certificate public key is loaded
on the
server before we sort out handshake parameters. If that load fails, the
hanshake is aborted before PSK can be negotiated for secret
establishment. To
fix this, we simply check whether the server has a PSK callback enabled,
and
allow the certificate loading to fail if so. We also handle the
potential
nullity of the public key.

This PR only applies to TLSv1.2 PSK. TLSv1.3 moved PSK negotiation
earlier in
the handshake to client/server hello's, so does utilize this code path.
Currently, we only support TLSv1.3 PSK for session resumption, _not
"pure" PSK
used for initial shared secret establishment. I have [a branch][4] where
I'm
implementing this, but it's non-trivial.

We won't be able to delete the TLSv1.3 PSK python test patch until we
implement
"pure" TLSv1.3 PSK.

[1]: https://datatracker.ietf.org/doc/html/rfc4279
[2]:
https://github.com/python/cpython/blob/aeb9b65aa26444529e4adc7d6e5b0d3dd9889ec2/Lib/test/test_ssl.py#L4395
[3]:
https://github.com/aws/aws-lc/pull/1120/files#diff-2743f6406343b4e3d671a71af35cf44a36ad77cef1b6cfbc02f25dc732915ee9R790
[4]: https://github.com/WillChilds-Klein/aws-lc/tree/tls13-pure-psk
  • Loading branch information
WillChilds-Klein authored Jan 14, 2025
1 parent 6393e80 commit a4fec03
Show file tree
Hide file tree
Showing 3 changed files with 8 additions and 22 deletions.
12 changes: 7 additions & 5 deletions ssl/handshake_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -793,11 +793,13 @@ static enum ssl_hs_wait_t do_select_certificate(SSL_HANDSHAKE *hs) {
}
}

// Load |hs->local_pubkey| from the cert prematurely. The certificate could be
// subject to change once we negotiate signature algorithms later. If it
// changes to another leaf certificate the server and client has support for,
// we reload it.
if (!ssl_handshake_load_local_pubkey(hs)) {
// Load |hs->local_pubkey| from the cert (if present) prematurely. The
// certificate could be subject to change once we negotiate signature
// algorithms later. If it changes to another leaf certificate the server and
// client has support for, we reload it. The public key may only be absent if
// PSK is enabled on the server, as indicated by presense of a callback.
if (!ssl_handshake_load_local_pubkey(hs) &&
!(hs->local_pubkey == nullptr && hs->config->psk_server_callback)) {
return ssl_hs_error;
}

Expand Down
1 change: 1 addition & 0 deletions ssl/ssl_privkey.cc
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ static bool ssl_public_key_rsa_pss_check(EVP_PKEY *pubkey, uint16_t sigalg) {

static bool tls12_pkey_supports_cipher_auth(SSL_HANDSHAKE *hs,
const EVP_PKEY *key) {
GUARD_PTR(key);
SSL *const ssl = hs->ssl;
// We may have a private key that supports the signature algorithm, but we
// need to verify that the negotiated cipher allows it. This behavior is only
Expand Down
17 changes: 0 additions & 17 deletions tests/ci/integration/python_patch/main/aws-lc-cpython.patch
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,6 @@ index 0e50d09..f4b7b3c 100644
def test_dh_params(self):
# Check we can get a connection with ephemeral Diffie-Hellman
client_context, server_context, hostname = testing_context()
@@ -4364,14 +4366,14 @@ def test_session_handling(self):
def test_psk(self):
psk = bytes.fromhex('deadbeef')

- client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
+ client_context, server_context, _ = testing_context()
+
client_context.check_hostname = False
client_context.verify_mode = ssl.CERT_NONE
client_context.maximum_version = ssl.TLSVersion.TLSv1_2
client_context.set_ciphers('PSK')
client_context.set_psk_client_callback(lambda hint: (None, psk))

- server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
server_context.maximum_version = ssl.TLSVersion.TLSv1_2
server_context.set_ciphers('PSK')
server_context.set_psk_server_callback(lambda identity: psk)
@@ -4443,14 +4445,14 @@ def server_callback(identity):
self.assertEqual(identity, client_identity)
return psk
Expand Down

0 comments on commit a4fec03

Please sign in to comment.