]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
BearSSL: add CURLOPT_SSL_CIPHER_LIST support
authorJan Venekamp <1422460+jan2000@users.noreply.github.com>
Mon, 6 Dec 2021 17:36:03 +0000 (18:36 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Sat, 12 Mar 2022 21:59:45 +0000 (22:59 +0100)
Closes #8477

docs/CIPHERS.md
docs/libcurl/opts/CURLOPT_PROXY_SSL_CIPHER_LIST.3
docs/libcurl/opts/CURLOPT_SSL_CIPHER_LIST.3
lib/vtls/bearssl.c

index ad5b5609189c8cea0a9a3b75fc4c3894dd7f12d2..e1df7642cf8ee580ddae082f6ac1f19cab7cba5d 100644 (file)
@@ -520,3 +520,61 @@ to [constrain the set of available ciphers as specified in the schannel
 documentation](https://docs.microsoft.com/en-us/windows/win32/secauthn/tls-cipher-suites-in-windows-server-2022).
 Note that the supported ciphers in this case follow the OS version, so if you
 are running an outdated OS you might still be supporting weak ciphers.
+
+## BearSSL
+
+BearSSL ciphers can be specified by either the OpenSSL name (`ECDHE-RSA-AES128-GCM-SHA256`) or the IANA name (`TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256`).
+
+Since BearSSL 0.1:
+
+`DES-CBC3-SHA`
+`AES128-SHA`
+`AES256-SHA`
+`AES128-SHA256`
+`AES256-SHA256`
+`AES128-GCM-SHA256`
+`AES256-GCM-SHA384`
+`ECDH-ECDSA-DES-CBC3-SHA`
+`ECDH-ECDSA-AES128-SHA`
+`ECDH-ECDSA-AES256-SHA`
+`ECDHE-ECDSA-DES-CBC3-SHA`
+`ECDHE-ECDSA-AES128-SHA`
+`ECDHE-ECDSA-AES256-SHA`
+`ECDH-RSA-DES-CBC3-SHA`
+`ECDH-RSA-AES128-SHA`
+`ECDH-RSA-AES256-SHA`
+`ECDHE-RSA-DES-CBC3-SHA`
+`ECDHE-RSA-AES128-SHA`
+`ECDHE-RSA-AES256-SHA`
+`ECDHE-ECDSA-AES128-SHA256`
+`ECDHE-ECDSA-AES256-SHA384`
+`ECDH-ECDSA-AES128-SHA256`
+`ECDH-ECDSA-AES256-SHA384`
+`ECDHE-RSA-AES128-SHA256`
+`ECDHE-RSA-AES256-SHA384`
+`ECDH-RSA-AES128-SHA256`
+`ECDH-RSA-AES256-SHA384`
+`ECDHE-ECDSA-AES128-GCM-SHA256`
+`ECDHE-ECDSA-AES256-GCM-SHA384`
+`ECDH-ECDSA-AES128-GCM-SHA256`
+`ECDH-ECDSA-AES256-GCM-SHA384`
+`ECDHE-RSA-AES128-GCM-SHA256`
+`ECDHE-RSA-AES256-GCM-SHA384`
+`ECDH-RSA-AES128-GCM-SHA256`
+`ECDH-RSA-AES256-GCM-SHA384`
+
+Since BearSSL 0.2:
+
+`ECDHE-RSA-CHACHA20-POLY1305`
+`ECDHE-ECDSA-CHACHA20-POLY1305`
+
+Since BearSSL 0.6:
+
+`AES128-CCM`
+`AES256-CCM`
+`AES128-CCM8`
+`AES256-CCM8`
+`ECDHE-ECDSA-AES128-CCM`
+`ECDHE-ECDSA-AES256-CCM`
+`ECDHE-ECDSA-AES128-CCM8`
+`ECDHE-ECDSA-AES256-CCM8`
index 6c1f754dbf2b597771f04e72702028d82af6dd9d..c5e10396bf639ccdf2082c0c2adeb6e62db868eb 100644 (file)
@@ -49,6 +49,13 @@ enabled.
 For WolfSSL, valid examples of cipher lists include \fBECDHE-RSA-RC4-SHA\fP,
 \fBAES256-SHA:AES256-SHA256\fP, etc.
 
+For BearSSL, valid examples of cipher lists include
+\fBECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256\fP, or when using IANA names
+\fBTLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\fP,
+etc.
+With BearSSL you do not add/remove ciphers. If one uses this option then all
+known ciphers are disabled and only those passed in are enabled.
+
 you will find more details about cipher lists on this URL:
 
  https://curl.se/docs/ssl-ciphers.html
@@ -71,7 +78,7 @@ if(curl) {
 }
 .fi
 .SH AVAILABILITY
-Added in 7.52.0
+Added in 7.52.0, in 7.83.0 for BearSSL
 
 If built TLS enabled.
 .SH RETURN VALUE
index 0b1c61f40d794fc14255f1a23580bc84b2b96c8c..90ee684a3437c405bd68e7dc630dc8926ed33bcd 100644 (file)
@@ -48,6 +48,13 @@ enabled.
 For WolfSSL, valid examples of cipher lists include \fBECDHE-RSA-RC4-SHA\fP,
 \fBAES256-SHA:AES256-SHA256\fP, etc.
 
+For BearSSL, valid examples of cipher lists include
+\fBECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256\fP, or when using IANA names
+\fBTLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\fP,
+etc.
+With BearSSL you do not add/remove ciphers. If one uses this option then all
+known ciphers are disabled and only those passed in are enabled.
+
 you will find more details about cipher lists on this URL:
 
  https://curl.se/docs/ssl-ciphers.html
@@ -69,6 +76,8 @@ if(curl) {
 }
 .fi
 .SH AVAILABILITY
+Added in 7.9, in 7.83.0 for BearSSL
+
 If built TLS enabled.
 .SH RETURN VALUE
 Returns CURLE_OK if TLS is supported, CURLE_UNKNOWN_OPTION if not, or
index 77e22cf3e0c7f7167d968f65ed9bb4844e973457..c06f85bdf1f8e8b1bd147808dc46bb030c249c6f 100644 (file)
@@ -35,6 +35,7 @@
 #include "multiif.h"
 #include "curl_printf.h"
 #include "curl_memory.h"
+#include "strcase.h"
 
 struct x509_context {
   const br_x509_class *vtable;
@@ -353,6 +354,216 @@ static const br_x509_class x509_vtable = {
   x509_get_pkey
 };
 
+struct st_cipher {
+  const char *name; /* Cipher suite IANA name. It starts with "TLS_" prefix */
+  const char *alias_name; /* Alias name is the same as OpenSSL cipher name */
+  uint16_t num; /* BearSSL cipher suite */
+};
+
+/* Macro to initialize st_cipher data structure */
+#define CIPHER_DEF(num, alias) { #num, alias, BR_##num }
+
+static const struct st_cipher ciphertable[] = {
+  /* RFC 2246 TLS 1.0 */
+  CIPHER_DEF(TLS_RSA_WITH_3DES_EDE_CBC_SHA,                        /* 0x000A */
+             "DES-CBC3-SHA"),
+
+  /* RFC 3268 TLS 1.0 AES */
+  CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA,                         /* 0x002F */
+             "AES128-SHA"),
+  CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA,                         /* 0x0035 */
+             "AES256-SHA"),
+
+  /* RFC 5246 TLS 1.2 */
+  CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA256,                      /* 0x003C */
+             "AES128-SHA256"),
+  CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA256,                      /* 0x003D */
+             "AES256-SHA256"),
+
+  /* RFC 5288 TLS 1.2 AES GCM */
+  CIPHER_DEF(TLS_RSA_WITH_AES_128_GCM_SHA256,                      /* 0x009C */
+             "AES128-GCM-SHA256"),
+  CIPHER_DEF(TLS_RSA_WITH_AES_256_GCM_SHA384,                      /* 0x009D */
+             "AES256-GCM-SHA384"),
+
+  /* RFC 4492 TLS 1.0 ECC */
+  CIPHER_DEF(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,                 /* 0xC003 */
+             "ECDH-ECDSA-DES-CBC3-SHA"),
+  CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,                  /* 0xC004 */
+             "ECDH-ECDSA-AES128-SHA"),
+  CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,                  /* 0xC005 */
+             "ECDH-ECDSA-AES256-SHA"),
+  CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,                /* 0xC008 */
+             "ECDHE-ECDSA-DES-CBC3-SHA"),
+  CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,                 /* 0xC009 */
+             "ECDHE-ECDSA-AES128-SHA"),
+  CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,                 /* 0xC00A */
+             "ECDHE-ECDSA-AES256-SHA"),
+  CIPHER_DEF(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,                   /* 0xC00D */
+             "ECDH-RSA-DES-CBC3-SHA"),
+  CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,                    /* 0xC00E */
+             "ECDH-RSA-AES128-SHA"),
+  CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,                    /* 0xC00F */
+             "ECDH-RSA-AES256-SHA"),
+  CIPHER_DEF(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,                  /* 0xC012 */
+             "ECDHE-RSA-DES-CBC3-SHA"),
+  CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,                   /* 0xC013 */
+             "ECDHE-RSA-AES128-SHA"),
+  CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,                   /* 0xC014 */
+             "ECDHE-RSA-AES256-SHA"),
+
+  /* RFC 5289 TLS 1.2 ECC HMAC SHA256/384 */
+  CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,              /* 0xC023 */
+             "ECDHE-ECDSA-AES128-SHA256"),
+  CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,              /* 0xC024 */
+             "ECDHE-ECDSA-AES256-SHA384"),
+  CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,               /* 0xC025 */
+             "ECDH-ECDSA-AES128-SHA256"),
+  CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,               /* 0xC026 */
+             "ECDH-ECDSA-AES256-SHA384"),
+  CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,                /* 0xC027 */
+             "ECDHE-RSA-AES128-SHA256"),
+  CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,                /* 0xC028 */
+             "ECDHE-RSA-AES256-SHA384"),
+  CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,                 /* 0xC029 */
+             "ECDH-RSA-AES128-SHA256"),
+  CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,                 /* 0xC02A */
+             "ECDH-RSA-AES256-SHA384"),
+
+  /* RFC 5289 TLS 1.2 GCM */
+  CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,              /* 0xC02B */
+             "ECDHE-ECDSA-AES128-GCM-SHA256"),
+  CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,              /* 0xC02C */
+             "ECDHE-ECDSA-AES256-GCM-SHA384"),
+  CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,               /* 0xC02D */
+             "ECDH-ECDSA-AES128-GCM-SHA256"),
+  CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,               /* 0xC02E */
+             "ECDH-ECDSA-AES256-GCM-SHA384"),
+  CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,                /* 0xC02F */
+             "ECDHE-RSA-AES128-GCM-SHA256"),
+  CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,                /* 0xC030 */
+             "ECDHE-RSA-AES256-GCM-SHA384"),
+  CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,                 /* 0xC031 */
+             "ECDH-RSA-AES128-GCM-SHA256"),
+  CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,                 /* 0xC032 */
+             "ECDH-RSA-AES256-GCM-SHA384"),
+#ifdef BR_TLS_RSA_WITH_AES_128_CCM
+
+  /* RFC 6655 TLS 1.2 CCM
+     Supported since BearSSL 0.6 */
+  CIPHER_DEF(TLS_RSA_WITH_AES_128_CCM,                             /* 0xC09C */
+             "AES128-CCM"),
+  CIPHER_DEF(TLS_RSA_WITH_AES_256_CCM,                             /* 0xC09D */
+             "AES256-CCM"),
+  CIPHER_DEF(TLS_RSA_WITH_AES_128_CCM_8,                           /* 0xC0A0 */
+             "AES128-CCM8"),
+  CIPHER_DEF(TLS_RSA_WITH_AES_256_CCM_8,                           /* 0xC0A1 */
+             "AES256-CCM8"),
+
+  /* RFC 7251 TLS 1.2 ECC CCM
+     Supported since BearSSL 0.6 */
+  CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CCM,                     /* 0xC0AC */
+             "ECDHE-ECDSA-AES128-CCM"),
+  CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CCM,                     /* 0xC0AD */
+             "ECDHE-ECDSA-AES256-CCM"),
+  CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,                   /* 0xC0AE */
+             "ECDHE-ECDSA-AES128-CCM8"),
+  CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,                   /* 0xC0AF */
+             "ECDHE-ECDSA-AES256-CCM8"),
+#endif
+
+  /* RFC 7905 TLS 1.2 ChaCha20-Poly1305
+     Supported since BearSSL 0.2 */
+  CIPHER_DEF(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,          /* 0xCCA8 */
+             "ECDHE-RSA-CHACHA20-POLY1305"),
+  CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,        /* 0xCCA9 */
+             "ECDHE-ECDSA-CHACHA20-POLY1305"),
+};
+
+#define NUM_OF_CIPHERS (sizeof(ciphertable) / sizeof(ciphertable[0]))
+#define CIPHER_NAME_BUF_LEN 64
+
+static bool is_separator(char c)
+{
+  /* Return whether character is a cipher list separator. */
+  switch(c) {
+    case ' ':
+    case '\t':
+    case ':':
+    case ',':
+    case ';':
+      return true;
+  }
+  return false;
+}
+
+static CURLcode bearssl_set_selected_ciphers(struct Curl_easy *data,
+                                             br_ssl_engine_context *ssl_eng,
+                                             const char *ciphers)
+{
+  uint16_t selected_ciphers[NUM_OF_CIPHERS];
+  size_t selected_count = 0;
+  char cipher_name[CIPHER_NAME_BUF_LEN];
+  const char *cipher_start = ciphers;
+  const char *cipher_end;
+  size_t i, j;
+
+  if(!cipher_start)
+    return CURLE_SSL_CIPHER;
+
+  while(true) {
+    /* Extract the next cipher name from the ciphers string */
+    while(is_separator(*cipher_start))
+      ++cipher_start;
+    if(*cipher_start == '\0')
+      break;
+    cipher_end = cipher_start;
+    while(*cipher_end != '\0' && !is_separator(*cipher_end))
+      ++cipher_end;
+    j = cipher_end - cipher_start < CIPHER_NAME_BUF_LEN - 1 ?
+        cipher_end - cipher_start : CIPHER_NAME_BUF_LEN - 1;
+    strncpy(cipher_name, cipher_start, j);
+    cipher_name[j] = '\0';
+    cipher_start = cipher_end;
+
+    /* Lookup the cipher name in the table of available ciphers. If the cipher
+       name starts with "TLS_" we do the lookup by IANA name. Otherwise, we try
+       to match cipher name by an (OpenSSL) alias. */
+    if(strncasecompare(cipher_name, "TLS_", 4)) {
+      for(i = 0; i < NUM_OF_CIPHERS &&
+                 !strcasecompare(cipher_name, ciphertable[i].name); ++i);
+    }
+    else {
+      for(i = 0; i < NUM_OF_CIPHERS &&
+                 !strcasecompare(cipher_name, ciphertable[i].alias_name); ++i);
+    }
+    if(i == NUM_OF_CIPHERS) {
+      infof(data, "BearSSL: unknown cipher in list: %s", cipher_name);
+      continue;
+    }
+
+    /* No duplicates allowed */
+    for(j = 0; j < selected_count &&
+               selected_ciphers[j] != ciphertable[i].num; j++);
+    if(j < selected_count) {
+      infof(data, "BearSSL: duplicate cipher in list: %s", cipher_name);
+      continue;
+    }
+
+    DEBUGASSERT(selected_count < NUM_OF_CIPHERS);
+    selected_ciphers[selected_count] = ciphertable[i].num;
+    ++selected_count;
+  }
+
+  if(selected_count == 0) {
+    failf(data, "BearSSL: no supported cipher in list");
+    return CURLE_SSL_CIPHER;
+  }
+
+  br_ssl_engine_set_suites(ssl_eng, selected_ciphers, selected_count);
+  return CURLE_OK;
+}
+
 static CURLcode bearssl_connect_step1(struct Curl_easy *data,
                                       struct connectdata *conn, int sockindex)
 {
@@ -446,6 +657,15 @@ static CURLcode bearssl_connect_step1(struct Curl_easy *data,
   br_ssl_engine_set_buffer(&backend->ctx.eng, backend->buf,
                            sizeof(backend->buf), 1);
 
+  if(SSL_CONN_CONFIG(cipher_list)) {
+    /* Override the ciphers as specified. For the default cipher list see the
+       BearSSL source code of br_ssl_client_init_full() */
+    ret = bearssl_set_selected_ciphers(data, &backend->ctx.eng,
+                                       SSL_CONN_CONFIG(cipher_list));
+    if(ret)
+      return ret;
+  }
+
   /* initialize X.509 context */
   backend->x509.vtable = &x509_vtable;
   backend->x509.verifypeer = verifypeer;