core/curl/bearssl-support-curl-8.17.0.patch
$ cat bearssl-support-curl-8.17.0.patch
From 56dcc1a0f326525405b0134fb190433e362cb5d7 Mon Sep 17 00:00:00 2001
From: shrub <maybeshrub@gmail.com>
Date: Tue, 17 Mar 2026 19:51:18 +0000
Subject: [PATCH 1/2] tls: restore BearSSL backend support

---
 CMake/FindBearSSL.cmake |   58 ++
 CMakeLists.txt          |   20 +-
 Makefile.am             |    1 +
 configure.ac            |   41 +-
 lib/CMakeLists.txt      |    2 +
 lib/Makefile.inc        |    2 +
 lib/curl_config.h.cmake |    3 +
 lib/curl_setup.h        |    2 +
 lib/vtls/bearssl.c      | 1104 +++++++++++++++++++++++++++++++++++++++
 lib/vtls/bearssl.h      |   34 ++
 lib/vtls/cipher_suite.c |   24 +-
 lib/vtls/cipher_suite.h |    6 +-
 lib/vtls/vtls.c         |    6 +
 m4/curl-bearssl.m4      |  111 ++++
 14 files changed, 1401 insertions(+), 13 deletions(-)
 create mode 100644 CMake/FindBearSSL.cmake
 create mode 100644 lib/vtls/bearssl.c
 create mode 100644 lib/vtls/bearssl.h
 create mode 100644 m4/curl-bearssl.m4

diff --git a/CMake/FindBearSSL.cmake b/CMake/FindBearSSL.cmake
new file mode 100644
index 0000000000..ff55be0f22
--- /dev/null
+++ b/CMake/FindBearSSL.cmake
@@ -0,0 +1,58 @@
+#***************************************************************************
+#                                  _   _ ____  _
+#  Project                     ___| | | |  _ \| |
+#                             / __| | | | |_) | |
+#                            | (__| |_| |  _ <| |___
+#                             \___|\___/|_| \_\_____|
+#
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at https://curl.se/docs/copyright.html.
+#
+# You may opt to use, copy, modify, merge, publish, distribute and/or sell
+# copies of the Software, and permit persons to whom the Software is
+# furnished to do so, under the terms of the COPYING file.
+#
+# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# KIND, either express or implied.
+#
+# SPDX-License-Identifier: curl
+#
+###########################################################################
+# Find the BearSSL library
+#
+# Input variables:
+#
+# - `BEARSSL_INCLUDE_DIR`:   The BearSSL include directory.
+# - `BEARSSL_LIBRARY`:       Path to `bearssl` library.
+#
+# Result variables:
+#
+# - `BEARSSL_FOUND`:         System has BearSSL.
+# - `BEARSSL_INCLUDE_DIRS`:  The BearSSL include directories.
+# - `BEARSSL_LIBRARIES`:     The BearSSL library names.
+
+if(DEFINED BEARSSL_INCLUDE_DIRS AND NOT DEFINED BEARSSL_INCLUDE_DIR)
+  message(WARNING "BEARSSL_INCLUDE_DIRS is deprecated, use BEARSSL_INCLUDE_DIR instead.")
+  set(BEARSSL_INCLUDE_DIR "${BEARSSL_INCLUDE_DIRS}")
+  unset(BEARSSL_INCLUDE_DIRS)
+endif()
+
+find_path(BEARSSL_INCLUDE_DIR NAMES "bearssl.h")
+find_library(BEARSSL_LIBRARY NAMES "bearssl")
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(BearSSL
+  REQUIRED_VARS
+    BEARSSL_INCLUDE_DIR
+    BEARSSL_LIBRARY
+)
+
+if(BEARSSL_FOUND)
+  set(BEARSSL_INCLUDE_DIRS ${BEARSSL_INCLUDE_DIR})
+  set(BEARSSL_LIBRARIES    ${BEARSSL_LIBRARY})
+endif()
+
+mark_as_advanced(BEARSSL_INCLUDE_DIR BEARSSL_LIBRARY)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4772a6219a..f7928e8563 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -712,6 +712,7 @@ else()
   set(CURL_WINDOWS_SSPI OFF)
 endif()
 cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
+cmake_dependent_option(CURL_USE_BEARSSL "Enable BearSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
 cmake_dependent_option(CURL_USE_WOLFSSL "Enable wolfSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
 cmake_dependent_option(CURL_USE_GNUTLS "Enable GnuTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
 cmake_dependent_option(CURL_USE_RUSTLS "Enable Rustls for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
@@ -719,6 +720,7 @@ cmake_dependent_option(CURL_USE_RUSTLS "Enable Rustls for SSL/TLS" OFF CURL_ENAB
 if(WIN32 OR
    CURL_USE_SCHANNEL OR
    CURL_USE_MBEDTLS OR
+   CURL_USE_BEARSSL OR
    CURL_USE_WOLFSSL OR
    CURL_USE_GNUTLS OR
    CURL_USE_RUSTLS)
@@ -738,6 +740,7 @@ curl_count_true(_enabled_ssl_options_count
   CURL_USE_SCHANNEL
   CURL_USE_OPENSSL
   CURL_USE_MBEDTLS
+  CURL_USE_BEARSSL
   CURL_USE_WOLFSSL
   CURL_USE_GNUTLS
   CURL_USE_RUSTLS
@@ -882,6 +885,19 @@ if(CURL_USE_MBEDTLS)
   endif()
 endif()
 
+if(CURL_USE_BEARSSL)
+  find_package(BearSSL REQUIRED)
+  set(_ssl_enabled ON)
+  set(USE_BEARSSL ON)
+  list(APPEND CURL_LIBS ${BEARSSL_LIBRARIES})
+  include_directories(SYSTEM ${BEARSSL_INCLUDE_DIRS})
+
+  if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "bearssl")
+    set(_valid_default_ssl_backend TRUE)
+  endif()
+  set(_curl_ca_bundle_supported TRUE)
+endif()
+
 if(CURL_USE_WOLFSSL)
   find_package(WolfSSL REQUIRED)
   set(_ssl_enabled ON)
@@ -2097,6 +2113,7 @@ endmacro()
 if(NOT CURL_DISABLE_NTLM AND
    ((USE_OPENSSL AND HAVE_DES_ECB_ENCRYPT) OR
     (USE_MBEDTLS AND HAVE_MBEDTLS_DES_CRYPT_ECB) OR
+    USE_BEARSSL OR
     USE_GNUTLS OR
     USE_WIN32_CRYPTO OR
     (USE_WOLFSSL AND HAVE_WOLFSSL_DES_ECB_ENCRYPT)))
@@ -2178,7 +2195,7 @@ curl_add_if("HTTP2"         USE_NGHTTP2)
 curl_add_if("HTTP3"         USE_NGTCP2 OR USE_QUICHE OR USE_OPENSSL_QUIC)
 curl_add_if("MultiSSL"      CURL_WITH_MULTI_SSL)
 curl_add_if("HTTPS-proxy"   NOT CURL_DISABLE_PROXY AND _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS
-                            OR USE_SCHANNEL OR USE_RUSTLS OR USE_MBEDTLS OR
+                            OR USE_SCHANNEL OR USE_RUSTLS OR USE_BEARSSL OR USE_MBEDTLS OR
                             (USE_WOLFSSL AND HAVE_WOLFSSL_BIO_NEW)))
 curl_add_if("Unicode"       ENABLE_UNICODE)
 curl_add_if("threadsafe"    HAVE_ATOMIC OR
@@ -2209,6 +2226,7 @@ curl_add_if("Schannel"         _ssl_enabled AND USE_SCHANNEL)
 curl_add_if("${_openssl}"      _ssl_enabled AND USE_OPENSSL AND OPENSSL_VERSION VERSION_LESS 3.0.0)
 curl_add_if("${_openssl} v3+"  _ssl_enabled AND USE_OPENSSL AND OPENSSL_VERSION VERSION_GREATER_EQUAL 3.0.0)
 curl_add_if("mbedTLS"          _ssl_enabled AND USE_MBEDTLS)
+curl_add_if("BearSSL"          _ssl_enabled AND USE_BEARSSL)
 curl_add_if("wolfSSL"          _ssl_enabled AND USE_WOLFSSL)
 curl_add_if("GnuTLS"           _ssl_enabled AND USE_GNUTLS)
 curl_add_if("Rustls"           _ssl_enabled AND USE_RUSTLS)
diff --git a/Makefile.am b/Makefile.am
index 7b2f05f92e..52c75622e0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -32,6 +32,7 @@ CMAKE_DIST =                                    \
  CMake/curl-config.cmake.in                     \
  CMake/CurlSymbolHiding.cmake                   \
  CMake/CurlTests.c                              \
+ CMake/FindBearSSL.cmake                        \
  CMake/FindBrotli.cmake                         \
  CMake/FindCares.cmake                          \
  CMake/FindGnuTLS.cmake                         \
diff --git a/configure.ac b/configure.ac
index cc16f4120a..2b136cc190 100644
--- a/configure.ac
+++ b/configure.ac
@@ -141,7 +141,7 @@ AC_SUBST(VERSIONNUM)
 
 dnl
 dnl initialize all the info variables
-    curl_ssl_msg="no      (--with-{openssl,gnutls,mbedtls,wolfssl,schannel,amissl,rustls} )"
+    curl_ssl_msg="no      (--with-{openssl,gnutls,mbedtls,wolfssl,schannel,secure-transport,amissl,bearssl,rustls} )"
     curl_ssh_msg="no      (--with-{libssh,libssh2})"
    curl_zlib_msg="no      (--with-zlib)"
  curl_brotli_msg="no      (--with-brotli)"
@@ -210,6 +210,13 @@ AS_HELP_STRING([--with-schannel],[enable Windows native SSL/TLS]),
   OPT_SCHANNEL=$withval
   TLSCHOICE="Schannel")
 
+OPT_SECURETRANSPORT=no
+AC_ARG_WITH(secure-transport,dnl
+AS_HELP_STRING([--with-secure-transport],[enable Apple OS native SSL/TLS]),[
+  OPT_SECURETRANSPORT=$withval
+  TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }Secure-Transport"
+])
+
 OPT_AMISSL=no
 AC_ARG_WITH(amissl,dnl
 AS_HELP_STRING([--with-amissl],[enable Amiga native SSL/TLS (AmiSSL)]),[
@@ -267,6 +274,15 @@ AS_HELP_STRING([--with-wolfssl=PATH],[where to look for wolfSSL, PATH points to
   fi
 ])
 
+OPT_BEARSSL=no
+AC_ARG_WITH(bearssl,dnl
+AS_HELP_STRING([--with-bearssl=PATH],[where to look for BearSSL, PATH points to the installation root]),[
+  OPT_BEARSSL=$withval
+  if test X"$withval" != Xno; then
+    TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }BearSSL"
+  fi
+])
+
 OPT_RUSTLS=no
 AC_ARG_WITH(rustls,dnl
 AS_HELP_STRING([--with-rustls=PATH],[where to look for Rustls, PATH points to the installation root]),[
@@ -435,11 +451,13 @@ if test -z "$TLSCHOICE"; then
 Select from these:
 
   --with-amissl
+  --with-bearssl
   --with-gnutls
   --with-mbedtls
   --with-openssl (also works for BoringSSL and LibreSSL)
   --with-rustls
   --with-schannel
+  --with-secure-transport
   --with-wolfssl
 ])
   fi
@@ -1989,11 +2007,13 @@ case "$DEFAULT_SSL_BACKEND" in
 esac
 
 CURL_WITH_SCHANNEL
+CURL_WITH_SECURETRANSPORT
 CURL_WITH_AMISSL
 CURL_WITH_OPENSSL
 CURL_WITH_GNUTLS
 CURL_WITH_MBEDTLS
 CURL_WITH_WOLFSSL
+CURL_WITH_BEARSSL
 CURL_WITH_RUSTLS
 CURL_WITH_APPLE_SECTRUST
 
@@ -2010,10 +2030,10 @@ if test "x$curl_cv_native_windows" = "xyes" -a "$curl_cv_wince" = 'no'; then
   LIBS="-lbcrypt $LIBS"
 fi
 
-case "x$SSL_DISABLED$OPENSSL_ENABLED$GNUTLS_ENABLED$MBEDTLS_ENABLED$WOLFSSL_ENABLED$SCHANNEL_ENABLED$RUSTLS_ENABLED" in
+case "x$SSL_DISABLED$OPENSSL_ENABLED$GNUTLS_ENABLED$MBEDTLS_ENABLED$WOLFSSL_ENABLED$SCHANNEL_ENABLED$SECURETRANSPORT_ENABLED$BEARSSL_ENABLED$RUSTLS_ENABLED" in
   x)
     AC_MSG_ERROR([TLS not detected, you will not be able to use HTTPS, FTPS, NTLM and more.
-Use --with-openssl, --with-gnutls, --with-wolfssl, --with-mbedtls, --with-schannel, --with-amissl or --with-rustls to address this.])
+Use --with-openssl, --with-gnutls, --with-wolfssl, --with-mbedtls, --with-schannel, --with-secure-transport, --with-amissl, --with-bearssl or --with-rustls to address this.])
     ;;
   x1)
     # one SSL backend is enabled
