1- use std:: io;
2- use std:: os:: fd:: AsFd ;
1+ use std:: os:: fd:: { AsFd , AsRawFd } ;
32use std:: os:: unix:: prelude:: RawFd ;
3+ use std:: { io, mem} ;
44
55use ktls_sys:: bindings as ktls;
66use nix:: sys:: socket:: { setsockopt, sockopt} ;
7+ use rustls:: crypto:: cipher:: NONCE_LEN ;
78use rustls:: internal:: msgs:: enums:: AlertLevel ;
89use rustls:: internal:: msgs:: message:: Message ;
9- use rustls:: { AlertDescription , ConnectionTrafficSecrets , SupportedCipherSuite } ;
10+ use rustls:: { AlertDescription , ConnectionTrafficSecrets , ExtractedSecrets , SupportedCipherSuite } ;
1011
1112pub ( crate ) const TLS_1_2_VERSION_NUMBER : u16 = ( ( ( ktls:: TLS_1_2_VERSION_MAJOR & 0xFF ) as u16 ) << 8 )
1213 | ( ( ktls:: TLS_1_2_VERSION_MINOR & 0xFF ) as u16 ) ;
@@ -17,12 +18,6 @@ pub(crate) const TLS_1_3_VERSION_NUMBER: u16 = (((ktls::TLS_1_3_VERSION_MAJOR &
1718/// `setsockopt` level constant: TLS
1819const SOL_TLS : libc:: c_int = 282 ;
1920
20- /// `setsockopt` SOL_TLS level constant: transmit (write)
21- const TLS_TX : libc:: c_int = 1 ;
22-
23- /// `setsockopt` SOL_TLS level constant: receive (read)
24- const TLX_RX : libc:: c_int = 2 ;
25-
2621/// Sets the TLS Upper Layer Protocol (ULP).
2722///
2823/// This should be called before performing any I/O operations on the
@@ -62,82 +57,131 @@ impl From<SetupUlpError> for io::Error {
6257 }
6358}
6459
65- #[ derive( Clone , Copy , Debug ) ]
66- pub enum Direction {
67- // Transmit
68- Tx ,
69- // Receive
70- Rx ,
71- }
60+ /// Sets the kTLS parameters on the socket after the TLS handshake is completed.
61+ ///
62+ /// ## Errors
63+ ///
64+ /// * Invalid crypto materials.
65+ /// * Syscall error.
66+ pub ( crate ) fn setup_tls_params < S : AsFd > (
67+ socket : & S ,
68+ cipher_suite : SupportedCipherSuite ,
69+ secrets : ExtractedSecrets ,
70+ ) -> io:: Result < ( ) > {
71+ TlsCryptoInfo :: extract ( cipher_suite, secrets. tx ) ?. set_tx ( socket) ?;
72+ TlsCryptoInfo :: extract ( cipher_suite, secrets. rx ) ?. set_rx ( socket) ?;
7273
73- impl From < Direction > for libc:: c_int {
74- fn from ( val : Direction ) -> Self {
75- match val {
76- Direction :: Tx => TLS_TX ,
77- Direction :: Rx => TLX_RX ,
78- }
79- }
74+ Ok ( ( ) )
8075}
8176
82- #[ allow( dead_code) ]
83- pub enum CryptoInfo {
84- AesGcm128 ( ktls:: tls12_crypto_info_aes_gcm_128 ) ,
85- AesGcm256 ( ktls:: tls12_crypto_info_aes_gcm_256 ) ,
86- AesCcm128 ( ktls:: tls12_crypto_info_aes_ccm_128 ) ,
87- Chacha20Poly1305 ( ktls:: tls12_crypto_info_chacha20_poly1305 ) ,
88- Sm4Gcm ( ktls:: tls12_crypto_info_sm4_gcm ) ,
89- Sm4Ccm ( ktls:: tls12_crypto_info_sm4_ccm ) ,
77+ #[ repr( C ) ]
78+ #[ allow( unused) ]
79+ /// A wrapper around the `libc::tls12_crypto_info_*` structs, use with setting
80+ /// up the kTLS r/w parameters on the TCP socket.
81+ ///
82+ /// This is originated from the `nix` crate, which currently does not support
83+ /// `AES-128-CCM` or `SM4-*`, so we implement our own version here.
84+ pub ( crate ) enum TlsCryptoInfo {
85+ AesGcm128 ( libc:: tls12_crypto_info_aes_gcm_128 ) ,
86+ AesGcm256 ( libc:: tls12_crypto_info_aes_gcm_256 ) ,
87+ AesCcm128 ( libc:: tls12_crypto_info_aes_ccm_128 ) ,
88+ Chacha20Poly1305 ( libc:: tls12_crypto_info_chacha20_poly1305 ) ,
89+ Sm4Gcm ( libc:: tls12_crypto_info_sm4_gcm ) ,
90+ Sm4Ccm ( libc:: tls12_crypto_info_sm4_ccm ) ,
9091}
9192
92- impl CryptoInfo {
93- /// Return the system struct as a pointer.
94- pub fn as_ptr ( & self ) -> * const libc:: c_void {
95- match self {
96- CryptoInfo :: AesGcm128 ( info) => info as * const _ as * const libc:: c_void ,
97- CryptoInfo :: AesGcm256 ( info) => info as * const _ as * const libc:: c_void ,
98- CryptoInfo :: AesCcm128 ( info) => info as * const _ as * const libc:: c_void ,
99- CryptoInfo :: Chacha20Poly1305 ( info) => info as * const _ as * const libc:: c_void ,
100- CryptoInfo :: Sm4Gcm ( info) => info as * const _ as * const libc:: c_void ,
101- CryptoInfo :: Sm4Ccm ( info) => info as * const _ as * const libc:: c_void ,
102- }
93+ impl TlsCryptoInfo {
94+ /// Sets the kTLS parameters on the given file descriptor, assuming that the
95+ /// [`TlsCryptoInfo`] is *extract* from the sequence number and
96+ /// secrets for the "tx" (transmit) direction.
97+ pub ( crate ) fn set_tx < S : AsFd > ( self , socket : & S ) -> io:: Result < ( ) > {
98+ self . set ( socket, libc:: TLS_TX )
10399 }
104100
105- /// Return the system struct size.
106- pub fn size ( & self ) -> usize {
107- match self {
108- CryptoInfo :: AesGcm128 ( _) => std:: mem:: size_of :: < ktls:: tls12_crypto_info_aes_gcm_128 > ( ) ,
109- CryptoInfo :: AesGcm256 ( _) => std:: mem:: size_of :: < ktls:: tls12_crypto_info_aes_gcm_256 > ( ) ,
110- CryptoInfo :: AesCcm128 ( _) => std:: mem:: size_of :: < ktls:: tls12_crypto_info_aes_ccm_128 > ( ) ,
111- CryptoInfo :: Chacha20Poly1305 ( _) => {
112- std:: mem:: size_of :: < ktls:: tls12_crypto_info_chacha20_poly1305 > ( )
113- }
114- CryptoInfo :: Sm4Gcm ( _) => std:: mem:: size_of :: < ktls:: tls12_crypto_info_sm4_gcm > ( ) ,
115- CryptoInfo :: Sm4Ccm ( _) => std:: mem:: size_of :: < ktls:: tls12_crypto_info_sm4_ccm > ( ) ,
101+ /// Sets the kTLS parameters on the given file descriptor, assuming that the
102+ /// [`TlsCryptoInfo`] is *extract* from the sequence number and
103+ /// secrets for the "rx" (receive) direction.
104+ pub ( crate ) fn set_rx < S : AsFd > ( self , socket : & S ) -> io:: Result < ( ) > {
105+ self . set ( socket, libc:: TLS_RX )
106+ }
107+
108+ /// Sets the kTLS parameters on the given file descriptor.
109+ fn set < S : AsFd > ( & self , socket : & S , direction : libc:: c_int ) -> io:: Result < ( ) > {
110+ let ( ffi_ptr, ffi_len) = match self {
111+ Self :: AesGcm128 ( crypto_info) => (
112+ <* const _ >:: cast ( crypto_info) ,
113+ mem:: size_of_val ( crypto_info) as libc:: socklen_t ,
114+ ) ,
115+ Self :: AesGcm256 ( crypto_info) => (
116+ <* const _ >:: cast ( crypto_info) ,
117+ mem:: size_of_val ( crypto_info) as libc:: socklen_t ,
118+ ) ,
119+ Self :: AesCcm128 ( crypto_info) => (
120+ <* const _ >:: cast ( crypto_info) ,
121+ mem:: size_of_val ( crypto_info) as libc:: socklen_t ,
122+ ) ,
123+ Self :: Chacha20Poly1305 ( crypto_info) => (
124+ <* const _ >:: cast ( crypto_info) ,
125+ mem:: size_of_val ( crypto_info) as libc:: socklen_t ,
126+ ) ,
127+ Self :: Sm4Gcm ( crypto_info) => (
128+ <* const _ >:: cast ( crypto_info) ,
129+ mem:: size_of_val ( crypto_info) as libc:: socklen_t ,
130+ ) ,
131+ Self :: Sm4Ccm ( crypto_info) => (
132+ <* const _ >:: cast ( crypto_info) ,
133+ mem:: size_of_val ( crypto_info) as libc:: socklen_t ,
134+ ) ,
135+ } ;
136+
137+ // SAFETY: syscall
138+ let ret = unsafe {
139+ libc:: setsockopt (
140+ socket. as_fd ( ) . as_raw_fd ( ) ,
141+ libc:: SOL_TLS ,
142+ direction,
143+ ffi_ptr,
144+ ffi_len,
145+ )
146+ } ;
147+
148+ if ret < 0 {
149+ return Err ( io:: Error :: last_os_error ( ) ) ;
116150 }
151+
152+ Ok ( ( ) )
117153 }
118154}
119155
120- #[ derive( thiserror:: Error , Debug ) ]
121- pub enum KtlsCompatibilityError {
122- #[ error( "cipher suite not supported with kTLS: {0:?}" ) ]
123- UnsupportedCipherSuite ( SupportedCipherSuite ) ,
124-
125- #[ error( "wrong size key" ) ]
156+ #[ derive( Debug , thiserror:: Error ) ]
157+ /// Crypto material is invalid, e.g., wrong size key or IV.
158+ enum InvalidCryptoInfo {
159+ #[ error( "Wrong size key" ) ]
160+ /// The provided key has an incorrect size (unlikely).
126161 WrongSizeKey ,
127162
128- #[ error( "wrong size iv" ) ]
129- WrongSizeIv ,
163+ #[ error( "The negotiated cipher suite [{0:?}] is not supported by the current kernel" ) ]
164+ /// The negotiated cipher suite is not supported by the current kernel.
165+ UnsupportedCipherSuite ( SupportedCipherSuite ) ,
166+ }
167+
168+ impl From < InvalidCryptoInfo > for io:: Error {
169+ fn from ( err : InvalidCryptoInfo ) -> Self {
170+ io:: Error :: other ( err)
171+ }
130172}
131173
132- impl CryptoInfo {
133- /// Try to convert rustls cipher suite and secrets into a `CryptoInfo`.
134- pub fn from_rustls (
174+ impl TlsCryptoInfo {
175+ /// Extract the [`TlsCryptoInfo`] from the given
176+ /// [`SupportedCipherSuite`] and [`ConnectionTrafficSecrets`].
177+ fn extract (
135178 cipher_suite : SupportedCipherSuite ,
136179 ( seq, secrets) : ( u64 , ConnectionTrafficSecrets ) ,
137- ) -> Result < CryptoInfo , KtlsCompatibilityError > {
180+ ) -> Result < Self , InvalidCryptoInfo > {
138181 let version = match cipher_suite {
139- SupportedCipherSuite :: Tls12 ( ..) => TLS_1_2_VERSION_NUMBER ,
140- SupportedCipherSuite :: Tls13 ( ..) => TLS_1_3_VERSION_NUMBER ,
182+ #[ cfg( feature = "tls12" ) ]
183+ SupportedCipherSuite :: Tls12 ( ..) => libc:: TLS_1_2_VERSION ,
184+ SupportedCipherSuite :: Tls13 ( ..) => libc:: TLS_1_3_VERSION ,
141185 } ;
142186
143187 Ok ( match secrets {
@@ -146,117 +190,64 @@ impl CryptoInfo {
146190 // rustls 0.21 and 0.22, the extract_keys codepath was changed,
147191 // so, for TLS 1.2, both GCM-128 and GCM-256 return the
148192 // Aes128Gcm variant.
193+ //
194+ // This issue is fixed since rustls 0.23.
149195
150- match key. as_ref ( ) . len ( ) {
151- 16 => CryptoInfo :: AesGcm128 ( ktls:: tls12_crypto_info_aes_gcm_128 {
152- info : ktls:: tls_crypto_info {
153- version,
154- cipher_type : ktls:: TLS_CIPHER_AES_GCM_128 as _ ,
155- } ,
156- iv : iv
157- . as_ref ( )
158- . get ( 4 ..)
159- . expect ( "AES-GCM-128 iv is 8 bytes" )
160- . try_into ( )
161- . expect ( "AES-GCM-128 iv is 8 bytes" ) ,
162- key : key
163- . as_ref ( )
164- . try_into ( )
165- . expect ( "AES-GCM-128 key is 16 bytes" ) ,
166- salt : iv
167- . as_ref ( )
168- . get ( ..4 )
169- . expect ( "AES-GCM-128 salt is 4 bytes" )
170- . try_into ( )
171- . expect ( "AES-GCM-128 salt is 4 bytes" ) ,
172- rec_seq : seq. to_be_bytes ( ) ,
173- } ) ,
174- 32 => CryptoInfo :: AesGcm256 ( ktls:: tls12_crypto_info_aes_gcm_256 {
175- info : ktls:: tls_crypto_info {
176- version,
177- cipher_type : ktls:: TLS_CIPHER_AES_GCM_256 as _ ,
178- } ,
179- iv : iv
180- . as_ref ( )
181- . get ( 4 ..)
182- . expect ( "AES-GCM-256 iv is 8 bytes" )
183- . try_into ( )
184- . expect ( "AES-GCM-256 iv is 8 bytes" ) ,
185- key : key
186- . as_ref ( )
187- . try_into ( )
188- . expect ( "AES-GCM-256 key is 32 bytes" ) ,
189- salt : iv
190- . as_ref ( )
191- . get ( ..4 )
192- . expect ( "AES-GCM-256 salt is 4 bytes" )
193- . try_into ( )
194- . expect ( "AES-GCM-256 salt is 4 bytes" ) ,
195- rec_seq : seq. to_be_bytes ( ) ,
196- } ) ,
197- _ => unreachable ! ( "GCM key length is not 16 or 32" ) ,
198- }
199- }
200- ConnectionTrafficSecrets :: Aes256Gcm { key, iv } => {
201- CryptoInfo :: AesGcm256 ( ktls:: tls12_crypto_info_aes_gcm_256 {
202- info : ktls:: tls_crypto_info {
196+ let iv_and_salt: & [ u8 ; NONCE_LEN ] = iv. as_ref ( ) . try_into ( ) . unwrap ( ) ;
197+
198+ Self :: AesGcm128 ( libc:: tls12_crypto_info_aes_gcm_128 {
199+ info : libc:: tls_crypto_info {
203200 version,
204- cipher_type : ktls :: TLS_CIPHER_AES_GCM_256 as _ ,
201+ cipher_type : libc :: TLS_CIPHER_AES_GCM_128 ,
205202 } ,
206- iv : iv
207- . as_ref ( )
208- . get ( 4 ..)
209- . expect ( "AES-GCM-256 iv is 8 bytes" )
210- . try_into ( )
211- . expect ( "AES-GCM-256 iv is 8 bytes" ) ,
203+ iv : iv_and_salt[ 4 ..] . try_into ( ) . unwrap ( ) ,
212204 key : key
213205 . as_ref ( )
214206 . try_into ( )
215- . expect ( "AES-GCM-256 key is 32 bytes" ) ,
216- salt : iv
207+ . map_err ( |_| InvalidCryptoInfo :: WrongSizeKey ) ?,
208+ salt : iv_and_salt[ ..4 ] . try_into ( ) . unwrap ( ) ,
209+ rec_seq : seq. to_be_bytes ( ) ,
210+ } )
211+ }
212+ ConnectionTrafficSecrets :: Aes256Gcm { key, iv } => {
213+ let iv_and_salt: & [ u8 ; NONCE_LEN ] = iv. as_ref ( ) . try_into ( ) . unwrap ( ) ;
214+
215+ Self :: AesGcm256 ( libc:: tls12_crypto_info_aes_gcm_256 {
216+ info : libc:: tls_crypto_info {
217+ version,
218+ cipher_type : libc:: TLS_CIPHER_AES_GCM_256 ,
219+ } ,
220+ iv : iv_and_salt[ 4 ..] . try_into ( ) . unwrap ( ) ,
221+ key : key
217222 . as_ref ( )
218- . get ( ..4 )
219- . expect ( "AES-GCM-256 salt is 4 bytes" )
220223 . try_into ( )
221- . expect ( "AES-GCM-256 salt is 4 bytes" ) ,
224+ . map_err ( |_| InvalidCryptoInfo :: WrongSizeKey ) ?,
225+ salt : iv_and_salt[ ..4 ] . try_into ( ) . unwrap ( ) ,
222226 rec_seq : seq. to_be_bytes ( ) ,
223227 } )
224228 }
225229 ConnectionTrafficSecrets :: Chacha20Poly1305 { key, iv } => {
226- CryptoInfo :: Chacha20Poly1305 ( ktls :: tls12_crypto_info_chacha20_poly1305 {
227- info : ktls :: tls_crypto_info {
230+ Self :: Chacha20Poly1305 ( libc :: tls12_crypto_info_chacha20_poly1305 {
231+ info : libc :: tls_crypto_info {
228232 version,
229- cipher_type : ktls :: TLS_CIPHER_CHACHA20_POLY1305 as _ ,
233+ cipher_type : libc :: TLS_CIPHER_CHACHA20_POLY1305 ,
230234 } ,
231- iv : iv
232- . as_ref ( )
233- . try_into ( )
234- . expect ( "Chacha20-Poly1305 iv is 12 bytes" ) ,
235+ iv : iv. as_ref ( ) . try_into ( ) . unwrap ( ) ,
235236 key : key
236237 . as_ref ( )
237238 . try_into ( )
238- . expect ( "Chacha20-Poly1305 key is 32 bytes" ) ,
239- salt : ktls :: __IncompleteArrayField :: new ( ) ,
239+ . map_err ( |_| InvalidCryptoInfo :: WrongSizeKey ) ? ,
240+ salt : [ ] ,
240241 rec_seq : seq. to_be_bytes ( ) ,
241242 } )
242243 }
243244 _ => {
244- return Err ( KtlsCompatibilityError :: UnsupportedCipherSuite ( cipher_suite) ) ;
245+ return Err ( InvalidCryptoInfo :: UnsupportedCipherSuite ( cipher_suite) ) ;
245246 }
246247 } )
247248 }
248249}
249250
250- pub fn setup_tls_info ( fd : RawFd , dir : Direction , info : CryptoInfo ) -> Result < ( ) , crate :: Error > {
251- let ret = unsafe { libc:: setsockopt ( fd, SOL_TLS , dir. into ( ) , info. as_ptr ( ) , info. size ( ) as _ ) } ;
252- if ret < 0 {
253- return Err ( crate :: Error :: TlsCryptoInfoError (
254- std:: io:: Error :: last_os_error ( ) ,
255- ) ) ;
256- }
257- Ok ( ( ) )
258- }
259-
260251const TLS_SET_RECORD_TYPE : libc:: c_int = 1 ;
261252const ALERT : u8 = 0x15 ;
262253
0 commit comments