Thread: ecdh support causes unnecessary roundtrips

ecdh support causes unnecessary roundtrips

From
Andres Freund
Date:
Hi,

When connecting with a libpq based client, the TLS establishment ends up like
this in many configurations;

C->S: TLSv1 393 Client Hello
S->C: TLSv1.3 167 Hello Retry Request, Change Cipher Spec
C->S: TLSv1.3 432 Change Cipher Spec, Client Hello
S->C: TLSv1.3 1407 Server Hello, Application Data, Application Data, Application Data, Application Data
...

I.e. there are two clients hellos, because the server rejects the clients
"parameters".


This appears to be caused by ECDH support.  The difference between the two
ClientHellos is
-            Extension: key_share (len=38) x25519
+            Extension: key_share (len=71) secp256r1

I.e. the clients wanted to use x25519, but the server insists on secp256r1.


This turns out to be due to

commit 3164721462d547fa2d15e2a2f07eb086a3590fd5
Author: Peter Eisentraut <peter_e@gmx.net>
Date:   2013-12-07 15:11:44 -0500

    SSL: Support ECDH key exchange



I don't know if it's good that we're calling SSL_CTX_set_tmp_ecdh at all, but
if it is, shouldn't we at least do the same in libpq, so we don't introduce
unnecessary roundtrips?

I did confirm that doing the same thing on the client side removes the
additional roundtrip.



It seems kind of a shame that we have fewer roundtrips due to
sslnegotiation=direct, but do completely unnecessary roundtrips all the
time...


In a network with ~10ms latency I see an almost 30% increased
connections-per-second for a single client if I avoid the added roundtrip.

I think this could almost be considered a small bug...

Greetings,

Andres Freund



Re: ecdh support causes unnecessary roundtrips

From
Daniel Gustafsson
Date:
> On 17 Jun 2024, at 01:46, Andres Freund <andres@anarazel.de> wrote:

> When connecting with a libpq based client, the TLS establishment ends up like
> this in many configurations;
>
> C->S: TLSv1 393 Client Hello
> S->C: TLSv1.3 167 Hello Retry Request, Change Cipher Spec
> C->S: TLSv1.3 432 Change Cipher Spec, Client Hello
> S->C: TLSv1.3 1407 Server Hello, Application Data, Application Data, Application Data, Application Data

I wonder if this is the result of us still using TLS 1.2 as the default minimum
protocol version.  In 1.2 the ClientHello doesn't contain the extensions for
cipher and curves, so the server must reply with a HRR (Hello Retry Request)
message asking the client to set protocol, curve and cipher.  The client will
respond with a new Client Hello using 1.3 with the extensions.

Using 1.3 as the default minimum has been on my personal TODO for a while,
maybe that should be revisited for 18.

> I.e. there are two clients hellos, because the server rejects the clients
> "parameters".

Or the client didn't send any.

> This appears to be caused by ECDH support.  The difference between the two
> ClientHellos is
> -            Extension: key_share (len=38) x25519
> +            Extension: key_share (len=71) secp256r1
>
> I.e. the clients wanted to use x25519, but the server insists on secp256r1.

Somewhat related, Erica Zhang has an open patch to make the server-side curves
configuration take a list rather than a single curve [0], and modernizing the
API used as a side effect (SSL_CTX_set_tmp_ecdh is documented as obsoleted by
OpenSSL, but not deprecated with an API level).

> I don't know if it's good that we're calling SSL_CTX_set_tmp_ecdh at all,

To set the specified curve in ssl_ecdh_curve we have to don't we?

> but if it is, shouldn't we at least do the same in libpq, so we don't introduce
> unnecessary roundtrips?

If we don't set the curve in the client I believe OpenSSL will pass the set of
supported curves the client has, which then should allow the server to pick the
one it wants based on ssl_ecdh_curve, so ideally we shouldn't have to I think.

> I did confirm that doing the same thing on the client side removes the
> additional roundtrip.

The roundtrip went away because the client was set to use secp256r1?  I wonder
if that made OpenSSL override the min protocol version and switch to a TLS1.3
ClientHello since it otherwise couldn't announce the curve.  If you force the
client min protocol to 1.3 in an unpatched client, do you see the same speedup?

--
Daniel Gustafsson

[0] tencent_1B368B07F4B5886F9822981189DA736CF209@qq.com


