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