@@ -2025,7 +2045,7 @@ Use --with-openssl, --with-gnutls, --with-wolfssl, --with-mbedtls, --with-schann
     ;;
   xD*)
     AC_MSG_ERROR([--without-ssl has been set together with an explicit option to use an ssl library
-(e.g. --with-openssl, --with-gnutls, --with-wolfssl, --with-mbedtls, --with-schannel, --with-amissl, --with-rustls).
+(e.g. --with-openssl, --with-gnutls, --with-wolfssl, --with-mbedtls, --with-schannel, --with-secure-transport, --with-amissl, --with-bearssl, --with-rustls).
 Since these are conflicting parameters, verify which is the desired one and drop the other.])
     ;;
   *)
@@ -2723,6 +2743,8 @@ AS_HELP_STRING([--disable-versioned-symbols], [Disable versioned symbols in shar
         versioned_symbols_flavour="OPENSSL_"
       elif test "x$MBEDTLS_ENABLED" = "x1"; then
         versioned_symbols_flavour="MBEDTLS_"
+      elif test "x$BEARSSL_ENABLED" = "x1"; then
+        versioned_symbols_flavour="BEARSSL_"
       elif test "x$WOLFSSL_ENABLED" = "x1"; then
         versioned_symbols_flavour="WOLFSSL_"
       elif test "x$GNUTLS_ENABLED" = "x1"; then
@@ -5254,6 +5276,7 @@ use_curl_ntlm_core=no
 if test "x$CURL_DISABLE_NTLM" != "x1"; then
   if test "x$HAVE_DES_ECB_ENCRYPT" = "x1" \
       -o "x$GNUTLS_ENABLED" = "x1" \
+      -o "x$SECURETRANSPORT_ENABLED" = "x1" \
       -o "x$USE_WIN32_CRYPTO" = "x1" \
       -o "x$HAVE_WOLFSSL_DES_ECB_ENCRYPT" = "x1" \
       -o "x$HAVE_MBEDTLS_DES_CRYPT_ECB" = "x1"; then
@@ -5292,7 +5315,9 @@ if test "x$CURL_DISABLE_HTTP" != "x1"; then
   if test "x$https_proxy" != "xno"; then
     if test "x$OPENSSL_ENABLED" = "x1" \
         -o "x$GNUTLS_ENABLED" = "x1" \
+        -o "x$SECURETRANSPORT_ENABLED" = "x1" \
         -o "x$RUSTLS_ENABLED" = "x1" \
+        -o "x$BEARSSL_ENABLED" = "x1" \
         -o "x$SCHANNEL_ENABLED" = "x1" \
         -o "x$GNUTLS_ENABLED" = "x1" \
         -o "x$MBEDTLS_ENABLED" = "x1"; then
@@ -5597,6 +5622,14 @@ AC_MSG_NOTICE([Configured to build curl/libcurl:
   Features:         ${SUPPORT_FEATURES}
 ])
 
+# grep -o would simplify this, but is nonportable
+[non13=`echo "$TLSCHOICE" | $AWK '{split("bearssl secure-transport", a); for (i in a) if(match(tolower($0), a[i])) print a[i];}'`]
+if test -n "$non13"; then
+  for a in $non13; do
+    AC_MSG_WARN([$a is enabled for TLS but it does not support TLS 1.3])
+  done
+fi
+
 if test -n "$experimental"; then
   for a in $experimental; do
     AC_MSG_WARN([$a is enabled but marked EXPERIMENTAL. Use with caution!])
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index d9857a50c2..7be484684a 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -266,6 +266,8 @@ if(BUILD_SHARED_LIBS)
         set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "OPENSSL_")
       elseif(CURL_USE_MBEDTLS)
         set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "MBEDTLS_")
+      elseif(CURL_USE_BEARSSL)
+        set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "BEARSSL_")
       elseif(CURL_USE_WOLFSSL)
         set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "WOLFSSL_")
       elseif(CURL_USE_GNUTLS)
diff --git a/lib/Makefile.inc b/lib/Makefile.inc
index 8b506febcf..fcdca6056d 100644
--- a/lib/Makefile.inc
+++ b/lib/Makefile.inc
@@ -79,6 +79,7 @@ LIB_VAUTH_HFILES =      \
   vauth/vauth.h
 
 LIB_VTLS_CFILES =           \
+  vtls/bearssl.c            \
   vtls/apple.c              \
   vtls/cipher_suite.c       \
   vtls/gtls.c               \
@@ -97,6 +98,7 @@ LIB_VTLS_CFILES =           \
   vtls/x509asn1.c
 
 LIB_VTLS_HFILES =           \
+  vtls/bearssl.h            \
   vtls/apple.h              \
   vtls/cipher_suite.h       \
   vtls/gtls.h               \
diff --git a/lib/curl_config.h.cmake b/lib/curl_config.h.cmake
index 88b991d7ef..68f3ff7c9e 100644
--- a/lib/curl_config.h.cmake
+++ b/lib/curl_config.h.cmake
@@ -676,6 +676,9 @@ ${SIZEOF_TIME_T_CODE}
 /* if mbedTLS <4 has the mbedtls_des_crypt_ecb function. */
 #cmakedefine HAVE_MBEDTLS_DES_CRYPT_ECB 1
 
+/* if BearSSL is enabled */
+#cmakedefine USE_BEARSSL 1
+
 /* if Rustls is enabled */
 #cmakedefine USE_RUSTLS 1
 
diff --git a/lib/curl_setup.h b/lib/curl_setup.h
index 7c033623c5..2d904d80d6 100644
--- a/lib/curl_setup.h
+++ b/lib/curl_setup.h
@@ -722,6 +722,7 @@
 
 #if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \
   defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \
+  defined(USE_BEARSSL) || \
   defined(USE_RUSTLS)
 #define USE_SSL    /* SSL support has been enabled */
 #endif
@@ -759,6 +760,7 @@
 #  if (defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT)) ||        \
   defined(USE_GNUTLS) ||                                                \
   (defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB)) ||      \
+  defined(USE_BEARSSL) ||                                               \
   defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) ||              \
   (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT))
 #    define USE_CURL_NTLM_CORE