Re: ecdh support causes unnecessary roundtrips

From
Andres Freund
Date:
Hi,

On 2024-06-17 12:00:30 +0200, Daniel Gustafsson wrote:
> > On 17 Jun 2024, at 01:46, Andres Freund <andres@anarazel.de> wrote:
>
> > When connecting with a libpq based client, the TLS establishment ends up like
> > this in many configurations;
> >
> > C->S: TLSv1 393 Client Hello
> > S->C: TLSv1.3 167 Hello Retry Request, Change Cipher Spec
> > C->S: TLSv1.3 432 Change Cipher Spec, Client Hello
> > S->C: TLSv1.3 1407 Server Hello, Application Data, Application Data, Application Data, Application Data
>
> I wonder if this is the result of us still using TLS 1.2 as the default minimum
> protocol version.  In 1.2 the ClientHello doesn't contain the extensions for
> cipher and curves, so the server must reply with a HRR (Hello Retry Request)
> message asking the client to set protocol, curve and cipher.  The client will
> respond with a new Client Hello using 1.3 with the extensions.

I'm pretty sure it's not that, given that
a) removing the server side SSL_CTX_set_tmp_ecdh() call
b) adding a client side SSL_CTX_set_tmp_ecdh() call
avoid the roundtrip.

I've now also confirmed that ssl_min_protocol_version=TLSv1.3 doesn't change
anything (relevant, of course the indicated version support changes).


> > This appears to be caused by ECDH support.  The difference between the two
> > ClientHellos is
> > -            Extension: key_share (len=38) x25519
> > +            Extension: key_share (len=71) secp256r1
> >
> > I.e. the clients wanted to use x25519, but the server insists on secp256r1.
>
> Somewhat related, Erica Zhang has an open patch to make the server-side curves
> configuration take a list rather than a single curve [0], and modernizing the
> API used as a side effect (SSL_CTX_set_tmp_ecdh is documented as obsoleted by
> OpenSSL, but not deprecated with an API level).

That does seem nicer. Fun coincidence in timing.


> > I don't know if it's good that we're calling SSL_CTX_set_tmp_ecdh at all,
>
> To set the specified curve in ssl_ecdh_curve we have to don't we?

Sure, but it's not obvious to me why we actually want to override openssl's
defaults here. There's not even a parameter to opt out of forcing a specific
choice on the server side.


> > but if it is, shouldn't we at least do the same in libpq, so we don't introduce
> > unnecessary roundtrips?
>
> If we don't set the curve in the client I believe OpenSSL will pass the set of
> supported curves the client has, which then should allow the server to pick the
> one it wants based on ssl_ecdh_curve, so ideally we shouldn't have to I think.

Afaict the client sends exactly one:

Transport Layer Security
    TLSv1.3 Record Layer: Handshake Protocol: Client Hello
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 320
        Handshake Protocol: Client Hello
...
            Extension: supported_versions (len=5) TLS 1.3, TLS 1.2
                Type: supported_versions (43)
                Length: 5
                Supported Versions length: 4
                Supported Version: TLS 1.3 (0x0304)
                Supported Version: TLS 1.2 (0x0303)
            Extension: psk_key_exchange_modes (len=2)
                Type: psk_key_exchange_modes (45)
                Length: 2
                PSK Key Exchange Modes Length: 1
                PSK Key Exchange Mode: PSK with (EC)DHE key establishment (psk_dhe_ke) (1)
            Extension: key_share (len=38) x25519
                Type: key_share (51)
                Length: 38
                Key Share extension
            Extension: compress_certificate (len=5)
                Type: compress_certificate (27)
...

Note key_share being set to x25519.


The HRR says:

Extension: key_share (len=2) secp256r1
    Type: key_share (51)
    Length: 2
    Key Share extension
        Selected Group: secp256r1 (23)


> > I did confirm that doing the same thing on the client side removes the
> > additional roundtrip.
>
> The roundtrip went away because the client was set to use secp256r1?

Yes. Or if I change the server to not set the ecdh curve.


> I wonder if that made OpenSSL override the min protocol version and switch
> to a TLS1.3 ClientHello since it otherwise couldn't announce the curve.

The client seems to announce the curve in the initial ClientHello even with
1.3 as the minimum version.

