#963 ssl handshake error: no shared cipher for prime256v1 ECDSA certificate
Reporter
Kevin Locke
Owner
Zash
Created
Updated
Stars
★★ (2)
Tags
Priority-Medium
Status-Fixed
Milestone-0.10
Type-Defect
Kevin Locke
on
What steps will reproduce the problem?
# Generate an ECDSA certificate which uses prime256v1
openssl ecparam -name prime256v1 -genkey -out "certs/localhost-ecc.key"
openssl req -batch -config "certs/localhost.cnf" -key "certs/localhost-ecc.key" -nodes -x509 -out "certs/localhost-ecc.crt"
# Run prosody with the certificate
cat > prosody.cfg.lua <<PROSODY_CFG
modules_enabled = { "tls"; }
ssl = {
key = "certs/localhost-ecc.key";
certificate = "certs/localhost-ecc.crt";
}
VirtualHost "localhost"
PROSODY_CFG
./prosody
# Demonstrate connection failure
openssl s_client -starttls xmpp -connect localhost:5222 </dev/null
What is the expected output?
A successful TLS connection is expected.
The server would print "Client connected".
The client would print the certificate contents then "DONE".
What do you see instead?
The TLS connection fails.
The server will print "Client disconnected: ssl handshake error: no shared cipher".
The client will print "140448209548544:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:../ssl/record/rec_layer_s3.c:1399:SSL alert number 40".
What version of the product are you using? On what operating system?
I have reproduced the issue using Prosody 0.9.12-2 on Debian 9 (stretch) and building from the current hg tip (8185:e89320b8a789) with LuaSec 0.6-3 and OpenSSL 1.1.0f-3.
Please provide any additional information below.
The issue appears to be caused by setting curve secp384r1 by default (at core/certmanager.lua:105) which causes an empty cipher list due to how ECDHE and ECDSA interact in OpenSSL 1.1.0. See https://github.com/openssl/openssl/issues/2033#issuecomment-265165765
Configuring curve prime256v1 explicitly works, but would fail with a secp384r1 ECDSA certificate. Removing the default curve in the code fixes the problem in a way that should work for all ECDSA certificates (and the s_client connection uses X25519 on my system). Is there a reason to set curve secp384r1 by default?
Thanks,
Kevin
Related:
https://prosody.im/issues/issue/879https://prosody.im/issues/issue/943https://prosody.im/issues/issue/951
Zash
on
Hi, thanks for the report. So, even more fallout from this :(
The reason we set a curve by default is because that was how you enabled ECDHE, but the meaning of that option has changed in LuaSec and/or OpenSSL in an incompatible way, without any way to detect the change.
Kevin Locke
on
Hi Zash,
You're right! After a bit of digging, it looks like OpenSSL < 1.0.2 requires calling SSL_CTX_set_tmp_ecdh to enable ECDH. 1.0.2 <= OpenSSL < 1.1.0 can use SSL_CTX_set_ecdh_auto to enable ECDH without limiting the curves.[1] OpenSSL >= 1.1.0 removed SSL_CTX_set_ecdh_auto and enables ECDH by default.[2]
LuaSec 0.6 setcurve calls SSL_CTX_set_tmp_ecdh. LuaSec master setcurve calls SSL_CTX_set1_curves_list and SSL_CTX_set_ecdh_auto where present.[3]
Perhaps the best solution for Prosody would be to not set a default curve on OpenSSL 1.1.0 and later. Unfortunately, it doesn't look like LuaSec exposes the OpenSSL version information. I could submit an issue to LuaSec about that. Thoughts?
1. https://bugs.python.org/issue29697
2. https://github.com/openssl/openssl/commit/fe6ef2472db933f01b59cad82aa925736935984b
3. https://github.com/brunoos/luasec/commit/231563682a721a39b047db5f415363a88bf3c872
What steps will reproduce the problem? # Generate an ECDSA certificate which uses prime256v1 openssl ecparam -name prime256v1 -genkey -out "certs/localhost-ecc.key" openssl req -batch -config "certs/localhost.cnf" -key "certs/localhost-ecc.key" -nodes -x509 -out "certs/localhost-ecc.crt" # Run prosody with the certificate cat > prosody.cfg.lua <<PROSODY_CFG modules_enabled = { "tls"; } ssl = { key = "certs/localhost-ecc.key"; certificate = "certs/localhost-ecc.crt"; } VirtualHost "localhost" PROSODY_CFG ./prosody # Demonstrate connection failure openssl s_client -starttls xmpp -connect localhost:5222 </dev/null What is the expected output? A successful TLS connection is expected. The server would print "Client connected". The client would print the certificate contents then "DONE". What do you see instead? The TLS connection fails. The server will print "Client disconnected: ssl handshake error: no shared cipher". The client will print "140448209548544:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:../ssl/record/rec_layer_s3.c:1399:SSL alert number 40". What version of the product are you using? On what operating system? I have reproduced the issue using Prosody 0.9.12-2 on Debian 9 (stretch) and building from the current hg tip (8185:e89320b8a789) with LuaSec 0.6-3 and OpenSSL 1.1.0f-3. Please provide any additional information below. The issue appears to be caused by setting curve secp384r1 by default (at core/certmanager.lua:105) which causes an empty cipher list due to how ECDHE and ECDSA interact in OpenSSL 1.1.0. See https://github.com/openssl/openssl/issues/2033#issuecomment-265165765 Configuring curve prime256v1 explicitly works, but would fail with a secp384r1 ECDSA certificate. Removing the default curve in the code fixes the problem in a way that should work for all ECDSA certificates (and the s_client connection uses X25519 on my system). Is there a reason to set curve secp384r1 by default? Thanks, Kevin Related: https://prosody.im/issues/issue/879 https://prosody.im/issues/issue/943 https://prosody.im/issues/issue/951
Hi, thanks for the report. So, even more fallout from this :( The reason we set a curve by default is because that was how you enabled ECDHE, but the meaning of that option has changed in LuaSec and/or OpenSSL in an incompatible way, without any way to detect the change.
Hi Zash, You're right! After a bit of digging, it looks like OpenSSL < 1.0.2 requires calling SSL_CTX_set_tmp_ecdh to enable ECDH. 1.0.2 <= OpenSSL < 1.1.0 can use SSL_CTX_set_ecdh_auto to enable ECDH without limiting the curves.[1] OpenSSL >= 1.1.0 removed SSL_CTX_set_ecdh_auto and enables ECDH by default.[2] LuaSec 0.6 setcurve calls SSL_CTX_set_tmp_ecdh. LuaSec master setcurve calls SSL_CTX_set1_curves_list and SSL_CTX_set_ecdh_auto where present.[3] Perhaps the best solution for Prosody would be to not set a default curve on OpenSSL 1.1.0 and later. Unfortunately, it doesn't look like LuaSec exposes the OpenSSL version information. I could submit an issue to LuaSec about that. Thoughts? 1. https://bugs.python.org/issue29697 2. https://github.com/openssl/openssl/commit/fe6ef2472db933f01b59cad82aa925736935984b 3. https://github.com/brunoos/luasec/commit/231563682a721a39b047db5f415363a88bf3c872
A bunch of existing discussion can be found in https://github.com/brunoos/luasec/pull/47 FWIW
This was fixed in https://hg.prosody.im/trunk/rev/92cddfe65003 according to bisect
Changes