diff --git a/lib/vtls/bearssl.c b/lib/vtls/bearssl.c
new file mode 100644
index 0000000000..4b65244451
--- /dev/null
+++ b/lib/vtls/bearssl.c
@@ -0,0 +1,1104 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Michael Forney, <mforney@mforney.org>
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "../curl_setup.h"
+
+#ifdef USE_BEARSSL
+
+#include <bearssl.h>
+
+#include "bearssl.h"
+#include "cipher_suite.h"
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../curlx/inet_pton.h"
+#include "vtls.h"
+#include "vtls_int.h"
+#include "vtls_scache.h"
+#include "../connect.h"
+#include "../select.h"
+#include "../multiif.h"
+#include "../curl_printf.h"
+
+/* The last #include files should be: */
+#include "../curl_memory.h"
+#include "../memdebug.h"
+
+struct x509_context {
+  const br_x509_class *vtable;
+  br_x509_minimal_context minimal;
+  br_x509_decoder_context decoder;
+  bool verifyhost;
+  bool verifypeer;
+  int cert_num;
+};
+
+struct bearssl_ssl_backend_data {
+  br_ssl_client_context ctx;
+  struct x509_context x509;
+  unsigned char buf[BR_SSL_BUFSIZE_BIDI];
+  br_x509_trust_anchor *anchors;
+  size_t anchors_len;
+  const char *protocols[ALPN_ENTRIES_MAX];
+  /* SSL client context is active */
+  bool active;
+  /* size of pending write, yet to be flushed */
+  size_t pending_write;
+  BIT(sent_shutdown);
+};
+
+struct cafile_parser {
+  CURLcode err;
+  bool in_cert;
+  br_x509_decoder_context xc;
+  /* array of trust anchors loaded from CAfile */
+  br_x509_trust_anchor *anchors;
+  size_t anchors_len;
+  /* buffer for DN data */
+  unsigned char dn[1024];
+  size_t dn_len;
+};
+
+#define CAFILE_SOURCE_PATH 1
+#define CAFILE_SOURCE_BLOB 2
+struct cafile_source {
+  int type;
+  const char *data;
+  size_t len;
+};
+
+static void append_dn(void *ctx, const void *buf, size_t len)
+{
+  struct cafile_parser *ca = ctx;
+
+  if(ca->err != CURLE_OK || !ca->in_cert)
+    return;
+  if(sizeof(ca->dn) - ca->dn_len < len) {
+    ca->err = CURLE_FAILED_INIT;
+    return;
+  }
+  memcpy(ca->dn + ca->dn_len, buf, len);
+  ca->dn_len += len;
+}
+
+static void x509_push(void *ctx, const void *buf, size_t len)
+{
+  struct cafile_parser *ca = ctx;
+
+  if(ca->in_cert)
+    br_x509_decoder_push(&ca->xc, buf, len);
+}
+
+static CURLcode load_cafile(struct cafile_source *source,
+                            br_x509_trust_anchor **anchors,
+                            size_t *anchors_len)
+{
+  struct cafile_parser ca;
+  br_pem_decoder_context pc;
+  br_x509_trust_anchor *ta;
+  size_t ta_size;
+  br_x509_trust_anchor *new_anchors;
+  size_t new_anchors_len;
+  br_x509_pkey *pkey;
+  FILE *fp = 0;
+  unsigned char buf[BUFSIZ];
+  const unsigned char *p = NULL;
+  const char *name;
+  size_t n = 0, i, pushed;
+
+  DEBUGASSERT(source->type == CAFILE_SOURCE_PATH
+              || source->type == CAFILE_SOURCE_BLOB);
+
+  if(source->type == CAFILE_SOURCE_PATH) {
+    fp = fopen(source->data, "rb");
+    if(!fp)
+      return CURLE_SSL_CACERT_BADFILE;
+  }
+
+  if(source->type == CAFILE_SOURCE_BLOB && source->len > (size_t)INT_MAX)
+    return CURLE_SSL_CACERT_BADFILE;
+
+  ca.err = CURLE_OK;
+  ca.in_cert = FALSE;
+  ca.anchors = NULL;
+  ca.anchors_len = 0;
+  br_pem_decoder_init(&pc);
+  br_pem_decoder_setdest(&pc, x509_push, &ca);
+  do {
+    if(source->type == CAFILE_SOURCE_PATH) {
+      n = fread(buf, 1, sizeof(buf), fp);
+      if(n == 0)
+        break;
+      p = buf;
+    }
+    else if(source->type == CAFILE_SOURCE_BLOB) {
+      n = source->len;
+      p = (const unsigned char *) source->data;
+    }
+    while(n) {
+      pushed = br_pem_decoder_push(&pc, p, n);
+      if(ca.err)
+        goto fail;
+      p += pushed;
+      n -= pushed;
+
+      switch(br_pem_decoder_event(&pc)) {
+      case 0:
+        break;
+      case BR_PEM_BEGIN_OBJ:
+        name = br_pem_decoder_name(&pc);
+        if(strcmp(name, "CERTIFICATE") && strcmp(name, "X509 CERTIFICATE"))
+          break;
+        br_x509_decoder_init(&ca.xc, append_dn, &ca);
+        ca.in_cert = TRUE;
+        ca.dn_len = 0;
+        break;
+      case BR_PEM_END_OBJ:
+        if(!ca.in_cert)
+          break;
+        ca.in_cert = FALSE;
+        if(br_x509_decoder_last_error(&ca.xc)) {
+          ca.err = CURLE_SSL_CACERT_BADFILE;
+          goto fail;
+        }
+        /* add trust anchor */
+        if(ca.anchors_len == SIZE_MAX / sizeof(ca.anchors[0])) {
+          ca.err = CURLE_OUT_OF_MEMORY;
+          goto fail;
+        }
+        new_anchors_len = ca.anchors_len + 1;
+        new_anchors = realloc(ca.anchors,
+                              new_anchors_len * sizeof(ca.anchors[0]));
+        if(!new_anchors) {
+          ca.err = CURLE_OUT_OF_MEMORY;
+          goto fail;
+        }
+        ca.anchors = new_anchors;
+        ca.anchors_len = new_anchors_len;
+        ta = &ca.anchors[ca.anchors_len - 1];
+        ta->dn.data = NULL;
+        ta->flags = 0;
+        if(br_x509_decoder_isCA(&ca.xc))
+          ta->flags |= BR_X509_TA_CA;
+        pkey = br_x509_decoder_get_pkey(&ca.xc);
+        if(!pkey) {
+          ca.err = CURLE_SSL_CACERT_BADFILE;
+          goto fail;
+        }
+        ta->pkey = *pkey;
+
+        /* calculate space needed for trust anchor data */
+        ta_size = ca.dn_len;
+        switch(pkey->key_type) {
+        case BR_KEYTYPE_RSA:
+          ta_size += pkey->key.rsa.nlen + pkey->key.rsa.elen;
+          break;
+        case BR_KEYTYPE_EC:
+          ta_size += pkey->key.ec.qlen;
+          break;
+        default:
+          ca.err = CURLE_FAILED_INIT;
+          goto fail;
+        }
+
+        /* fill in trust anchor DN and public key data */
+        ta->dn.data = malloc(ta_size);
+        if(!ta->dn.data) {
+          ca.err = CURLE_OUT_OF_MEMORY;
+          goto fail;
+        }
+        memcpy(ta->dn.data, ca.dn, ca.dn_len);
+        ta->dn.len = ca.dn_len;
+        switch(pkey->key_type) {
+        case BR_KEYTYPE_RSA:
+          ta->pkey.key.rsa.n = ta->dn.data + ta->dn.len;
+          memcpy(ta->pkey.key.rsa.n, pkey->key.rsa.n, pkey->key.rsa.nlen);
+          ta->pkey.key.rsa.e = ta->pkey.key.rsa.n + ta->pkey.key.rsa.nlen;
+          memcpy(ta->pkey.key.rsa.e, pkey->key.rsa.e, pkey->key.rsa.elen);
+          break;
+        case BR_KEYTYPE_EC:
+          ta->pkey.key.ec.q = ta->dn.data + ta->dn.len;
+          memcpy(ta->pkey.key.ec.q, pkey->key.ec.q, pkey->key.ec.qlen);
+          break;
+        }
+        break;
+      default:
+        ca.err = CURLE_SSL_CACERT_BADFILE;
+        goto fail;
+      }
+    }
+  } while(source->type != CAFILE_SOURCE_BLOB);
+  if(fp && ferror(fp))
+    ca.err = CURLE_READ_ERROR;
+  else if(ca.in_cert)
+    ca.err = CURLE_SSL_CACERT_BADFILE;
+
+fail:
+  if(fp)
+    fclose(fp);
+  if(ca.err == CURLE_OK) {
+    *anchors = ca.anchors;
+    *anchors_len = ca.anchors_len;
+  }
+  else {
+    for(i = 0; i < ca.anchors_len; ++i)
+      free(ca.anchors[i].dn.data);
+    free(ca.anchors);
+  }
+
+  return ca.err;
+}
+
+static void x509_start_chain(const br_x509_class **ctx,
+                             const char *server_name)
+{
+  struct x509_context *x509 = (struct x509_context *)ctx;
+
+  if(!x509->verifypeer) {
+    x509->cert_num = 0;
+    return;
+  }
+
+  if(!x509->verifyhost)
+    server_name = NULL;
+  x509->minimal.vtable->start_chain(&x509->minimal.vtable, server_name);
+}
+
+static void x509_start_cert(const br_x509_class **ctx, uint32_t length)
+{
+  struct x509_context *x509 = (struct x509_context *)ctx;
+
+  if(!x509->verifypeer) {
+    /* Only decode the first cert in the chain to obtain the public key */
+    if(x509->cert_num == 0)
+      br_x509_decoder_init(&x509->decoder, NULL, NULL);
+    return;
+  }
+
+  x509->minimal.vtable->start_cert(&x509->minimal.vtable, length);
+}
+
+static void x509_append(const br_x509_class **ctx, const unsigned char *buf,
+                        size_t len)
+{
+  struct x509_context *x509 = (struct x509_context *)ctx;
+
+  if(!x509->verifypeer) {
+    if(x509->cert_num == 0)
+      br_x509_decoder_push(&x509->decoder, buf, len);
+    return;
+  }
+
+  x509->minimal.vtable->append(&x509->minimal.vtable, buf, len);
+}
+
+static void x509_end_cert(const br_x509_class **ctx)
+{
+  struct x509_context *x509 = (struct x509_context *)ctx;
+
+  if(!x509->verifypeer) {
+    x509->cert_num++;
+    return;
+  }
+
+  x509->minimal.vtable->end_cert(&x509->minimal.vtable);
+}
+
+static unsigned x509_end_chain(const br_x509_class **ctx)
+{
+  struct x509_context *x509 = (struct x509_context *)ctx;
+
+  if(!x509->verifypeer) {
+    return (unsigned)br_x509_decoder_last_error(&x509->decoder);
+  }
+
+  return x509->minimal.vtable->end_chain(&x509->minimal.vtable);
+}
+
+static const br_x509_pkey *x509_get_pkey(const br_x509_class *const *ctx,
+                                         unsigned *usages)
+{
+  struct x509_context *x509 = (struct x509_context *)CURL_UNCONST(ctx);
+
+  if(!x509->verifypeer) {
+    /* Nothing in the chain is verified, just return the public key of the
+       first certificate and allow its usage for both TLS_RSA_* and
+       TLS_ECDHE_* */
+    if(usages)
+      *usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN;
+    return br_x509_decoder_get_pkey(&x509->decoder);
+  }
+
+  return x509->minimal.vtable->get_pkey(&x509->minimal.vtable, usages);
+}
+
+static const br_x509_class x509_vtable = {
+  sizeof(struct x509_context),
+  x509_start_chain,
+  x509_start_cert,
+  x509_append,
+  x509_end_cert,
+  x509_end_chain,
+  x509_get_pkey
+};
+
+static CURLcode
+bearssl_set_ssl_version_min_max(struct Curl_easy *data,
+                                br_ssl_engine_context *ssl_eng,
+                                struct ssl_primary_config *conn_config)
+{
+  unsigned version_min, version_max;
+
+  switch(conn_config->version) {
+  case CURL_SSLVERSION_DEFAULT:
+  case CURL_SSLVERSION_TLSv1:
+  case CURL_SSLVERSION_TLSv1_0:
+    version_min = BR_TLS10;
+    break;
+  case CURL_SSLVERSION_TLSv1_1:
+    version_min = BR_TLS11;
+    break;
+  case CURL_SSLVERSION_TLSv1_2:
+    version_min = BR_TLS12;
+    break;
+  case CURL_SSLVERSION_TLSv1_3:
+    failf(data, "BearSSL: does not support TLS 1.3");
+    return CURLE_SSL_CONNECT_ERROR;
+  default:
+    failf(data, "BearSSL: unsupported minimum TLS version value");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  switch(conn_config->version_max) {
+  case CURL_SSLVERSION_MAX_DEFAULT:
+  case CURL_SSLVERSION_MAX_NONE:
+  case CURL_SSLVERSION_MAX_TLSv1_3:
+  case CURL_SSLVERSION_MAX_TLSv1_2:
+    version_max = BR_TLS12;
+    break;
+  case CURL_SSLVERSION_MAX_TLSv1_1:
+    version_max = BR_TLS11;
+    break;
+  case CURL_SSLVERSION_MAX_TLSv1_0:
+    version_max = BR_TLS10;
+    break;
+  default:
+    failf(data, "BearSSL: unsupported maximum TLS version value");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  br_ssl_engine_set_versions(ssl_eng, version_min, version_max);
+
+  return CURLE_OK;
+}
+
+static const uint16_t ciphertable[] = {
+  /* RFC 2246 TLS 1.0 */
+  BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA,                        /* 0x000A */
+
+  /* RFC 3268 TLS 1.0 AES */
+  BR_TLS_RSA_WITH_AES_128_CBC_SHA,                         /* 0x002F */
+  BR_TLS_RSA_WITH_AES_256_CBC_SHA,                         /* 0x0035 */
+
+  /* RFC 5246 TLS 1.2 */
+  BR_TLS_RSA_WITH_AES_128_CBC_SHA256,                      /* 0x003C */
+  BR_TLS_RSA_WITH_AES_256_CBC_SHA256,                      /* 0x003D */
+
+  /* RFC 5288 TLS 1.2 AES GCM */
+  BR_TLS_RSA_WITH_AES_128_GCM_SHA256,                      /* 0x009C */
+  BR_TLS_RSA_WITH_AES_256_GCM_SHA384,                      /* 0x009D */
+
+  /* RFC 4492 TLS 1.0 ECC */
+  BR_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,                 /* 0xC003 */
+  BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,                  /* 0xC004 */
+  BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,                  /* 0xC005 */
+  BR_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,                /* 0xC008 */
+  BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,                 /* 0xC009 */
+  BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,                 /* 0xC00A */
+  BR_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,                   /* 0xC00D */
+  BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,                    /* 0xC00E */
+  BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,                    /* 0xC00F */
+  BR_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,                  /* 0xC012 */
+  BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,                   /* 0xC013 */
+  BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,                   /* 0xC014 */
+
+  /* RFC 5289 TLS 1.2 ECC HMAC SHA256/384 */
+  BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,              /* 0xC023 */
+  BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,              /* 0xC024 */
+  BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,               /* 0xC025 */
+  BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,               /* 0xC026 */
+  BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,                /* 0xC027 */
+  BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,                /* 0xC028 */
+  BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,                 /* 0xC029 */
+  BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,                 /* 0xC02A */
+
+  /* RFC 5289 TLS 1.2 GCM */
+  BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,              /* 0xC02B */
+  BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,              /* 0xC02C */
+  BR_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,               /* 0xC02D */
+  BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,               /* 0xC02E */
+  BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,                /* 0xC02F */
+  BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,                /* 0xC030 */
+  BR_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,                 /* 0xC031 */
+  BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,                 /* 0xC032 */
+
+#ifdef BR_TLS_RSA_WITH_AES_128_CCM
+  /* RFC 6655 TLS 1.2 CCM
+     Supported since BearSSL 0.6 */
+  BR_TLS_RSA_WITH_AES_128_CCM,                             /* 0xC09C */
+  BR_TLS_RSA_WITH_AES_256_CCM,                             /* 0xC09D */
+  BR_TLS_RSA_WITH_AES_128_CCM_8,                           /* 0xC0A0 */
+  BR_TLS_RSA_WITH_AES_256_CCM_8,                           /* 0xC0A1 */
+
+  /* RFC 7251 TLS 1.2 ECC CCM
+     Supported since BearSSL 0.6 */
+  BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,                     /* 0xC0AC */
+  BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,                     /* 0xC0AD */
+  BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,                   /* 0xC0AE */
+  BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,                   /* 0xC0AF */
+#endif
+
+  /* RFC 7905 TLS 1.2 ChaCha20-Poly1305
+     Supported since BearSSL 0.2 */
+  BR_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,          /* 0xCCA8 */
+  BR_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,        /* 0xCCA9 */
+};
+
+#define NUM_OF_CIPHERS CURL_ARRAYSIZE(ciphertable)
+
+static CURLcode bearssl_set_selected_ciphers(struct Curl_easy *data,
+                                             br_ssl_engine_context *ssl_eng,
+                                             const char *ciphers)
+{
+  uint16_t selected[NUM_OF_CIPHERS];
+  size_t count = 0, i;
+  const char *ptr, *end;
+
+  for(ptr = ciphers; ptr[0] != '\0' && count < NUM_OF_CIPHERS; ptr = end) {
+    uint16_t id = Curl_cipher_suite_walk_str(&ptr, &end);
+
+    /* Check if cipher is supported */
+    if(id) {
+      for(i = 0; i < NUM_OF_CIPHERS && ciphertable[i] != id; i++);
+      if(i == NUM_OF_CIPHERS)
+        id = 0;
+    }
+    if(!id) {
+      if(ptr[0] != '\0')
+        infof(data, "BearSSL: unknown cipher in list: \"%.*s\"",
+              (int) (end - ptr), ptr);
+      continue;
+    }
+
+    /* No duplicates allowed */
+    for(i = 0; i < count && selected[i] != id; i++);
+    if(i < count) {
+      infof(data, "BearSSL: duplicate cipher in list: \"%.*s\"",
+            (int) (end - ptr), ptr);
+      continue;
+    }
+
+    selected[count++] = id;
+  }
+
+  if(count == 0) {
+    failf(data, "BearSSL: no supported cipher in list");
+    return CURLE_SSL_CIPHER;
+  }
+
+  br_ssl_engine_set_suites(ssl_eng, selected, count);
+  return CURLE_OK;
+}
+
+static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf,
+                                      struct Curl_easy *data)
+{
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
+  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+  const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
+  const char * const ssl_cafile =
+    /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
+    (ca_info_blob ? NULL : conn_config->CAfile);
+  const char *hostname = connssl->peer.hostname;
+  const bool verifypeer = conn_config->verifypeer;
+  const bool verifyhost = conn_config->verifyhost;
+  CURLcode ret;
+  int session_set = 0;
+
+  DEBUGASSERT(backend);
+  CURL_TRC_CF(data, cf, "connect_step1");
+
+  if(verifypeer) {
+    if(ca_info_blob) {
+      struct cafile_source source;
+      source.type = CAFILE_SOURCE_BLOB;
+      source.data = ca_info_blob->data;
+      source.len = ca_info_blob->len;
+
+      CURL_TRC_CF(data, cf, "connect_step1, load ca_info_blob");
+      ret = load_cafile(&source, &backend->anchors, &backend->anchors_len);
+      if(ret != CURLE_OK) {
+        failf(data, "error importing CA certificate blob");
+        return ret;
+      }
+    }
+
+    if(ssl_cafile) {
+      struct cafile_source source;
+      source.type = CAFILE_SOURCE_PATH;
+      source.data = ssl_cafile;
+      source.len = 0;
+
+      CURL_TRC_CF(data, cf, "connect_step1, load cafile");
+      ret = load_cafile(&source, &backend->anchors, &backend->anchors_len);
+      if(ret != CURLE_OK) {
+        failf(data, "error setting certificate verify locations."
+              " CAfile: %s", ssl_cafile);
+        return ret;
+      }
+    }
+  }
+
+  /* initialize SSL context */
+  br_ssl_client_init_full(&backend->ctx, &backend->x509.minimal,
+                          backend->anchors, backend->anchors_len);
+
+  ret = bearssl_set_ssl_version_min_max(data, &backend->ctx.eng, conn_config);
+  if(ret != CURLE_OK)
+    return ret;
+
+  br_ssl_engine_set_buffer(&backend->ctx.eng, backend->buf,
+                           sizeof(backend->buf), 1);
+
+  if(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() */
+    CURL_TRC_CF(data, cf, "connect_step1, set ciphers");
+    ret = bearssl_set_selected_ciphers(data, &backend->ctx.eng,
+                                       conn_config->cipher_list);
+    if(ret)
+      return ret;
+  }
+
+  /* initialize X.509 context */
+  backend->x509.vtable = &x509_vtable;
+  backend->x509.verifypeer = verifypeer;
+  backend->x509.verifyhost = verifyhost;
+  br_ssl_engine_set_x509(&backend->ctx.eng, &backend->x509.vtable);
+
+  if(ssl_config->primary.cache_session) {
+    struct Curl_ssl_session *sc_session = NULL;
+
+    ret = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key,
+                               &sc_session);
+    if(!ret && sc_session && sc_session->sdata && sc_session->sdata_len) {
+      const br_ssl_session_parameters *session;
+      session = (const br_ssl_session_parameters *)sc_session->sdata;
+      br_ssl_engine_set_session_parameters(&backend->ctx.eng, session);
+      session_set = 1;
+      infof(data, "BearSSL: reusing session ID");
+      /* single use of sessions */
+      Curl_ssl_scache_return(cf, data, connssl->peer.scache_key, sc_session);
+    }
+  }
+
+  if(connssl->alpn) {
+    struct alpn_proto_buf proto;
+    size_t i;
+
+    for(i = 0; i < connssl->alpn->count; ++i) {
+      backend->protocols[i] = connssl->alpn->entries[i];
+    }
+    br_ssl_engine_set_protocol_names(&backend->ctx.eng, backend->protocols,
+                                     connssl->alpn->count);
+    Curl_alpn_to_proto_str(&proto, connssl->alpn);
+    infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
+  }
+
+  if(connssl->peer.type != CURL_SSL_PEER_DNS) {
+    if(verifyhost) {
+      failf(data, "BearSSL: "
+            "host verification of IP address is not supported");
+      return CURLE_PEER_FAILED_VERIFICATION;
+    }
+    hostname = NULL;
+  }
+  else {
+    if(!connssl->peer.sni) {
+      failf(data, "Failed to set SNI");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+    hostname = connssl->peer.sni;
+    CURL_TRC_CF(data, cf, "connect_step1, SNI set");
+  }
+
+  /* give application a chance to interfere with SSL set up. */
+  if(data->set.ssl.fsslctx) {
+    Curl_set_in_callback(data, TRUE);
+    ret = (*data->set.ssl.fsslctx)(data, &backend->ctx,
+                                   data->set.ssl.fsslctxp);
+    Curl_set_in_callback(data, FALSE);
+    if(ret) {
+      failf(data, "BearSSL: error signaled by ssl ctx callback");
+      return ret;
+    }
+  }
+
+  if(!br_ssl_client_reset(&backend->ctx, hostname, session_set))
+    return CURLE_FAILED_INIT;
+  backend->active = TRUE;
+
+  connssl->connecting_state = ssl_connect_2;
+
+  return CURLE_OK;
+}
+
+static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
+                                  struct Curl_easy *data,
+                                  unsigned target)
+{
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
+  unsigned state;
+  unsigned char *buf;
+  size_t len;
+  ssize_t ret;
+  CURLcode result;
+  int err;
+
+  DEBUGASSERT(backend);
+
+  connssl->io_need = CURL_SSL_IO_NEED_NONE;
+  for(;;) {
+    state = br_ssl_engine_current_state(&backend->ctx.eng);
+    if(state & BR_SSL_CLOSED) {
+      err = br_ssl_engine_last_error(&backend->ctx.eng);
+      switch(err) {
+      case BR_ERR_OK:
+        /* TLS close notify */
+        if(connssl->state != ssl_connection_complete) {
+          failf(data, "SSL: connection closed during handshake");
+          return CURLE_SSL_CONNECT_ERROR;
+        }
+        return CURLE_OK;
+      case BR_ERR_X509_EXPIRED:
+        failf(data, "SSL: X.509 verification: "
+              "certificate is expired or not yet valid");
+        return CURLE_PEER_FAILED_VERIFICATION;
+      case BR_ERR_X509_BAD_SERVER_NAME:
+        failf(data, "SSL: X.509 verification: "
+              "expected server name was not found in the chain");
+        return CURLE_PEER_FAILED_VERIFICATION;
+      case BR_ERR_X509_NOT_TRUSTED:
+        failf(data, "SSL: X.509 verification: "
+              "chain could not be linked to a trust anchor");
+        return CURLE_PEER_FAILED_VERIFICATION;
+      default:;
+      }
+      failf(data, "BearSSL: connection error 0x%04x", err);
+      /* X.509 errors are documented to have the range 32..63 */
+      if(err >= 32 && err < 64)
+        return CURLE_PEER_FAILED_VERIFICATION;
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+    if(state & target)
+      return CURLE_OK;
+    if(state & BR_SSL_SENDREC) {
+      buf = br_ssl_engine_sendrec_buf(&backend->ctx.eng, &len);
+      ret = Curl_conn_cf_send(cf->next, data, (const char *)buf, len, FALSE,
+                              &result);
+      CURL_TRC_CF(data, cf, "ssl_send(len=%zu) -> %zd, %d", len, ret, result);
+      if(ret <= 0) {
+        if(result == CURLE_AGAIN)
+          connssl->io_need |= CURL_SSL_IO_NEED_SEND;
+        return result;
+      }
+      br_ssl_engine_sendrec_ack(&backend->ctx.eng, ret);
+    }
+    else if(state & BR_SSL_RECVREC) {
+      buf = br_ssl_engine_recvrec_buf(&backend->ctx.eng, &len);
+      ret = Curl_conn_cf_recv(cf->next, data, (char *)buf, len, &result);
+      CURL_TRC_CF(data, cf, "ssl_recv(len=%zu) -> %zd, %d", len, ret, result);
+      if(ret == 0) {
+        failf(data, "SSL: EOF without close notify");
+        return CURLE_RECV_ERROR;
+      }
+      if(ret <= 0) {
+        if(result == CURLE_AGAIN)
+          connssl->io_need |= CURL_SSL_IO_NEED_RECV;
+        return result;
+      }
+      br_ssl_engine_recvrec_ack(&backend->ctx.eng, ret);
+    }
+  }
+}
+
+static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf,
+                                      struct Curl_easy *data)
+{
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
+  br_ssl_session_parameters session;
+  char cipher_str[64];
+  CURLcode ret;
+
+  DEBUGASSERT(backend);
+  CURL_TRC_CF(data, cf, "connect_step2");
+
+  ret = bearssl_run_until(cf, data, BR_SSL_SENDAPP | BR_SSL_RECVAPP);
+  if(ret == CURLE_AGAIN)
+    return CURLE_OK;
+  if(ret == CURLE_OK) {
+    unsigned int tver;
+    int subver = 0;
+
+    if(br_ssl_engine_current_state(&backend->ctx.eng) == BR_SSL_CLOSED) {
+      failf(data, "SSL: connection closed during handshake");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+    connssl->connecting_state = ssl_connect_3;
+    /* Informational message */
+    tver = br_ssl_engine_get_version(&backend->ctx.eng);
+    switch(tver) {
+    case BR_TLS12:
+      subver = 2; /* 1.2 */
+      break;
+    case BR_TLS11:
+      subver = 1; /* 1.1 */
+      break;
+    case BR_TLS10: /* 1.0 */
+    default: /* unknown, leave it at zero */
+      break;
+    }
+    br_ssl_engine_get_session_parameters(&backend->ctx.eng, &session);
+    Curl_cipher_suite_get_str(session.cipher_suite, cipher_str,
+                              sizeof(cipher_str), TRUE);
+    infof(data, "BearSSL: TLS v1.%d connection using %s", subver,
+          cipher_str);
+  }
+  return ret;
+}
+
+static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf,
+                                      struct Curl_easy *data)
+{
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
+  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+  CURLcode ret;
+
+  DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+  DEBUGASSERT(backend);
+  CURL_TRC_CF(data, cf, "connect_step3");
+
+  if(connssl->alpn) {
+    const char *proto;
+
+    proto = br_ssl_engine_get_selected_protocol(&backend->ctx.eng);
+    Curl_alpn_set_negotiated(cf, data, connssl, (const unsigned char *)proto,
+                             proto ? strlen(proto) : 0);
+  }
+
+  if(ssl_config->primary.cache_session) {
+    struct Curl_ssl_session *sc_session;
+    br_ssl_session_parameters *session;
+
+    session = malloc(sizeof(*session));
+    if(!session)
+      return CURLE_OUT_OF_MEMORY;
+    br_ssl_engine_get_session_parameters(&backend->ctx.eng, session);
+    ret = Curl_ssl_session_create((unsigned char *)session, sizeof(*session),
+                                  (int)session->version,
+                                  connssl->negotiated.alpn,
+                                  0, 0, &sc_session);
+    if(!ret) {
+      ret = Curl_ssl_scache_put(cf, data, connssl->peer.scache_key,
+                                sc_session);
+      /* took ownership of `sc_session` */
+    }
+    if(ret)
+      return ret;
+  }
+
+  connssl->connecting_state = ssl_connect_done;
+
+  return CURLE_OK;
+}
+
+static ssize_t bearssl_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+                            const void *buf, size_t len, CURLcode *err)
+{
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
+  unsigned char *app;
+  size_t applen;
+
+  DEBUGASSERT(backend);
+
+  for(;;) {
+    *err = bearssl_run_until(cf, data, BR_SSL_SENDAPP);
+    if(*err)
+      return -1;
+    app = br_ssl_engine_sendapp_buf(&backend->ctx.eng, &applen);
+    if(!app) {
+      failf(data, "SSL: connection closed during write");
+      *err = CURLE_SEND_ERROR;
+      return -1;
+    }
+    if(backend->pending_write) {
+      applen = backend->pending_write;
+      backend->pending_write = 0;
+      return applen;
+    }
+    if(applen > len)
+      applen = len;
+    memcpy(app, buf, applen);
+    br_ssl_engine_sendapp_ack(&backend->ctx.eng, applen);
+    br_ssl_engine_flush(&backend->ctx.eng, 0);
+    backend->pending_write = applen;
+  }
+}
+
+static ssize_t bearssl_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+                            char *buf, size_t len, CURLcode *err)
+{
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
+  unsigned char *app;
+  size_t applen;
+
+  DEBUGASSERT(backend);
+
+  *err = bearssl_run_until(cf, data, BR_SSL_RECVAPP);
+  if(*err != CURLE_OK)
+    return -1;
+  app = br_ssl_engine_recvapp_buf(&backend->ctx.eng, &applen);
+  if(!app)
+    return 0;
+  if(applen > len)
+    applen = len;
+  memcpy(buf, app, applen);
+  br_ssl_engine_recvapp_ack(&backend->ctx.eng, applen);
+
+  return applen;
+}
+
+static CURLcode bearssl_connect(struct Curl_cfilter *cf,
+                                struct Curl_easy *data,
+                                bool *done)
+{
+  CURLcode ret;
+  struct ssl_connect_data *connssl = cf->ctx;
+
+  CURL_TRC_CF(data, cf, "connect()");
+  /* check if the connection has already been established */
+  if(ssl_connection_complete == connssl->state) {
+    CURL_TRC_CF(data, cf, "connect_common, connected");
+    *done = TRUE;
+    return CURLE_OK;
+  }
+
+  *done = FALSE;
+  connssl->io_need = CURL_SSL_IO_NEED_NONE;
+
+  if(ssl_connect_1 == connssl->connecting_state) {
+    ret = bearssl_connect_step1(cf, data);
+    if(ret)
+      return ret;
+  }
+
+  if(ssl_connect_2 == connssl->connecting_state) {
+    ret = bearssl_connect_step2(cf, data);
+    if(ret)
+      return ret;
+  }
+
+  if(ssl_connect_3 == connssl->connecting_state) {
+    ret = bearssl_connect_step3(cf, data);
+    if(ret)
+      return ret;
+  }
+
+  if(ssl_connect_done == connssl->connecting_state) {
+    connssl->state = ssl_connection_complete;
+    *done = TRUE;
+  }
+
+  return CURLE_OK;
+}
+
+static size_t bearssl_version(char *buffer, size_t size)
+{
+  return msnprintf(buffer, size, "BearSSL");
+}
+
+static bool bearssl_data_pending(struct Curl_cfilter *cf,
+                                 const struct Curl_easy *data)
+{
+  struct ssl_connect_data *ctx = cf->ctx;
+  struct bearssl_ssl_backend_data *backend;
+
+  (void)data;
+  DEBUGASSERT(ctx && ctx->backend);
+  backend = (struct bearssl_ssl_backend_data *)ctx->backend;
+  return br_ssl_engine_current_state(&backend->ctx.eng) & BR_SSL_RECVAPP;
+}
+
+static CURLcode bearssl_random(struct Curl_easy *data UNUSED_PARAM,
+                               unsigned char *entropy, size_t length)
+{
+  static br_hmac_drbg_context ctx;
+  static bool seeded = FALSE;
+
+  if(!seeded) {
+    br_prng_seeder seeder;
+
+    br_hmac_drbg_init(&ctx, &br_sha256_vtable, NULL, 0);
+    seeder = br_prng_seeder_system(NULL);
+    if(!seeder || !seeder(&ctx.vtable))
+      return CURLE_FAILED_INIT;
+    seeded = TRUE;
+  }
+  br_hmac_drbg_generate(&ctx, entropy, length);
+
+  return CURLE_OK;
+}
+
+static void *bearssl_get_internals(struct ssl_connect_data *connssl,
+                                   CURLINFO info UNUSED_PARAM)
+{
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
+  DEBUGASSERT(backend);
+  return &backend->ctx;
+}
+
+static CURLcode bearssl_shutdown(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 bool send_shutdown, bool *done)
+{
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
+  CURLcode result;
+
+  DEBUGASSERT(backend);
+  if(!backend->active || cf->shutdown) {
+    *done = TRUE;
+    return CURLE_OK;
+  }
+
+  *done = FALSE;
+  if(!backend->sent_shutdown) {
+    (void)send_shutdown; /* unknown how to suppress our close notify */
+    br_ssl_engine_close(&backend->ctx.eng);
+    backend->sent_shutdown = TRUE;
+  }
+
+  result = bearssl_run_until(cf, data, BR_SSL_CLOSED);
+  if(result == CURLE_OK) {
+    *done = TRUE;
+  }
+  else if(result == CURLE_AGAIN) {
+    CURL_TRC_CF(data, cf, "shutdown EAGAIN, io_need=%x", connssl->io_need);
+    result = CURLE_OK;
+  }
+  else
+    CURL_TRC_CF(data, cf, "shutdown error: %d", result);
+
+  cf->shutdown = (result || *done);
+  return result;
+}
+
+static void bearssl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
+  size_t i;
+
+  (void)data;
+  DEBUGASSERT(backend);
+
+  backend->active = FALSE;
+  if(backend->anchors) {
+    for(i = 0; i < backend->anchors_len; ++i)
+      free(backend->anchors[i].dn.data);
+    Curl_safefree(backend->anchors);
+  }
+}
+
+static CURLcode bearssl_sha256sum(const unsigned char *input,
+                                  size_t inputlen,
+                                  unsigned char *sha256sum,
+                                  size_t sha256len UNUSED_PARAM)
+{
+  br_sha256_context ctx;
+
+  br_sha256_init(&ctx);
+  br_sha256_update(&ctx, input, inputlen);
+  br_sha256_out(&ctx, sha256sum);
+  return CURLE_OK;
+}
+
+const struct Curl_ssl Curl_ssl_bearssl = {
+  { CURLSSLBACKEND_BEARSSL, "bearssl" }, /* info */
+
+  SSLSUPP_CAINFO_BLOB |
+  SSLSUPP_SSL_CTX |
+  SSLSUPP_HTTPS_PROXY |
+  SSLSUPP_CIPHER_LIST,
+
+  sizeof(struct bearssl_ssl_backend_data),
+
+  NULL,                            /* init */
+  NULL,                            /* cleanup */
+  bearssl_version,                 /* version */
+  bearssl_shutdown,                /* shutdown */
+  bearssl_data_pending,            /* data_pending */
+  bearssl_random,                  /* random */
+  NULL,                            /* cert_status_request */
+  bearssl_connect,                 /* connect */
+  Curl_ssl_adjust_pollset,         /* adjust_pollset */
+  bearssl_get_internals,           /* get_internals */
+  bearssl_close,                   /* close_one */
+  NULL,                            /* close_all */
+  NULL,                            /* set_engine */
+  NULL,                            /* set_engine_default */
+  NULL,                            /* engines_list */
+  NULL,                            /* false_start */
+  bearssl_sha256sum,               /* sha256sum */
+  bearssl_recv,                    /* recv decrypted data */
+  bearssl_send,                    /* send data to encrypt */
+  NULL,                            /* get_channel_binding */
+};
+
+#endif /* USE_BEARSSL */
diff --git a/lib/vtls/bearssl.h b/lib/vtls/bearssl.h
new file mode 100644
index 0000000000..8bb254f7da
--- /dev/null
+++ b/lib/vtls/bearssl.h
@@ -0,0 +1,34 @@
+#ifndef HEADER_CURL_BEARSSL_H
+#define HEADER_CURL_BEARSSL_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Michael Forney, <mforney@mforney.org>
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "../curl_setup.h"
+
+#ifdef USE_BEARSSL
+
+extern const struct Curl_ssl Curl_ssl_bearssl;
+
+#endif /* USE_BEARSSL */
+#endif /* HEADER_CURL_BEARSSL_H */
diff --git a/lib/vtls/cipher_suite.c b/lib/vtls/cipher_suite.c
index 23fb3a3c5d..47de782a49 100644
--- a/lib/vtls/cipher_suite.c
+++ b/lib/vtls/cipher_suite.c
@@ -23,7 +23,7 @@
  ***************************************************************************/
 #include "../curl_setup.h"
 