What *does* make the HRR go away is setting ssl_max_protocol_version=TLSv1.2
on the client side.


https://wiki.openssl.org/index.php/TLS1.3 says:

> In practice most clients will use X25519 or P-256 for their initial
> key_share. For maximum performance it is recommended that servers are
> configured to support at least those two groups and clients use one of those
> two for its initial key_share. This is the default case (OpenSSL clients
> will use X25519).

We're not allowing both groups and the client defaults to X25519, hence
the HRR.


> If you force the client min protocol to 1.3 in an unpatched client, do you
> see the same speedup?

Nope.

Greetings,

Andres Freund



Re: ecdh support causes unnecessary roundtrips

From
Jacob Champion
Date:
On Mon, Jun 17, 2024 at 10:01 AM Andres Freund <andres@anarazel.de> wrote:
> On 2024-06-17 12:00:30 +0200, Daniel Gustafsson wrote:
> > To set the specified curve in ssl_ecdh_curve we have to don't we?
>
> Sure, but it's not obvious to me why we actually want to override openssl's
> defaults here. There's not even a parameter to opt out of forcing a specific
> choice on the server side.

I had exactly the same question in the context of the other thread, and found

    https://www.openssl.org/blog/blog/2022/10/21/tls-groups-configuration/index.html

My initial takeaway was that our default is more restrictive than it
should be, but the OpenSSL default is more permissive than what they
recommend in practice, due to denial of service concerns:

> A general recommendation is to limit the groups to those that meet the
> required security level and that all the potential TLS clients support.

--Jacob



Re: ecdh support causes unnecessary roundtrips

From
Daniel Gustafsson
Date:
> On 17 Jun 2024, at 19:01, Andres Freund <andres@anarazel.de> wrote:
> On 2024-06-17 12:00:30 +0200, Daniel Gustafsson wrote:
>>> On 17 Jun 2024, at 01:46, Andres Freund <andres@anarazel.de> wrote:

>>> I don't know if it's good that we're calling SSL_CTX_set_tmp_ecdh at all,
>>
>> To set the specified curve in ssl_ecdh_curve we have to don't we?
>
> Sure, but it's not obvious to me why we actually want to override openssl's
> defaults here. There's not even a parameter to opt out of forcing a specific
> choice on the server side.

I agree that the GUC is a bit rough around the edges, maybe leavint it blank or
something should be defined as "OpenSSL defaults".  Let's bring that to Erica's
patch for allowing a list of curves.

>>> I did confirm that doing the same thing on the client side removes the
>>> additional roundtrip.
>>
>> The roundtrip went away because the client was set to use secp256r1?
>
> Yes. Or if I change the server to not set the ecdh curve.

Configuring the server to use x25519 instead of secp256r1 should achieve the
same thing.

>> I wonder if that made OpenSSL override the min protocol version and switch
>> to a TLS1.3 ClientHello since it otherwise couldn't announce the curve.
>
> The client seems to announce the curve in the initial ClientHello even with
> 1.3 as the minimum version.

With 1.3 it should announce it in ClientHello, do you mean that it's announced
when 1.2 is the minimum version as well?  It does make sense since a 1.2 server
is defined to disregard all extensions.

> What *does* make the HRR go away is setting ssl_max_protocol_version=TLSv1.2
> on the client side.

Makes sense, that would remove the curve and there is no change required.

> https://wiki.openssl.org/index.php/TLS1.3 says:
>
>> In practice most clients will use X25519 or P-256 for their initial
>> key_share. For maximum performance it is recommended that servers are
>> configured to support at least those two groups and clients use one of those
>> two for its initial key_share. This is the default case (OpenSSL clients
>> will use X25519).
>
> We're not allowing both groups and the client defaults to X25519, hence
> the HRR.

So this would be solved by the curve-list patch referenced above, especially if
allow it to have an opt-out to use OpenSSL defaults.

--
Daniel Gustafsson




Re: ecdh support causes unnecessary roundtrips

From
Andres Freund
Date:
Hi,

On 2024-06-17 19:29:47 +0200, Daniel Gustafsson wrote:
> >> I wonder if that made OpenSSL override the min protocol version and switch
> >> to a TLS1.3 ClientHello since it otherwise couldn't announce the curve.
> >
> > The client seems to announce the curve in the initial ClientHello even with
> > 1.3 as the minimum version.
>
> With 1.3 it should announce it in ClientHello, do you mean that it's announced
> when 1.2 is the minimum version as well?  It does make sense since a 1.2 server
> is defined to disregard all extensions.

