1
1
//! TLS support.
2
+ use std:: collections:: HashMap ;
2
3
use std:: sync:: Arc ;
3
4
4
5
use anyhow:: Result ;
6
+ use once_cell:: sync:: Lazy ;
7
+ use parking_lot:: Mutex ;
5
8
6
9
use crate :: net:: session:: SessionStream ;
7
10
11
+ use tokio_rustls:: rustls:: client:: ClientSessionStore ;
12
+
8
13
pub async fn wrap_tls (
9
14
strict_tls : bool ,
10
15
hostname : & str ,
16
+ port : u16 ,
11
17
alpn : & str ,
12
18
stream : impl SessionStream + ' static ,
13
19
) -> Result < impl SessionStream > {
14
20
if strict_tls {
15
- let tls_stream = wrap_rustls ( hostname, alpn, stream) . await ?;
21
+ let tls_stream = wrap_rustls ( hostname, port , alpn, stream) . await ?;
16
22
let boxed_stream: Box < dyn SessionStream > = Box :: new ( tls_stream) ;
17
23
Ok ( boxed_stream)
18
24
} else {
@@ -35,8 +41,20 @@ pub async fn wrap_tls(
35
41
}
36
42
}
37
43
44
+ type SessionMap = HashMap < ( u16 , String ) , Arc < dyn ClientSessionStore > > ;
45
+
46
+ /// Map to store TLS session tickets.
47
+ ///
48
+ /// Tickets are separated by port and ALPN
49
+ /// to avoid trying to use Postfix ticket for Dovecot and vice versa.
50
+ /// Doing so would not be a security issue,
51
+ /// but wastes the ticket and the opportunity to resume TLS session unnecessarily.
52
+ /// Rustls takes care of separating tickets that belong to different domain names.
53
+ static RESUMPTION_STORE : Lazy < Mutex < SessionMap > > = Lazy :: new ( Default :: default) ;
54
+
38
55
pub async fn wrap_rustls (
39
56
hostname : & str ,
57
+ port : u16 ,
40
58
alpn : & str ,
41
59
stream : impl SessionStream ,
42
60
) -> Result < impl SessionStream > {
@@ -52,6 +70,31 @@ pub async fn wrap_rustls(
52
70
vec ! [ alpn. as_bytes( ) . to_vec( ) ]
53
71
} ;
54
72
73
+ // Enable TLS 1.3 session resumption
74
+ // as defined in <https://www.rfc-editor.org/rfc/rfc8446#section-2.2>.
75
+ //
76
+ // Obsolete TLS 1.2 mechanisms defined in RFC 5246
77
+ // and RFC 5077 have worse security
78
+ // and are not worth increasing
79
+ // attack surface: <https://words.filippo.io/we-need-to-talk-about-session-tickets/>.
80
+ let resumption_store = Arc :: clone (
81
+ RESUMPTION_STORE
82
+ . lock ( )
83
+ . entry ( ( port, alpn. to_string ( ) ) )
84
+ . or_insert_with ( || {
85
+ // This is the default as of Rustls version 0.23.16,
86
+ // but we want to create multiple caches
87
+ // to separate them by port and ALPN.
88
+ Arc :: new ( tokio_rustls:: rustls:: client:: ClientSessionMemoryCache :: new (
89
+ 256 ,
90
+ ) )
91
+ } ) ,
92
+ ) ;
93
+
94
+ let resumption = tokio_rustls:: rustls:: client:: Resumption :: store ( resumption_store)
95
+ . tls12_resumption ( tokio_rustls:: rustls:: client:: Tls12Resumption :: Disabled ) ;
96
+ config. resumption = resumption;
97
+
55
98
let tls = tokio_rustls:: TlsConnector :: from ( Arc :: new ( config) ) ;
56
99
let name = rustls_pki_types:: ServerName :: try_from ( hostname) ?. to_owned ( ) ;
57
100
let tls_stream = tls. connect ( name, stream) . await ?;
0 commit comments