-#if defined(USE_MBEDTLS) || defined(USE_RUSTLS)
+#if defined(USE_MBEDTLS) || defined(USE_BEARSSL) || defined(USE_RUSTLS)
 #include "cipher_suite.h"
 #include <string.h>
 
@@ -79,7 +79,7 @@ static const char *cs_txt =
   "SHA" "\0"
   "SHA256" "\0"
   "SHA384" "\0"
-#ifdef USE_MBEDTLS
+#if defined(USE_MBEDTLS) || defined(USE_BEARSSL)
   "ARIA" "\0"
   "ARIA128" "\0"
   "ARIA256" "\0"
@@ -120,7 +120,7 @@ enum {
   CS_TXT_IDX_SHA,
   CS_TXT_IDX_SHA256,
   CS_TXT_IDX_SHA384,
-#ifdef USE_MBEDTLS
+#if defined(USE_MBEDTLS) || defined(USE_BEARSSL)
   CS_TXT_IDX_ARIA,
   CS_TXT_IDX_ARIA128,
   CS_TXT_IDX_ARIA256,
@@ -159,7 +159,7 @@ struct cs_entry {
 /* !checksrc! disable COMMANOSPACE all */
 static const struct cs_entry cs_list [] = {
   /* TLS 1.3 ciphers */
-#if defined(USE_MBEDTLS) || defined(USE_RUSTLS)
+#if defined(USE_MBEDTLS) || defined(USE_BEARSSL) || defined(USE_RUSTLS)
   CS_ENTRY(0x1301, TLS,AES,128,GCM,SHA256,,,),
   CS_ENTRY(0x1302, TLS,AES,256,GCM,SHA384,,,),
   CS_ENTRY(0x1303, TLS,CHACHA20,POLY1305,SHA256,,,,),
@@ -321,7 +321,19 @@ static const struct cs_entry cs_list [] = {
   CS_ENTRY(0xCCAB, TLS,PSK,WITH,CHACHA20,POLY1305,SHA256,,),
   CS_ENTRY(0xCCAB, PSK,CHACHA20,POLY1305,,,,,),
 #endif
-#ifdef USE_MBEDTLS
+#ifdef USE_BEARSSL
+  CS_ENTRY(0x000A, TLS,RSA,WITH,3DES,EDE,CBC,SHA,),
+  CS_ENTRY(0x000A, DES,CBC3,SHA,,,,,),
+  CS_ENTRY(0xC003, TLS,ECDH,ECDSA,WITH,3DES,EDE,CBC,SHA),
+  CS_ENTRY(0xC003, ECDH,ECDSA,DES,CBC3,SHA,,,),
+  CS_ENTRY(0xC008, TLS,ECDHE,ECDSA,WITH,3DES,EDE,CBC,SHA),
+  CS_ENTRY(0xC008, ECDHE,ECDSA,DES,CBC3,SHA,,,),
+  CS_ENTRY(0xC00D, TLS,ECDH,RSA,WITH,3DES,EDE,CBC,SHA),
+  CS_ENTRY(0xC00D, ECDH,RSA,DES,CBC3,SHA,,,),
+  CS_ENTRY(0xC012, TLS,ECDHE,RSA,WITH,3DES,EDE,CBC,SHA),
+  CS_ENTRY(0xC012, ECDHE,RSA,DES,CBC3,SHA,,,),
+#endif
+#if defined(USE_MBEDTLS) || defined(USE_BEARSSL)
   CS_ENTRY(0xC09C, TLS,RSA,WITH,AES,128,CCM,,),
   CS_ENTRY(0xC09C, AES128,CCM,,,,,,),
   CS_ENTRY(0xC09D, TLS,RSA,WITH,AES,256,CCM,,),
@@ -707,4 +719,4 @@ int Curl_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size,
   return r;
 }
 
-#endif /* defined(USE_MBEDTLS) || defined(USE_RUSTLS) */
+#endif /* defined(USE_MBEDTLS) || defined(USE_BEARSSL) || defined(USE_RUSTLS) */
diff --git a/lib/vtls/cipher_suite.h b/lib/vtls/cipher_suite.h
index a1dcbc524f..cd556db10f 100644
--- a/lib/vtls/cipher_suite.h
+++ b/lib/vtls/cipher_suite.h
@@ -26,7 +26,8 @@
 
 #include "../curl_setup.h"
 
-#if defined(USE_MBEDTLS) || defined(USE_RUSTLS)
+#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \
+    defined(USE_BEARSSL) || defined(USE_RUSTLS)
 #include <stdint.h>
 
 /* Lookup IANA id for cipher suite string, returns 0 if not recognized */
@@ -42,5 +43,6 @@ uint16_t Curl_cipher_suite_walk_str(const char **str, const char **end);
 int Curl_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size,
                               bool prefer_rfc);
 
-#endif /* defined(USE_MBEDTLS) || defined(USE_RUSTLS) */
+#endif /* defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \
+          defined(USE_BEARSSL) || defined(USE_RUSTLS) */
 #endif /* HEADER_CURL_CIPHER_SUITE_H */
diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c
index df0449cbee..943a2e1420 100644
--- a/lib/vtls/vtls.c
+++ b/lib/vtls/vtls.c
@@ -56,6 +56,7 @@
 #include "wolfssl.h"        /* wolfSSL versions */
 #include "schannel.h"       /* Schannel SSPI version */
 #include "mbedtls.h"        /* mbedTLS versions */
+#include "bearssl.h"        /* BearSSL versions */
 #include "rustls.h"         /* Rustls versions */
 
 #include "../slist.h"
@@ -1027,6 +1028,8 @@ const struct Curl_ssl *Curl_ssl =
   &Curl_ssl_rustls;
 #elif defined(USE_OPENSSL)
   &Curl_ssl_openssl;
+#elif defined(USE_BEARSSL)
+  &Curl_ssl_bearssl;
 #elif defined(USE_SCHANNEL)
   &Curl_ssl_schannel;
 #else
@@ -1049,6 +1052,9 @@ static const struct Curl_ssl *available_backends[] = {
 #ifdef USE_SCHANNEL
   &Curl_ssl_schannel,
 #endif
+#ifdef USE_BEARSSL
+  &Curl_ssl_bearssl,
+#endif
 #ifdef USE_RUSTLS
   &Curl_ssl_rustls,
 #endif
diff --git a/m4/curl-bearssl.m4 b/m4/curl-bearssl.m4
new file mode 100644
index 0000000000..c7df036890
--- /dev/null
+++ b/m4/curl-bearssl.m4
@@ -0,0 +1,111 @@
+#***************************************************************************
+#                                  _   _ ____  _
+#  Project                     ___| | | |  _ \| |
+#                             / __| | | | |_) | |
+#                            | (__| |_| |  _ <| |___
+#                             \___|\___/|_| \_\_____|
+#
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at https://curl.se/docs/copyright.html.
+#
+# You may opt to use, copy, modify, merge, publish, distribute and/or sell
+# copies of the Software, and permit persons to whom the Software is
+# furnished to do so, under the terms of the COPYING file.
+#
+# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# KIND, either express or implied.
+#
+# SPDX-License-Identifier: curl
+#
+#***************************************************************************
+
+AC_DEFUN([CURL_WITH_BEARSSL], [
+dnl ----------------------------------------------------
+dnl check for BearSSL
+dnl ----------------------------------------------------
+
+if test "x$OPT_BEARSSL" != xno; then
+  _cppflags=$CPPFLAGS
+  _ldflags=$LDFLAGS
+  _ldflagspc=$LDFLAGSPC
+  ssl_msg=
+
+  if test X"$OPT_BEARSSL" != Xno; then
+
+    if test "$OPT_BEARSSL" = "yes"; then
+      OPT_BEARSSL=""
+    fi
+
+    if test -z "$OPT_BEARSSL" ; then
+      dnl check for lib first without setting any new path
+
+      AC_CHECK_LIB(bearssl, br_ssl_client_init_full,
+      dnl libbearssl found, set the variable
+      [
+        AC_DEFINE(USE_BEARSSL, 1, [if BearSSL is enabled])
+        BEARSSL_ENABLED=1
+        USE_BEARSSL="yes"
+        ssl_msg="BearSSL"
+        test bearssl != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes
+      ], [], -lbearssl)
+    fi
+
+    addld=""
+    addlib=""
+    addcflags=""
+    bearssllib=""
+
+    if test "x$USE_BEARSSL" != "xyes"; then
+      dnl add the path and test again
+      addld=-L$OPT_BEARSSL/lib$libsuff
+      addcflags=-I$OPT_BEARSSL/include
+      bearssllib=$OPT_BEARSSL/lib$libsuff
+
+      LDFLAGS="$LDFLAGS $addld"
+      LDFLAGSPC="$LDFLAGSPC $addld"
+      if test "$addcflags" != "-I/usr/include"; then
+        CPPFLAGS="$CPPFLAGS $addcflags"
+      fi
+
+      AC_CHECK_LIB(bearssl, br_ssl_client_init_full,
+        [
+        AC_DEFINE(USE_BEARSSL, 1, [if BearSSL is enabled])
+        BEARSSL_ENABLED=1
+        USE_BEARSSL="yes"
+        ssl_msg="BearSSL"
+        test bearssl != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes
+        ],
+        [
+          CPPFLAGS=$_cppflags
+          LDFLAGS=$_ldflags
+          LDFLAGSPC=$_ldflagspc
+        ], -lbearssl)
+    fi
+
+    if test "x$USE_BEARSSL" = "xyes"; then
+      AC_MSG_NOTICE([detected BearSSL])
+      check_for_ca_bundle=1
+
+      LIBS="-lbearssl $LIBS"
+
+      if test -n "$bearssllib"; then
+        dnl when shared libs were found in a path that the run-time
+        dnl linker doesn't search through, we need to add it to
+        dnl CURL_LIBRARY_PATH to prevent further configure tests to fail
+        dnl due to this
+        if test "x$cross_compiling" != "xyes"; then
+          CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$bearssllib"
+          export CURL_LIBRARY_PATH
+          AC_MSG_NOTICE([Added $bearssllib to CURL_LIBRARY_PATH])
+        fi
+      fi
+    fi
+
+  fi dnl BearSSL not disabled
+
+  test -z "$ssl_msg" || ssl_backends="${ssl_backends:+$ssl_backends, }$ssl_msg"
+fi
+])
-- 
2.53.0


From cad2c9d2cc6db2ff4ab9046d997310cdf67268ae Mon Sep 17 00:00:00 2001
From: shrub <maybeshrub@gmail.com>
Date: Tue, 17 Mar 2026 20:37:54 +0000
Subject: [PATCH 2/2] bearssl: adapt vtls backend to 8.17 cfilter API

---
 CMakeLists.txt     |  1 -
 lib/curl_setup.h   |  1 -
 lib/vtls/bearssl.c | 73 ++++++++++++++++++++++++++--------------------
 3 files changed, 41 insertions(+), 34 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index f7928e8563..131ffac426 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2113,7 +2113,6 @@ endmacro()
 if(NOT CURL_DISABLE_NTLM AND
    ((USE_OPENSSL AND HAVE_DES_ECB_ENCRYPT) OR
     (USE_MBEDTLS AND HAVE_MBEDTLS_DES_CRYPT_ECB) OR
-    USE_BEARSSL OR
     USE_GNUTLS OR
     USE_WIN32_CRYPTO OR
     (USE_WOLFSSL AND HAVE_WOLFSSL_DES_ECB_ENCRYPT)))
diff --git a/lib/curl_setup.h b/lib/curl_setup.h
index 2d904d80d6..89a35b759e 100644
--- a/lib/curl_setup.h
+++ b/lib/curl_setup.h
@@ -760,7 +760,6 @@
 #  if (defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT)) ||        \
   defined(USE_GNUTLS) ||                                                \
   (defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB)) ||      \
-  defined(USE_BEARSSL) ||                                               \
   defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) ||              \
   (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT))
 #    define USE_CURL_NTLM_CORE
