Files
jak-project/third-party/curl/docs/CIPHERS.md
T
Alexander J. Semenuk 5d5e35fb9b fix: Windows toolchain compatibility (curl 8.21 re-vendor, endless reconfigure loop) (#4355)
## Problem

Windows builds break with a current local toolchain (Scoop LLVM 22.1.8,
CMake 4.4.0, VS 2026), in two independent ways:

1. The build stops at curl's deliberate guard: `#error "no non-blocking
method was found/used/set"` in `third-party/curl/lib/nonblock.c`.
2. From the second configure onward, `cmake --build` re-runs CMake in an
endless loop (observed 42 consecutive reconfigure cycles in a single
build). Likely the same mechanism behind the "endlessly building" VS
2026 note in `docs/setup/dev/vs.md`.

## Root cause

1. `third-party/curl/CMake/CurlTests.c` passes `int *` to
`ioctlsocket()`, whose third parameter is `u_long *`. Clang 22 promotes
`-Wincompatible-pointer-types` to a hard error in C, so the
`HAVE_IOCTLSOCKET_FIONBIO` try_compile silently fails and
`curl_config.h` never defines it. Upstream CI does not see this because
the windows-2022 runner image ships an older LLVM. GCC 14 promotes the
same warning to a hard error, which is very likely the `CurlTests.c.obj`
failure reported from MSYS2 in open-goal/jak-project#3551. Upstream curl
hit the identical problem with GCC 14 and fixed the probe in curl 8.8.0
(curl/curl#13578).
2. The root CMakeLists copies the build tree's `compile_commands.json`
into `<src>/build/` for clangd using `configure_file()`, which registers
its input as a configure dependency. CMake rewrites
`compile_commands.json` late in every generation, after
`CTestTestfile.cmake` and `cmake_install.cmake` (outputs of the same
Ninja regen rule), so once the dependency is registered the rule is
deterministically dirty and every `ninja` invocation re-runs CMake. A
pristine first configure is safe (the file does not exist yet, so the
`if(EXISTS ...)` guard skips the copy), which is why the loop looks
machine- or IDE-specific.

## Fix

1. Per review, re-vendor `third-party/curl` at the `curl-8_21_0` tag
(previously `curl-8_3_0`), which carries the upstream probe fix plus two
years of upstream development; `vendor.yaml` updated to match.
Adjustments the version jump forced:
- curl 8.15 removed the native macOS Secure Transport backend
(`CURL_USE_SECTRANSP`), so macOS now builds curl against OpenSSL like
Linux. The two macOS workflows install Homebrew `openssl@3` and export
`OPENSSL_ROOT_DIR` (keg-only), and the macOS setup docs gained the same
two lines.
- `CURL_BROTLI` / `CURL_ZSTD` switched to AUTO-detection in curl 8.10;
pinned OFF to keep the previous no-compression behavior and avoid
silently linking whatever the CI images happen to have.
- curl's new top-level `BUILD_EXAMPLES` cache option (default ON) leaked
into discord-rpc's identically named option and broke configure at a
nonexistent `examples/send-presence` directory; pinned OFF ahead of the
third-party subdirectories.

The diff is dominated by the mechanical tag-tree swap under
`third-party/curl` (linguist-vendored, collapsed in review). The
hand-written changes are `CMakeLists.txt`, the two macOS workflows,
`docs/setup/system/macos.md`, and `vendor.yaml`.
2. Swap `configure_file()` for `file(COPY ...)`: the same clangd copy
with no configure dependency registered. (`file(COPY_FILE ...
ONLY_IF_DIFFERENT)` would be cleaner still but requires CMake 3.21,
above the declared `cmake_minimum_required(VERSION 3.10)`.)

## Test plan

- [x] Fresh `cmake --preset Release-windows-clang` (LLVM 22, no cache
seeding) completes and logs `Enabled SSL backends: Schannel`; the
FIONBIO probe passes without the previous `#error`
- [x] Full Windows Release build from scratch in the branch worktree
(all 1422 targets)
- [x] goalc-test suite: 1509 passed, 0 failed
- [x] Second consecutive configure with `compile_commands.json` present:
the regen rule in `build.ninja` has no `compile_commands.json` input;
`<src>/build/compile_commands.json` is still refreshed for clangd
- [x] Repeated `ninja` invocations after a full build no longer re-run
CMake
- [x] macOS Intel and ARM CI green (first exercise of the OpenSSL
backend switch)

---

I work off a self-hosted forge, so this GitHub account is quiet; the
configure logs and ninja dirty-node traces from the investigation are
available if anyone wants the raw data.

(AI-assisted)
2026-07-27 19:19:18 -04:00

11 KiB
Vendored
Generated

curl cipher options

A TLS handshake involves many parameters which take part in the negotiation between client and server in order to agree on the TLS version and set of algorithms to use for a connection.

What has become known as a "cipher" or better "cipher suite" in TLS are names for specific combinations of key exchange, bulk encryption, message authentication code and with TLSv1.3 the authenticated encryption. In addition, there are other parameters that influence the TLS handshake, like DHE "groups" and ECDHE with its "curves".

History

curl's way of letting users configure these settings closely followed OpenSSL in its API. TLS learned new parameters, OpenSSL added new API functions and curl added command line options.

Several other TLS backends followed the OpenSSL approach, more or less closely, and curl maps the command line options to these TLS backends. Some TLS backends do not support all of it and command line options are either ignored or lead to an error.

Many examples below show the OpenSSL-like use of these options. GnuTLS however chose a different approach. These are described in a separate section further below.

ciphers, the OpenSSL way

With curl's option --tls13-ciphers or CURLOPT_TLS13_CIPHERS users can control which cipher suites to consider when negotiating TLS 1.3 connections. With option --ciphers or CURLOPT_SSL_CIPHER_LIST users can control which cipher suites to consider when negotiating TLS 1.2 (1.1, 1.0) connections.

By default, curl may negotiate TLS 1.3 and TLS 1.2 connections, so the cipher suites considered when negotiating a TLS connection are a union of the TLS 1.3 and TLS 1.2 cipher suites. If you want curl to consider only TLS 1.3 cipher suites for the connection, you have to set the minimum TLS version to 1.3 by using --tlsv1.3 or CURLOPT_SSLVERSION with CURL_SSLVERSION_TLSv1_3.

Both the TLS 1.3 and TLS 1.2 cipher options expect a list of cipher suites separated by colons (:). This list is parsed opportunistically, cipher suites that are not recognized or implemented are ignored. As long as there is at least one recognized cipher suite in the list, the list is considered valid.

For both the TLS 1.3 and TLS 1.2 cipher options, the order in which the cipher suites are specified determine the preference of them. When negotiating a TLS connection the server picks a cipher suite from the intersection of the cipher suites supported by the server and the cipher suites sent by curl. If the server is configured to honor the client's cipher preference, the first common cipher suite in the list sent by curl is chosen.

TLS 1.3 cipher suites

Setting TLS 1.3 cipher suites is supported by curl with OpenSSL (1.1.1+, curl 7.61.0+), LibreSSL (3.4.1+, curl 8.3.0+), wolfSSL (curl 8.10.0+) and mbedTLS (3.6.0+, curl 8.10.0+).

The list of cipher suites that can be used for the --tls13-ciphers option:

TLS_AES_128_GCM_SHA256
TLS_AES_256_GCM_SHA384
TLS_CHACHA20_POLY1305_SHA256
TLS_AES_128_CCM_SHA256
TLS_AES_128_CCM_8_SHA256

wolfSSL notes

In addition to above list the following cipher suites can be used: TLS_SM4_GCM_SM3 TLS_SM4_CCM_SM3 TLS_SHA256_SHA256 TLS_SHA384_SHA384. Usage of these cipher suites is not recommended. (The last two cipher suites are NULL ciphers, offering no encryption whatsoever.)

TLS 1.2 (1.1, 1.0) cipher suites

Setting TLS 1.2 cipher suites is supported by curl with AWS-LC, BoringSSL, LibreSSL, mbedTLS (curl 8.8.0+), OpenSSL, wolfSSL (curl 7.53.0+). Schannel does not support setting cipher suites directly, but does support setting algorithms (curl 7.61.0+), see Schannel notes below.

For TLS 1.2 cipher suites there are multiple naming schemes, the two most used are with OpenSSL names (e.g. ECDHE-RSA-AES128-GCM-SHA256) and IANA names (e.g. TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256). IANA names of TLS 1.2 cipher suites look similar to TLS 1.3 cipher suite names, to distinguish them note that TLS 1.2 names contain _WITH_, while TLS 1.3 names do not. When setting TLS 1.2 cipher suites with curl it is recommended that you use OpenSSL names as these are most widely recognized by the supported SSL backends.

The complete list of cipher suites that may be considered for the --ciphers option is extensive, it consists of more than 300 ciphers suites. Nowadays, most of them are discouraged, and support for a lot of them has been removed from the various SSL backends, if ever implemented at all.

A shortened list (based on recommendations by Mozilla) of cipher suites, which are (mostly) supported by all SSL backends, that can be used for the --ciphers option:

ECDHE-ECDSA-AES128-GCM-SHA256
ECDHE-RSA-AES128-GCM-SHA256
ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-RSA-AES256-GCM-SHA384
ECDHE-ECDSA-CHACHA20-POLY1305
ECDHE-RSA-CHACHA20-POLY1305
DHE-RSA-AES128-GCM-SHA256
DHE-RSA-AES256-GCM-SHA384
DHE-RSA-CHACHA20-POLY1305
ECDHE-ECDSA-AES128-SHA256
ECDHE-RSA-AES128-SHA256
ECDHE-ECDSA-AES128-SHA
ECDHE-RSA-AES128-SHA
ECDHE-ECDSA-AES256-SHA384
ECDHE-RSA-AES256-SHA384
ECDHE-ECDSA-AES256-SHA
ECDHE-RSA-AES256-SHA
DHE-RSA-AES128-SHA256
DHE-RSA-AES256-SHA256
AES128-GCM-SHA256
AES256-GCM-SHA384
AES128-SHA256
AES256-SHA256
AES128-SHA
AES256-SHA
DES-CBC3-SHA

See this list for a complete list of TLS 1.2 cipher suites.

OpenSSL notes

In addition to specifying a list of cipher suites, OpenSSL also accepts a format with specific cipher strings (like TLSv1.2, AESGCM, CHACHA20) and !, - and + operators. Refer to the OpenSSL cipher documentation for further information on that format.

Schannel notes

Schannel does not support setting individual TLS 1.2 cipher suites directly. It only allows the enabling and disabling of encryption algorithms. These are in the form of CALG_xxx, see the Schannel ALG_ID documentation for a list of these algorithms. Also, (since curl 7.77.0) SCH_USE_STRONG_CRYPTO can be given to pass that flag to Schannel, lookup the documentation for the Windows version in use to see how that affects the cipher suite selection. When not specifying the --ciphers and --tls13-ciphers options curl passes this flag by default.

Examples

curl \
  --tls13-ciphers TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256 \
  --ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\
ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305 \
  https://example.com/

Restrict ciphers to aes128-gcm and chacha20. Works with OpenSSL, LibreSSL, mbedTLS and wolfSSL.

curl \
  --tlsv1.3 \
  --tls-max 1.3 \
  --tls13-ciphers TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256 \
  https://example.com/

Restrict to only TLS 1.3 with aes128-gcm and chacha20 ciphers. Works with OpenSSL, LibreSSL, mbedTLS and wolfSSL.

curl \
  --ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\
ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305 \
  https://example.com/

Restrict TLS 1.2 ciphers to aes128-gcm and chacha20, use default TLS 1.3 ciphers (if TLS 1.3 is available). Works with OpenSSL, LibreSSL, BoringSSL, mbedTLS and wolfSSL.

ciphers, the GnuTLS way

With GnuTLS, curl allows configuration of all TLS parameters via option --ciphers or CURLOPT_SSL_CIPHER_LIST only. The option --tls13-ciphers or CURLOPT_TLS13_CIPHERS is being ignored.

--ciphers is used to set the GnuTLS priority string in the following way:

  • When the set string starts with '+', '-' or '!' it is appended to the priority string libcurl itself generates (separated by ':'). This initial priority depends other settings such as CURLOPT_SSLVERSION(3), CURLOPT_TLSAUTH_USERNAME(3) (for SRP) or if HTTP/3 (QUIC) is being negotiated.
  • Otherwise, the set string fully replaces the libcurl generated one. While giving full control to the application, the set priority needs to provide for everything the transfer may need to negotiate. Example: if the set priority only allows TLSv1.2, all HTTP/3 attempts fail.

Users may specify via --ciphers anything that GnuTLS supports: ciphers, key exchange, MAC, compression, TLS versions, signature algorithms, groups, elliptic curves, certificate types. In addition, GnuTLS has a variety of other keywords that tweak its operations. Applications or a system may define new alias names for priority strings that can then be used here.

Since the order of items in priority strings is significant, it makes no sense for curl to puzzle other SSL options somehow together. --ciphers is the single way to change priority.

Examples

curl \
  --ciphers '-CIPHER_ALL:+AES-128-GCM:+CHACHA20-POLY1305' \
  https://example.com/

Restrict ciphers to aes128-gcm and chacha20 in GnuTLS.

curl \
  --ciphers 'NORMAL:-VERS-ALL:+TLS1.3:-AES-256-GCM' \
  https://example.com/

Restrict to only TLS 1.3 without the aes256-gcm cipher.

curl \
  --ciphers 'NORMAL:-VERS-ALL:+TLS1.2:-CIPHER_ALL:+CAMELLIA-128-GCM' \
  https://example.com/

Restrict to only TLS 1.2 with the CAMELLIA-128-GCM cipher.

Further reading