From 3b2f18f9262d44470d128b4e3366e67544715d80 Mon Sep 17 00:00:00 2001
From: l <link2xt@testrun.org>
Date: Thu, 7 Nov 2024 19:07:11 +0000
Subject: [PATCH] feat: use Rustls for connections with strict TLS (#6186)

---
 .../letsencrypt/isrgrootx1.der                | Bin 1391 -> 0 bytes
 src/net.rs                                    |   4 +-
 src/net/tls.rs                                |  50 ++++++++----------
 3 files changed, 24 insertions(+), 30 deletions(-)
 delete mode 100644 assets/root-certificates/letsencrypt/isrgrootx1.der

diff --git a/assets/root-certificates/letsencrypt/isrgrootx1.der b/assets/root-certificates/letsencrypt/isrgrootx1.der
deleted file mode 100644
index 9d2132e7f1e352fabac7eafb231488b5da91ffb2..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1391
zcmXqLV$C*aVh&!w%*4pVB*@StaDKxjhsTjF$q#lXH+3@@@Un4gwRyCC=VfH%W@Rw&
zH{>?pWMd9xVH0Kw4K~y?PzQ0igcUsVN>YpRQcDzqQ<F=JGD|8If>Mi96N{2F6x@sQ
zOA8D|4TM2TnT2^ggM-`^g7WiA6e0`_<ivRmO%2QpObiVTOpGm}#CeU8xzx9?iAf3B
zQ;e((%uP)E3<gb1Tue<&j0|gEs1$z@G5<V!o_4r~O#8k&+wWUU=*hEr7QUe3d+DJ?
z|GsABePi&~xP339Eyrc@wvEYuMTD~V%U^nBI9svqqOr{`kFR$t?{D7mU+AOaEboI|
zZH1X$X=yqAbv6b2*J>)UeSUJ_S;M+V-u>HW)=goaf7yL{%}fvF;1?F_{JHX*^)7mb
z_cWAjyQP1@qPLp4KvBB%lYz~z{&jb6C9i%h=6|S9(7WzD_ly5q%k{o&s`h%|Bc#ex
z(95j3;9;=J8{wPpB=-w!_Uf_kT$~tqZ%sS<lrPDJZ}cAJN6%<{*coF|nN#-OdO}j=
zv)fB%>8l;RAn=gy-c5l%vESRjulRoaDHHpQelw1#&mWmj<25Ut_nWV1qwMTG%s)L@
zZ#3Rz-J*5P@#PxEvZ-ABH|}5ED<p5KuOXguX~w}7oGImb?&iDBt%;1wm|I_Tt@9|G
zqo!S?-Ceb>DklY(M=kbokat@+bL(=ez`Qo=d9_8$g;*;h-`WLMh;lRc_g>Iv-DFqo
zCF5PpD)i^rs|NwXHO`YuHlHea-Y3t<alzn9bfMW6_FV@J3}QUCH(AeER-4eZXt8F~
znO%FES)>;=GdnK4#`;nE(6$dNYTB&bR(NQ2+$oz?wqHJLsjX!HYm3h*_fBZ@a%uek
ze*2NA(-ox)>ah<i|4BSA=veFb>}I#svAgPldH?sMd^L9VXJTe#U|j5E;9$T9Os}&1
zjEw(TSb({M&43@o7Y6ZJ4VZzHfh<UXk420{q;gB-v+8Y6pD-3TaCDrIium?&b{=vn
z17;myDq~~_m5*4tXVZ#+3p^WdNM$OhYjUhfERE^P`_c3?Q~&C>Fz=l^iUlGsD^9O_
z?o;@C)1`#9mMgeli7SS+ehlD?e0}ag<jY+rMc=p0?Qd!L_T=Tn33tS2CrP`0NSk`8
zCjZbY>-X~KPhVT7{&D4o6YKug*3J5*#Pa(8&H7gpwUsuC^Ywq~GKr43@rUtb$j%*V
zXSzC!JAHIpY?|)Bn-<QxOK11@BioPrSvT!7JfT!vJn7=0h9#Dk0>;WsJ~s2)HigcR
z-KW{sqcnToqipMNtEK|qJDkTmPjj*R=DdjQJNf?H>f^h&YWulf^SYpR=4sI>j;y6q
zAB!&hzU1vmo%p4{|F6+t(%W~vdiUeP>Iq_(+2h=TYs}f5dM+QCHs|Whty&MJN;P<_
z^RZ+<cgB55&{XYRJASXdWE@=kRMt25>cWl3o${YKsGG(t*4WP8`@Gk9!gJ;MzXRq}
z=D1zmBD#56Ufpb-X;wRebnUN2Km5&csO6u^ip8C`)?_`D(Av1dIWhXO{2lAwvQN4%
zdQ0z%8|T;t|E@mm82|syq6>)@52x)|6WeWmz4WT_ftiBq<~klMDs9=v<meQiuHrG}
z;%xPO?Dji%_&1gWKCIgQcCPZHeGjf`un5~2GS9nPmD7KWUE)~%J@-C)jd?6==a+_<
hl<$4hIs2u!^Zn@C@&Eed!WW%&m|K^mbnBjkIsk}=Sg-&9

diff --git a/src/net.rs b/src/net.rs
index 1ad85a3ea5..350967ddef 100644
--- a/src/net.rs
+++ b/src/net.rs
@@ -5,13 +5,13 @@ use std::pin::Pin;
 use std::time::Duration;
 
 use anyhow::{format_err, Context as _, Result};
-use async_native_tls::TlsStream;
 use tokio::net::TcpStream;
 use tokio::task::JoinSet;
 use tokio::time::timeout;
 use tokio_io_timeout::TimeoutStream;
 
 use crate::context::Context;
+use crate::net::session::SessionStream;
 use crate::sql::Sql;
 use crate::tools::time;
 
@@ -128,7 +128,7 @@ pub(crate) async fn connect_tls_inner(
     host: &str,
     strict_tls: bool,
     alpn: &[&str],
-) -> Result<TlsStream<Pin<Box<TimeoutStream<TcpStream>>>>> {
+) -> Result<impl SessionStream> {
     let tcp_stream = connect_tcp_inner(addr).await?;
     let tls_stream = wrap_tls(strict_tls, host, alpn, tcp_stream).await?;
     Ok(tls_stream)
diff --git a/src/net/tls.rs b/src/net/tls.rs
index f30ed5cfd8..183ad75319 100644
--- a/src/net/tls.rs
+++ b/src/net/tls.rs
@@ -2,45 +2,39 @@
 use std::sync::Arc;
 
 use anyhow::Result;
-use async_native_tls::{Certificate, Protocol, TlsConnector, TlsStream};
-use once_cell::sync::Lazy;
-use tokio::io::{AsyncRead, AsyncWrite};
 
-// this certificate is missing on older android devices (eg. lg with android6 from 2017)
-// certificate downloaded from https://letsencrypt.org/certificates/
-static LETSENCRYPT_ROOT: Lazy<Certificate> = Lazy::new(|| {
-    Certificate::from_der(include_bytes!(
-        "../../assets/root-certificates/letsencrypt/isrgrootx1.der"
-    ))
-    .unwrap()
-});
+use crate::net::session::SessionStream;
 
-pub async fn wrap_tls<T: AsyncRead + AsyncWrite + Unpin>(
+pub async fn wrap_tls(
     strict_tls: bool,
     hostname: &str,
     alpn: &[&str],
-    stream: T,
-) -> Result<TlsStream<T>> {
-    let tls_builder = TlsConnector::new()
-        .min_protocol_version(Some(Protocol::Tlsv12))
-        .request_alpns(alpn)
-        .add_root_certificate(LETSENCRYPT_ROOT.clone());
-    let tls = if strict_tls {
-        tls_builder
+    stream: impl SessionStream + 'static,
+) -> Result<impl SessionStream> {
+    if strict_tls {
+        let tls_stream = wrap_rustls(hostname, alpn, stream).await?;
+        let boxed_stream: Box<dyn SessionStream> = Box::new(tls_stream);
+        Ok(boxed_stream)
     } else {
-        tls_builder
+        // We use native_tls because it accepts 1024-bit RSA keys.
+        // Rustls does not support them even if
+        // certificate checks are disabled: <https://github.com/rustls/rustls/issues/234>.
+        let tls = async_native_tls::TlsConnector::new()
+            .min_protocol_version(Some(async_native_tls::Protocol::Tlsv12))
+            .request_alpns(alpn)
             .danger_accept_invalid_hostnames(true)
-            .danger_accept_invalid_certs(true)
-    };
-    let tls_stream = tls.connect(hostname, stream).await?;
-    Ok(tls_stream)
+            .danger_accept_invalid_certs(true);
+        let tls_stream = tls.connect(hostname, stream).await?;
+        let boxed_stream: Box<dyn SessionStream> = Box::new(tls_stream);
+        Ok(boxed_stream)
+    }
 }
 
-pub async fn wrap_rustls<T: AsyncRead + AsyncWrite + Unpin>(
+pub async fn wrap_rustls(
     hostname: &str,
     alpn: &[&str],
-    stream: T,
-) -> Result<tokio_rustls::client::TlsStream<T>> {
+    stream: impl SessionStream,
+) -> Result<impl SessionStream> {
     let mut root_cert_store = rustls::RootCertStore::empty();
     root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());