OpenSSL 4.1.0 rejects no-ecdsa before the compiler starts

John Burns

OpenSSL 4.1.0 rejects no-ecdsa before the compiler starts

A build line that still says ./Configure linux-x86_64 no-ecdsa does not reach gcc on OpenSSL 4.1.0. Configure exits 255, prints Unsupported options: no-ecdsa, and says the build file was not produced. The same line on the 4.0.3 tree exits 0 and writes a Makefile. That success is not evidence that ECDSA left the library. The generated header defines OPENSSL_NO_ECDSA, and the Makefile still compiles crypto/ec/ecdsa_ossl.c.

OpenSSL 4.1.0, released 23 October 2026, dropped no-ecdsa and no-ecdh from Configure. The release notes say those options did not really disable the implementations, and that no-ec is the option that disables elliptic-curve support. The notes do not show the exit status, the exact error text, or the Makefile rule that remains after a 4.0.3 configure. Those are the checks a build job has to make before it treats a green Configure as a disabled algorithm.

The trees that were configured

The comparison used the official release tarballs on Debian 13, Linux amd64, with gcc 14.2.0 and Perl 5.40.1. Each SHA-256 matched the digest published on the corresponding GitHub release asset before the archive was unpacked:

4.1.0  50c17baf54cb008f6ce42d50deb4b28050a1aad711636cabd4b241c7e90cc2fd
4.0.3  325b5c806167c13b40b1ffeadfe0248197c00eccc4cf123ec1e28d2d2fd216d9

Configure was invoked as perl ./Configure with the target linux-x86_64 written out. That keeps the failure on the option. It does not mix in whatever ./config would guess from the host. The system OpenSSL, used only as a comparison client, was 3.5.7 from the distribution package. Nothing from these trees was installed over that package. make install was not run.

This run did not send DTLS packets, did not exercise the new listener API, and did not try the tsget client. The DTLS 1.3 flag was checked only as far as option parsing and the missing-certificate error. The four DTLS issues named in the 4.1.0 notes were not reproduced.

The option dies before a compiler starts

On the 4.1.0 tree, this command exits 255:

perl ./Configure linux-x86_64 no-ecdsa

The whole message is six lines. There is no compiler command in it.

Failure!  build file wasn't produced.
Please read INSTALL.md and associated NOTES-* files.  You may also have to
look over your available compiler tool chain or change your configuration.

***** Unsupported options: no-ecdsa

no-ecdh fails the same way, with the same exit status and the option name changed:

***** Unsupported options: no-ecdh

No Makefile is left behind. A job that continues into make after a non-zero Configure will fail for lack of a build file, not because a later compile rejected an ECDSA source file. The useful signal is the unsupported-option line. A log scraper that only watches gcc or the linker will never see it.

The wording also matters. Configure does not say the option is deprecated, ignored, or mapped to no-ec. It refuses the name. A script that appends no-ecdsa “just in case” cannot keep that token and still configure 4.1.0.

What 4.0.3 wrote when the flag succeeded

The same no-ecdsa command on the 4.0.3 tree exits 0 and prints the usual successful-configure banner. configdata.pm records the argument. Its %disabled table contains "ecdsa" => "option". The generated include/openssl/configuration.h contains this block:

# ifndef OPENSSL_NO_ECDSA
#  define OPENSSL_NO_ECDSA
# endif

That is as far as the flag goes. crypto/ec/ecdsa_ossl.c does not mention OPENSSL_NO_ECDSA. The generated Makefile still has a compile rule for crypto/ec/libcrypto-lib-ecdsa_ossl.o from crypto/ec/ecdsa_ossl.c, and the archive step still feeds that object to libcrypto.a. The compiler command in that rule does not pass -DOPENSSL_NO_ECDSA. The macro exists in a header the implementation file does not consult, and the object remains on the link line.

no-ecdh on 4.0.3 is the same shape. Configure exits 0. configuration.h defines OPENSSL_NO_ECDH. crypto/ec/ecdh_ossl.c does not mention that macro. The Makefile still compiles ecdh_ossl.c and still lists libcrypto-lib-ecdh_ossl.o in the libcrypto.a archive step. A review that stops at the Configure banner, or at the presence of OPENSSL_NO_ECDH in the header, will report the algorithm as disabled while the build file still compiles it.

That is the gap the 4.1.0 removal is aimed at. The release notes say the old options did not really disable the implementations. The 4.0.3 build file shows the concrete form of that statement: the feature is recorded as disabled, and the implementation object is still built. A later 4.0 patch could change the Makefile. The check is the object name in the build file you just generated, not a memory of this run.

no-ec is the option that changes the build file

On 4.1.0, this command exits 0:

perl ./Configure linux-x86_64 no-ec

The generated header defines OPENSSL_NO_EC, and with it OPENSSL_NO_EC2M and OPENSSL_NO_ECX. It does not define OPENSSL_NO_ECDSA or OPENSSL_NO_ECDH. Those names are no longer Configure options, so a successful no-ec run will not grow the old macros. configdata.pm records "ec" => "option" in %disabled.

The Makefile from that run has no ecdsa_ossl rule. A search of the build file for that object name returns nothing. That is the difference a build job can test without reading the release notes. no-ecdsa on 4.0.3 leaves the object in the Makefile. no-ec on 4.1.0 removes the rule.

A default 4.1.0 configure, with no extra disable flags, does not define OPENSSL_NO_EC. It does define the usual default exclusions such as OPENSSL_NO_EC_NISTP_64_GCC_128. Rejecting no-ecdsa is not the same as shipping a library without elliptic curves. The default build still has EC. A script that wanted ECDSA gone, and that now fails on the old flag, is not protected by the upgrade. It has to pass no-ec, then confirm both the macro and the missing object rule.