diff --git a/lib/vtls/bearssl.c b/lib/vtls/bearssl.c
index 4b65244451..9669706737 100644
--- a/lib/vtls/bearssl.c
+++ b/lib/vtls/bearssl.c
@@ -686,7 +686,7 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
   unsigned state;
   unsigned char *buf;
   size_t len;
-  ssize_t ret;
+  size_t ntransferred;
   CURLcode result;
   int err;
 
@@ -729,30 +729,33 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
       return CURLE_OK;
     if(state & BR_SSL_SENDREC) {
       buf = br_ssl_engine_sendrec_buf(&backend->ctx.eng, &len);
-      ret = Curl_conn_cf_send(cf->next, data, (const char *)buf, len, FALSE,
-                              &result);
-      CURL_TRC_CF(data, cf, "ssl_send(len=%zu) -> %zd, %d", len, ret, result);
-      if(ret <= 0) {
+      result = Curl_conn_cf_send(cf->next, data, (const char *)buf, len, FALSE,
+                                 &ntransferred);
+      CURL_TRC_CF(data, cf, "ssl_send(len=%zu) -> %d, %zu",
+                  len, result, ntransferred);
+      if(result) {
         if(result == CURLE_AGAIN)
           connssl->io_need |= CURL_SSL_IO_NEED_SEND;
         return result;
       }
-      br_ssl_engine_sendrec_ack(&backend->ctx.eng, ret);
+      br_ssl_engine_sendrec_ack(&backend->ctx.eng, ntransferred);
     }
     else if(state & BR_SSL_RECVREC) {
       buf = br_ssl_engine_recvrec_buf(&backend->ctx.eng, &len);
-      ret = Curl_conn_cf_recv(cf->next, data, (char *)buf, len, &result);
-      CURL_TRC_CF(data, cf, "ssl_recv(len=%zu) -> %zd, %d", len, ret, result);
-      if(ret == 0) {
+      result = Curl_conn_cf_recv(cf->next, data, (char *)buf, len,
+                                 &ntransferred);
+      CURL_TRC_CF(data, cf, "ssl_recv(len=%zu) -> %d, %zu",
+                  len, result, ntransferred);
+      if(!result && !ntransferred) {
         failf(data, "SSL: EOF without close notify");
         return CURLE_RECV_ERROR;
       }
-      if(ret <= 0) {
+      if(result) {
         if(result == CURLE_AGAIN)
           connssl->io_need |= CURL_SSL_IO_NEED_RECV;
         return result;
       }
-      br_ssl_engine_recvrec_ack(&backend->ctx.eng, ret);
+      br_ssl_engine_recvrec_ack(&backend->ctx.eng, ntransferred);
     }
   }
 }
@@ -851,31 +854,32 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf,
   return CURLE_OK;
 }
 