Yes, it's announced even when 1.2 is the minimum:

            Extension: supported_versions (len=5) TLS 1.3, TLS 1.2
                Type: supported_versions (43)
                Length: 5
                Supported Versions length: 4
                Supported Version: TLS 1.3 (0x0304)
                Supported Version: TLS 1.2 (0x0303)
...
            Extension: key_share (len=38) x25519
                Type: key_share (51)
                Length: 38
                Key Share extension



> Let's bring that to Erica's patch for allowing a list of curves.

I'm kinda wondering if we ought to do something about this in the
backbranches. Forcing unnecessary roundtrips onto everyone for the next five
years due to an oversight on our part isn't great.  Once you're not local, the
roundtrip does measurably increase the "time to first query".

Greetings,

Andres Freund



Re: ecdh support causes unnecessary roundtrips

From
Daniel Gustafsson
Date:
> On 17 Jun 2024, at 19:44, Andres Freund <andres@anarazel.de> wrote:

>> Let's bring that to Erica's patch for allowing a list of curves.
>
> I'm kinda wondering if we ought to do something about this in the
> backbranches. Forcing unnecessary roundtrips onto everyone for the next five
> years due to an oversight on our part isn't great.  Once you're not local, the
> roundtrip does measurably increase the "time to first query".

I don't disagree, but wouldn't it be the type of behavioural change which we
typically try to avoid in backbranches?  Changing the default of the ecdh GUC
would perhaps be doable?  (assuming that's a working solution to avoid the
roundtrip).  Amending the documentation is the one thing we certainly can do
but 99.9% of affected users won't know they are affected so won't look for that
section.

--
Daniel Gustafsson




Re: ecdh support causes unnecessary roundtrips

From
Andres Freund
Date:
Hi,

On 2024-06-17 19:51:45 +0200, Daniel Gustafsson wrote:
> > On 17 Jun 2024, at 19:44, Andres Freund <andres@anarazel.de> wrote:
> 
> >> Let's bring that to Erica's patch for allowing a list of curves.
> > 
> > I'm kinda wondering if we ought to do something about this in the
> > backbranches. Forcing unnecessary roundtrips onto everyone for the next five
> > years due to an oversight on our part isn't great.  Once you're not local, the
> > roundtrip does measurably increase the "time to first query".
> 
> I don't disagree, but wouldn't it be the type of behavioural change which we
> typically try to avoid in backbranches?

Yea, it's not great. Not sure what the right thing is here.


> Changing the default of the ecdh GUC would perhaps be doable?

I was wondering whether we could change the default so that it accepts both
x25519 and secp256r1. Unfortunately that seems to requires changing what we
use to set the parameter...


> (assuming that's a working solution to avoid the roundtrip).

It is.


> Amending the documentation is the one thing we certainly can do but 99.9% of
> affected users won't know they are affected so won't look for that section.

Yea. It's also possible that some other bindings changed their default to
match ours...

Greetings,

Andres Freund



Re: ecdh support causes unnecessary roundtrips

From
Daniel Gustafsson
Date:
> On 17 Jun 2024, at 19:56, Andres Freund <andres@anarazel.de> wrote:
> On 2024-06-17 19:51:45 +0200, Daniel Gustafsson wrote:

>> Changing the default of the ecdh GUC would perhaps be doable?
>
> I was wondering whether we could change the default so that it accepts both
> x25519 and secp256r1. Unfortunately that seems to requires changing what we
> use to set the parameter...

Right.  The patch in https://commitfest.postgresql.org/48/5025/ does allow for
accepting both but that's a different discussion.

Changing, and backpatching, the default to at least keep new installations from
extra roundtrips doesn't seem that far off in terms of scope from what
860fe27ee1e2 backpatched.  Maybe it can be an option.

>> Amending the documentation is the one thing we certainly can do but 99.9% of
>> affected users won't know they are affected so won't look for that section.
>
> Yea. It's also possible that some other bindings changed their default to
> match ours...

There is that possibility, though I think we would've heard something about
that by now if that had happened.

--
Daniel Gustafsson