DevOps
devtools
DHCP
+DHE
dir
distro
distro's
## 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](https://en.wikipedia.org/wiki/Key_exchange),
+[bulk encryption](https://en.wikipedia.org/wiki/Link_encryption),
+[message authentication code](https://en.wikipedia.org/wiki/Message_authentication_code)
+and with TLSv1.3 the
+[authenticated encryption](https://en.wikipedia.org/wiki/Authenticated_encryption).
+In addition, there are other parameters that influence the TLS handshake, like
+[DHE](https://en.wikipedia.org/wiki/Diffie–Hellman_key_exchange) "groups" and
+[ECDHE](https://en.wikipedia.org/wiki/Elliptic-curve_Diffie–Hellman) 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`](https://curl.se/docs/manpage.html#--tls13-ciphers)
or
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
+### 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+),
TLS_AES_128_CCM_8_SHA256
```
-### wolfSSL notes
+#### 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
+### TLS 1.2 (1.1, 1.0) cipher suites
Setting TLS 1.2 cipher suites is supported by curl with OpenSSL, LibreSSL,
BoringSSL, mbedTLS (curl 8.8.0+), wolfSSL (curl 7.53.0+),
See this [list](https://github.com/curl/curl/blob/master/docs/CIPHERS-TLS12.md)
for a complete list of TLS 1.2 cipher suites.
-### OpenSSL notes
+#### 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
[OpenSSL cipher documentation](https://docs.openssl.org/master/man1/openssl-ciphers/#cipher-list-format)
for further information on that format.
-### Schannel notes
+#### 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
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
+### Examples
```sh
curl \
ciphers (if TLS 1.3 is available). Works with OpenSSL, LibreSSL, BoringSSL,
mbedTLS, wolfSSL, Secure Transport and BearSSL.
+## ciphers, the GnuTLS way
+
+With GnuTLS, curl allows configuration of all TLS parameters via option
+[`--ciphers`](https://curl.se/docs/manpage.html#--ciphers)
+or
+[`CURLOPT_SSL_CIPHER_LIST`](https://curl.se/libcurl/c/CURLOPT_SSL_CIPHER_LIST.html)
+only. The option
+[`--tls13-ciphers`](https://curl.se/docs/manpage.html#--tls13-ciphers)
+or
+[`CURLOPT_TLS13_CIPHERS`](https://curl.se/libcurl/c/CURLOPT_TLS13_CIPHERS.html)
+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
+
+```sh
+curl \
+ --ciphers '-CIPHER_ALL:+AES-128-GCM:+CHACHA20-POLY1305' \
+ https://example.com/
+```
+Restrict ciphers to `aes128-gcm` and `chacha20` in GnuTLS.
+
+```sh
+curl \
+ --ciphers 'NORMAL:-VERS-ALL:+TLS1.3:-AES-256-GCM' \
+ https://example.com/
+```
+Restrict to only TLS 1.3 without the `aes256-gcm` cipher.
+
+```sh
+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
- [OpenSSL cipher suite names documentation](https://docs.openssl.org/master/man1/openssl-ciphers/#cipher-suite-names)
- [wolfSSL cipher support documentation](https://www.wolfssl.com/documentation/manuals/wolfssl/chapter04.html#cipher-support)
- [Secure Transport cipher suite values](https://developer.apple.com/documentation/security/1550981-ssl_cipher_suite_values)
- [IANA cipher suites list](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4)
- [Wikipedia cipher suite article](https://en.wikipedia.org/wiki/Cipher_suite)
+- [GnuTLS Priority Strings](https://gnutls.org/manual/html_node/Priority-Strings.html)
- wolfSSL
- mbedTLS
- rustls
+ - GnuTLS
Added-in: 7.9
---
For setting TLS 1.3 ciphers see CURLOPT_TLS13_CIPHERS(3).
-A valid example of a cipher list is:
+A valid example of a cipher list with OpenSSL is:
~~~
"ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:"
"ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305"
For Schannel, you can use this option to set algorithms but not specific
cipher suites. Refer to the ciphers lists document for algorithms.
+GnuTLS has the concept of a
+[priority string](https://gnutls.org/manual/html_node/Priority-Strings.html)
+which has its own syntax and keywords. The string set via
+CURLOPT_SSL_CIPHER_LIST(3) directly influences the priority setting.
+
Find more details about cipher lists on this URL:
https://curl.se/docs/ssl-ciphers.html
/* The last #include file should be: */
#include "memdebug.h"
-#define QUIC_PRIORITY \
- "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
- "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
- "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
- "%DISABLE_TLS13_COMPAT_MODE"
-
/* Enable GnuTLS debugging by defining GTLSDEBUG */
/*#define GTLSDEBUG */
*/
#define GNUTLS_SRP "+SRP"
+#define QUIC_PRIORITY \
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
+ "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
+ "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
+ "%DISABLE_TLS13_COMPAT_MODE"
+
static CURLcode
gnutls_set_ssl_version_min_max(struct Curl_easy *data,
struct ssl_peer *peer,
struct ssl_primary_config *conn_config,
const char **prioritylist,
- const char *tls13support)
+ bool tls13support)
{
long ssl_version = conn_config->version;
long ssl_version_max = conn_config->version_max;
return 0;
}
+static CURLcode gtls_set_priority(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct gtls_ctx *gtls,
+ const char *priority)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct dynbuf buf;
+ const char *err = NULL;
+ CURLcode result = CURLE_OK;
+ int rc;
+
+ Curl_dyn_init(&buf, 4096);
+
+#ifdef USE_GNUTLS_SRP
+ if(conn_config->username) {
+ /* Only add SRP to the cipher list if SRP is requested. Otherwise
+ * GnuTLS will disable TLS 1.3 support. */
+ result = Curl_dyn_add(&buf, priority);
+ if(!result)
+ result = Curl_dyn_add(&buf, ":" GNUTLS_SRP);
+ if(result)
+ goto out;
+ priority = Curl_dyn_ptr(&buf);
+ }
+#endif
+
+ if(conn_config->cipher_list) {
+ if((conn_config->cipher_list[0] == '+') ||
+ (conn_config->cipher_list[0] == '-') ||
+ (conn_config->cipher_list[0] == '!')) {
+ /* add it to out own */
+ if(!Curl_dyn_len(&buf)) { /* not added yet */
+ result = Curl_dyn_add(&buf, priority);
+ if(result)
+ goto out;
+ }
+ result = Curl_dyn_addf(&buf, ":%s", conn_config->cipher_list);
+ if(result)
+ goto out;
+ priority = Curl_dyn_ptr(&buf);
+ }
+ else /* replace our own completely */
+ priority = conn_config->cipher_list;
+ }
+
+ infof(data, "GnuTLS priority: %s", priority);
+ rc = gnutls_priority_set_direct(gtls->session, priority, &err);
+ if(rc != GNUTLS_E_SUCCESS) {
+ failf(data, "Error %d setting GnuTLS priority: %s", rc, err);
+ result = CURLE_SSL_CONNECT_ERROR;
+ }
+
+out:
+ Curl_dyn_free(&buf);
+ return result;
+}
+
static CURLcode gtls_client_init(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
int rc;
bool sni = TRUE; /* default is SNI enabled */
const char *prioritylist;
- const char *err = NULL;
- const char *tls13support;
+ bool tls13support;
CURLcode result;
if(!gtls_inited)
return CURLE_SSL_CONNECT_ERROR;
/* "In GnuTLS 3.6.5, TLS 1.3 is enabled by default" */
- tls13support = gnutls_check_version("3.6.5");
+ tls13support = !!gnutls_check_version("3.6.5");
/* Ensure +SRP comes at the *end* of all relevant strings so that it can be
* removed if a runtime error indicates that SRP is not supported by this
if(result)
return result;
-#ifdef USE_GNUTLS_SRP
- /* Only add SRP to the cipher list if SRP is requested. Otherwise
- * GnuTLS will disable TLS 1.3 support. */
- if(config->username) {
- char *prioritysrp = aprintf("%s:" GNUTLS_SRP, prioritylist);
- if(!prioritysrp)
- return CURLE_OUT_OF_MEMORY;
- rc = gnutls_priority_set_direct(gtls->session, prioritysrp, &err);
- free(prioritysrp);
-
- if((rc == GNUTLS_E_INVALID_REQUEST) && err) {
- infof(data, "This GnuTLS does not support SRP");
- }
- }
- else {
-#endif
- infof(data, "GnuTLS ciphers: %s", prioritylist);
- rc = gnutls_priority_set_direct(gtls->session, prioritylist, &err);
-#ifdef USE_GNUTLS_SRP
- }
-#endif
-
- if(rc != GNUTLS_E_SUCCESS) {
- failf(data, "Error %d setting GnuTLS cipher list starting with %s",
- rc, err);
- return CURLE_SSL_CONNECT_ERROR;
- }
+ result = gtls_set_priority(cf, data, gtls, prioritylist);
+ if(result)
+ return result;
if(config->clientcert) {
if(!gtls->shared_creds->trust_setup) {
SSLSUPP_CERTINFO |
SSLSUPP_PINNEDPUBKEY |
SSLSUPP_HTTPS_PROXY |
+ SSLSUPP_CIPHER_LIST |
SSLSUPP_CA_CACHE,
sizeof(struct gtls_ssl_backend_data),
proto = 'h3'
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
+ if env.curl_uses_lib('gnutls'):
+ pytest.skip("gnutls does not ingore --ciphers on TLSv1.3")
curl = CurlClient(env=env)
url = f'https://{env.authority_for(env.domain1, proto)}/curltest/sslinfo'
r = curl.http_get(url=url, alpn_proto=proto, extra_args=[
'--tls13-ciphers', 'NONSENSE', '--tls-max', '1.2'
])
assert r.exit_code == 0, f'{r}'
+
+ @pytest.mark.parametrize("priority, tls_proto, ciphers, success", [
+ ("", "", [], False),
+ ("NONSENSE", "", [], False),
+ ("+NONSENSE", "", [], False),
+ ("NORMAL:-VERS-ALL:+VERS-TLS1.2", "TLSv1.2", ['ECDHE-RSA-CHACHA20-POLY1305'], True),
+ ("-VERS-ALL:+VERS-TLS1.2", "TLSv1.2", ['ECDHE-RSA-CHACHA20-POLY1305'], True),
+ ("NORMAL", "TLSv1.3", ['TLS_CHACHA20_POLY1305_SHA256'], True),
+ ("NORMAL:-VERS-ALL:+VERS-TLS1.3", "TLSv1.3", ['TLS_CHACHA20_POLY1305_SHA256'], True),
+ ("-VERS-ALL:+VERS-TLS1.3", "TLSv1.3", ['TLS_CHACHA20_POLY1305_SHA256'], True),
+ ("!CHACHA20-POLY1305", "TLSv1.3", ['TLS_AES_128_GCM_SHA256'], True),
+ ("-CIPHER-ALL:+CHACHA20-POLY1305", "TLSv1.3", ['TLS_CHACHA20_POLY1305_SHA256'], True),
+ ("-CIPHER-ALL:+AES-256-GCM", "", [], False),
+ ("-CIPHER-ALL:+AES-128-GCM", "TLSv1.3", ['TLS_AES_128_GCM_SHA256'], True),
+ ("SECURE:-CIPHER-ALL:+AES-128-GCM:-VERS-ALL:+VERS-TLS1.2", "TLSv1.2", ['ECDHE-RSA-AES128-GCM-SHA256'], True),
+ ("-MAC-ALL:+SHA256", "", [], False),
+ ("-MAC-ALL:+AEAD", "TLSv1.3", ['TLS_CHACHA20_POLY1305_SHA256'], True),
+ ("-GROUP-ALL:+GROUP-X25519", "TLSv1.3", ['TLS_CHACHA20_POLY1305_SHA256'], True),
+ ("-GROUP-ALL:+GROUP-SECP192R1", "", [], False),
+ ])
+ def test_17_18_gnutls_priority(self, env: Env, httpd, priority, tls_proto, ciphers, success):
+ # to test setting cipher suites, the AES 256 ciphers are disabled in the test server
+ httpd.set_extra_config('base', [
+ 'SSLCipherSuite SSL'
+ ' ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256'
+ ':ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305',
+ 'SSLCipherSuite TLSv1.3'
+ ' TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256',
+ ])
+ httpd.reload_if_config_changed()
+ proto = 'http/1.1'
+ curl = CurlClient(env=env)
+ url = f'https://{env.authority_for(env.domain1, proto)}/curltest/sslinfo'
+ # SSL backend specifics
+ if not env.curl_uses_lib('gnutls'):
+ pytest.skip('curl not build with GnuTLS')
+ # test
+ extra_args = ['--ciphers', f'{priority}']
+ r = curl.http_get(url=url, alpn_proto=proto, extra_args=extra_args)
+ if success:
+ assert r.exit_code == 0, r.dump_logs()
+ assert r.json['HTTPS'] == 'on', r.dump_logs()
+ if tls_proto:
+ assert r.json['SSL_PROTOCOL'] == tls_proto, r.dump_logs()
+ assert r.json['SSL_CIPHER'] in ciphers, r.dump_logs()
+ else:
+ assert r.exit_code != 0, r.dump_logs()