no-ec is broader than the old flag names. It disables elliptic-curve support, not a single signature or key-agreement entry point. If a build still needs ECDH, or still needs ECDSA, do not pass no-ec as a mechanical substitute for a flag that never did what its name said. The substitute is for the case where the script’s actual intent was to remove EC.

The binary advertises DTLS 1.3. The version line does not explain it

A default 4.1.0 tree, configured with perl ./Configure linux-x86_64 and built with make -j8, produced a working apps/openssl. It was run with the build directory on the library path so the new libssl.so.4 was the one it loaded. It was not installed. openssl version printed:

OpenSSL 4.1.0 23 Oct 2026 (Library: OpenSSL 4.1.0 23 Oct 2026)

openssl version -a adds the platform linux-x86_64, the compiler line, and the default OPENSSLDIR. It does not mention DTLS 1.3, and it does not mention the four known issues in the release notes.

openssl s_server -help on that binary lists four DTLS selectors:

-dtls                        Use any DTLS version
-dtls1                       Just talk DTLSv1
-dtls1_2                     Just talk DTLSv1.2
-dtls1_3                     Just talk DTLSv1.3

openssl s_client -help lists the same four, with the 1.3 line reading Just use DTLSv1.3. The 4.0.3 source tree’s apps/s_server.c option table has no dtls1_3 entry. The Debian 3.5.7 openssl s_server -dtls1_3 -help exits 1 and prints:

s_server: Unknown option: -dtls1_3
s_server: Use -help for summary.

On the built 4.1.0 binary, openssl s_server -dtls1_3 does not print an unknown-option error. It exits 1 because the default certificate file is absent:

Could not open file or uri for loading server certificate private key from server.pem: No such file or directory

That failure is the option parser accepting -dtls1_3 and then hitting the ordinary missing-certificate path. It is not a DTLS handshake, and it is not a test of the known issues. A job that treats any non-zero exit from s_server -dtls1_3 as “this build has no DTLS 1.3” will misread a missing server.pem.

The version string hides four DTLS notes

The 4.1.0 release notes list four known issues in the DTLS code that ships with the release. This run did not reproduce them. They still belong in the check, because openssl version will not surface them and a help line that mentions -dtls1_3 will not either.

The project says a DTLS 1.3 KeyUpdate drops other outstanding post-handshake records from the retransmission buffer, so a NewSessionTicket that was lost on the first send is not sent again. It says a permanent receive failure on a DTLS listener can be reported as SSL_ERROR_WANT_READ, and that later accept calls may also miss it. It says a retryable write failure on a nonblocking connection accepted from a DTLS listener can be reported as SSL_ERROR_SYSCALL. It says a DTLS 1.3 server may abort a handshake with unexpected_message when the client acknowledges part or all of the server’s first flight before sending its response. The notes point at GitHub issues 32878, 33277, 33278, and 33242. They say a fix for the KeyUpdate case is in progress. They do not list a fix for the other three.

Those sentences are the vendor’s description, not a result from this build. The operational point is narrower. A caller that treats SSL_ERROR_WANT_READ as “wait for the socket” and SSL_ERROR_SYSCALL as “the connection is dead” will misread the listener cases the project has already named. Do not adopt DTLS 1.3, or the new DTLS listener API, on the strength of the version string. Read those four notes, and keep the old error-code assumptions off any path that uses the new listener until the issues are closed in a later release.

The 4.1 DTLS guide says DTLSv1_listen supports only DTLS 1.0 and 1.2 with HelloVerifyRequest, and cannot be used with DTLS 1.3. This run did not call that function. A server that still uses it will not pick up the new protocol by upgrading the library and leaving the call in place.

The release also changes tsget from WWW::Curl::Easy to Net::Curl::Easy. This run did not invoke tsget. A host that still has only the old Perl module will fail that client after an upgrade that otherwise configures cleanly. That failure is independent of no-ecdsa. A build review that only deletes the unsupported Configure token will miss it.

Verify the script, then the binary

Run the checks against the tree you are about to build, not against a remembered version string.

Confirm the tarball digest before unpacking. Then run Configure with the flags the job actually passes, and with the target written out. If the command line still contains no-ecdsa or no-ecdh, 4.1.0 must exit non-zero and must not leave a Makefile. The error text has to include Unsupported options: and the flag name. A zero exit on 4.1.0 with either flag still present means you are not looking at this release.

If the script’s intent is to remove elliptic curves, replace the old flag with no-ec and configure again. After a zero exit, include/openssl/configuration.h must define OPENSSL_NO_EC. The Makefile must not contain an ecdsa_ossl rule. If the header defines only OPENSSL_NO_ECDSA or OPENSSL_NO_ECDH, you are still on a tree that accepted the old option, and the implementation object is still in the build file. Do not pass no-ec when the service still needs ECDSA or ECDH. The new option is wider than the names it replaces.

For a default build, after make and without installing, run openssl version and expect OpenSSL 4.1.0. Run openssl s_server -help and require the -dtls1_3 line. Then run openssl s_server -dtls1_3 only far enough to see that the error is the missing certificate, not Unknown option. Point the dynamic linker at the build directory if you have not installed the libraries. An unknown-option error means that binary is not the 4.1.0 build you think it is. A missing-certificate error means the flag was accepted. It does not mean a DTLS 1.3 handshake succeeded.

Do not treat the version line as a clean bill for DTLS 1.3. The four issues above are still open in the notes for this release. Keep them next to the help output when you decide whether a job may pass -dtls1_3 or use the listener API.

The Configure rejection is the part a build script can enforce today. The 4.0.3 Makefile is the reason the old flag was not a disable. The 4.1.0 binary’s help line is the part that shows DTLS 1.3 is present. None of those three is a substitute for the other two.

Sources