-static ssize_t bearssl_send(struct Curl_cfilter *cf, struct Curl_easy *data,
-                            const void *buf, size_t len, CURLcode *err)
+static CURLcode bearssl_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+                             const void *buf, size_t len, size_t *pnwritten)
 {
   struct ssl_connect_data *connssl = cf->ctx;
   struct bearssl_ssl_backend_data *backend =
     (struct bearssl_ssl_backend_data *)connssl->backend;
   unsigned char *app;
   size_t applen;
+  CURLcode result;
 
   DEBUGASSERT(backend);
+  *pnwritten = 0;
 
   for(;;) {
-    *err = bearssl_run_until(cf, data, BR_SSL_SENDAPP);
-    if(*err)
-      return -1;
+    result = bearssl_run_until(cf, data, BR_SSL_SENDAPP);
+    if(result)
+      return result;
     app = br_ssl_engine_sendapp_buf(&backend->ctx.eng, &applen);
     if(!app) {
       failf(data, "SSL: connection closed during write");
-      *err = CURLE_SEND_ERROR;
-      return -1;
+      return CURLE_SEND_ERROR;
     }
     if(backend->pending_write) {
-      applen = backend->pending_write;
+      *pnwritten = backend->pending_write;
       backend->pending_write = 0;
-      return applen;
+      return CURLE_OK;
     }
     if(applen > len)
       applen = len;
@@ -886,29 +890,32 @@ static ssize_t bearssl_send(struct Curl_cfilter *cf, struct Curl_easy *data,
   }
 }
 
-static ssize_t bearssl_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
-                            char *buf, size_t len, CURLcode *err)
+static CURLcode bearssl_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+                             char *buf, size_t len, size_t *pnread)
 {
   struct ssl_connect_data *connssl = cf->ctx;
   struct bearssl_ssl_backend_data *backend =
     (struct bearssl_ssl_backend_data *)connssl->backend;
   unsigned char *app;
   size_t applen;
+  CURLcode result;
 
   DEBUGASSERT(backend);
+  *pnread = 0;
 
-  *err = bearssl_run_until(cf, data, BR_SSL_RECVAPP);
-  if(*err != CURLE_OK)
-    return -1;
+  result = bearssl_run_until(cf, data, BR_SSL_RECVAPP);
+  if(result != CURLE_OK)
+    return result;
   app = br_ssl_engine_recvapp_buf(&backend->ctx.eng, &applen);
   if(!app)
-    return 0;
+    return CURLE_OK;
   if(applen > len)
     applen = len;
   memcpy(buf, app, applen);
   br_ssl_engine_recvapp_ack(&backend->ctx.eng, applen);
+  *pnread = applen;
 
-  return applen;
+  return CURLE_OK;
 }
 
 static CURLcode bearssl_connect(struct Curl_cfilter *cf,
@@ -957,7 +964,7 @@ static CURLcode bearssl_connect(struct Curl_cfilter *cf,
 
 static size_t bearssl_version(char *buffer, size_t size)
 {
-  return msnprintf(buffer, size, "BearSSL");
+  return curl_msnprintf(buffer, size, "BearSSL");
 }
 
 static bool bearssl_data_pending(struct Curl_cfilter *cf,
@@ -972,11 +979,12 @@ static bool bearssl_data_pending(struct Curl_cfilter *cf,
   return br_ssl_engine_current_state(&backend->ctx.eng) & BR_SSL_RECVAPP;
 }
 
-static CURLcode bearssl_random(struct Curl_easy *data UNUSED_PARAM,
+static CURLcode bearssl_random(struct Curl_easy *data,
                                unsigned char *entropy, size_t length)
 {
   static br_hmac_drbg_context ctx;
   static bool seeded = FALSE;
+  (void)data;
 
   if(!seeded) {
     br_prng_seeder seeder;
@@ -993,10 +1001,11 @@ static CURLcode bearssl_random(struct Curl_easy *data UNUSED_PARAM,
 }
 
 static void *bearssl_get_internals(struct ssl_connect_data *connssl,
-                                   CURLINFO info UNUSED_PARAM)
+                                   CURLINFO info)
 {
   struct bearssl_ssl_backend_data *backend =
     (struct bearssl_ssl_backend_data *)connssl->backend;
+  (void)info;
   DEBUGASSERT(backend);
   return &backend->ctx;
 }
@@ -1059,9 +1068,10 @@ static void bearssl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 static CURLcode bearssl_sha256sum(const unsigned char *input,
                                   size_t inputlen,
                                   unsigned char *sha256sum,
-                                  size_t sha256len UNUSED_PARAM)
+                                  size_t sha256len)
 {
   br_sha256_context ctx;
+  (void)sha256len;
 
   br_sha256_init(&ctx);
   br_sha256_update(&ctx, input, inputlen);
@@ -1094,7 +1104,6 @@ const struct Curl_ssl Curl_ssl_bearssl = {
   NULL,                            /* set_engine */
   NULL,                            /* set_engine_default */
   NULL,                            /* engines_list */
-  NULL,                            /* false_start */
   bearssl_sha256sum,               /* sha256sum */
   bearssl_recv,                    /* recv decrypted data */
   bearssl_send,                    /* send data to encrypt */
-- 
2.53.0