@@ -9,8 +9,10 @@ use std::ffi::c_void;
99use std:: sync:: atomic:: { AtomicU8 , Ordering } ;
1010use std:: sync:: OnceLock ;
1111
12- static CUSTOM_ENCRYPTION_CALLED_BACK : AtomicU8 = AtomicU8 :: new ( 0 ) ;
13- static CUSTOM_DECRYPTION_CALLED_BACK : AtomicU8 = AtomicU8 :: new ( 0 ) ;
12+ static SUCCESS_ENCRYPTION_CALLED_BACK : AtomicU8 = AtomicU8 :: new ( 0 ) ;
13+ static SUCCESS_DECRYPTION_CALLED_BACK : AtomicU8 = AtomicU8 :: new ( 0 ) ;
14+ static NOOP_ENCRYPTION_CALLED_BACK : AtomicU8 = AtomicU8 :: new ( 0 ) ;
15+ static NOOP_DECRYPTION_CALLED_BACK : AtomicU8 = AtomicU8 :: new ( 0 ) ;
1416
1517#[ test]
1618fn resume_session ( ) {
@@ -59,7 +61,7 @@ fn custom_callback() {
5961 server. expected_connections_count ( 2 ) ;
6062 server
6163 . ctx ( )
62- . set_ticket_key_callback ( test_tickey_key_callback ) ;
64+ . set_ticket_key_callback ( test_success_tickey_key_callback ) ;
6365 let server = server. build ( ) ;
6466
6567 let mut client = server. client ( ) ;
@@ -77,8 +79,8 @@ fn custom_callback() {
7779
7880 assert ! ( !ssl_stream. ssl( ) . session_reused( ) ) ;
7981 assert ! ( SESSION_TICKET . get( ) . is_some( ) ) ;
80- assert_eq ! ( CUSTOM_ENCRYPTION_CALLED_BACK . load( Ordering :: SeqCst ) , 2 ) ;
81- assert_eq ! ( CUSTOM_DECRYPTION_CALLED_BACK . load( Ordering :: SeqCst ) , 0 ) ;
82+ assert_eq ! ( SUCCESS_ENCRYPTION_CALLED_BACK . load( Ordering :: SeqCst ) , 2 ) ;
83+ assert_eq ! ( SUCCESS_DECRYPTION_CALLED_BACK . load( Ordering :: SeqCst ) , 0 ) ;
8284 assert_eq ! ( NST_RECIEVED_COUNT . load( Ordering :: SeqCst ) , 2 ) ;
8385
8486 // Retrieve the session ticket
@@ -91,12 +93,111 @@ fn custom_callback() {
9193 let ssl_stream_2 = ssl_builder. connect ( ) ;
9294
9395 assert ! ( ssl_stream_2. ssl( ) . session_reused( ) ) ;
94- assert_eq ! ( CUSTOM_ENCRYPTION_CALLED_BACK . load( Ordering :: SeqCst ) , 4 ) ;
95- assert_eq ! ( CUSTOM_DECRYPTION_CALLED_BACK . load( Ordering :: SeqCst ) , 1 ) ;
96+ assert_eq ! ( SUCCESS_ENCRYPTION_CALLED_BACK . load( Ordering :: SeqCst ) , 4 ) ;
97+ assert_eq ! ( SUCCESS_DECRYPTION_CALLED_BACK . load( Ordering :: SeqCst ) , 1 ) ;
98+ }
99+
100+ #[ test]
101+ fn custom_callback_unrecognized_decryption_ticket ( ) {
102+ static SESSION_TICKET : OnceLock < Vec < u8 > > = OnceLock :: new ( ) ;
103+ static NST_RECIEVED_COUNT : AtomicU8 = AtomicU8 :: new ( 0 ) ;
104+
105+ let mut server = Server :: builder ( ) ;
106+ server. expected_connections_count ( 2 ) ;
107+ server
108+ . ctx ( )
109+ . set_ticket_key_callback ( test_noop_tickey_key_callback) ;
110+ let server = server. build ( ) ;
111+
112+ let mut client = server. client ( ) ;
113+ client
114+ . ctx ( )
115+ . set_session_cache_mode ( SslSessionCacheMode :: CLIENT ) ;
116+ client. ctx ( ) . set_new_session_callback ( |_, session| {
117+ NST_RECIEVED_COUNT . fetch_add ( 1 , Ordering :: SeqCst ) ;
118+ // The server sends multiple session tickets but we only care to retrieve one.
119+ if SESSION_TICKET . get ( ) . is_none ( ) {
120+ SESSION_TICKET . set ( session. to_der ( ) . unwrap ( ) ) . unwrap ( ) ;
121+ }
122+ } ) ;
123+ let ssl_stream = client. connect ( ) ;
124+
125+ assert ! ( !ssl_stream. ssl( ) . session_reused( ) ) ;
126+ assert ! ( SESSION_TICKET . get( ) . is_some( ) ) ;
127+ assert_eq ! ( SUCCESS_ENCRYPTION_CALLED_BACK . load( Ordering :: SeqCst ) , 2 ) ;
128+ assert_eq ! ( NOOP_DECRYPTION_CALLED_BACK . load( Ordering :: SeqCst ) , 0 ) ;
129+ assert_eq ! ( NST_RECIEVED_COUNT . load( Ordering :: SeqCst ) , 2 ) ;
130+
131+ // Retrieve the session ticket
132+ let session_ticket = SslSession :: from_der ( SESSION_TICKET . get ( ) . unwrap ( ) ) . unwrap ( ) ;
133+
134+ // Attempt to resume the connection using the session ticket
135+ let client_2 = server. client ( ) ;
136+ let mut ssl_builder = client_2. build ( ) . builder ( ) ;
137+ unsafe { ssl_builder. ssl ( ) . set_session ( & session_ticket) . unwrap ( ) } ;
138+ let ssl_stream_2 = ssl_builder. connect ( ) ;
139+
140+ // Second connection was NOT resumed due to TicketKeyCallbackResult::Noop on decryption
141+ assert ! ( !ssl_stream_2. ssl( ) . session_reused( ) ) ;
142+ assert_eq ! ( SUCCESS_ENCRYPTION_CALLED_BACK . load( Ordering :: SeqCst ) , 4 ) ;
143+ assert_eq ! ( NOOP_DECRYPTION_CALLED_BACK . load( Ordering :: SeqCst ) , 1 ) ;
144+ }
145+
146+ // Successfully return a session ticket in encryption mode but return a
147+ // TicketKeyCallbackResult::Noop in decryption mode.
148+ fn test_noop_tickey_key_callback (
149+ _ssl : & SslRef ,
150+ _key_name : & mut [ u8 ; 16 ] ,
151+ _iv : * mut u8 ,
152+ evp_ctx : * mut ffi:: EVP_CIPHER_CTX ,
153+ hmac_ctx : * mut ffi:: HMAC_CTX ,
154+ encrypt : bool ,
155+ ) -> TicketKeyCallbackResult {
156+ // These should only be used for testing purposes.
157+ const TEST_CBC_IV : [ u8 ; 16 ] = [ 1 ; 16 ] ;
158+ const TEST_AES_128_CBC_KEY : [ u8 ; 16 ] = [ 2 ; 16 ] ;
159+ const TEST_HMAC_KEY : [ u8 ; 32 ] = [ 3 ; 32 ] ;
160+
161+ let digest = MessageDigest :: sha256 ( ) ;
162+ let cipher = Cipher :: aes_128_cbc ( ) ;
163+
164+ if encrypt {
165+ NOOP_ENCRYPTION_CALLED_BACK . fetch_add ( 1 , Ordering :: SeqCst ) ;
166+ // Set the encryption context.
167+ let ret = unsafe {
168+ ffi:: EVP_EncryptInit_ex (
169+ evp_ctx,
170+ cipher. as_ptr ( ) ,
171+ // ENGINE api is deprecated
172+ core:: ptr:: null_mut ( ) ,
173+ TEST_AES_128_CBC_KEY . as_ptr ( ) ,
174+ TEST_CBC_IV . as_ptr ( ) ,
175+ )
176+ } ;
177+ assert ! ( ret == 1 ) ;
178+
179+ // Set the hmac context.
180+ let ret = unsafe {
181+ ffi:: HMAC_Init_ex (
182+ hmac_ctx,
183+ TEST_HMAC_KEY . as_ptr ( ) as * const c_void ,
184+ TEST_HMAC_KEY . len ( ) ,
185+ digest. as_ptr ( ) ,
186+ // ENGINE api is deprecated
187+ core:: ptr:: null_mut ( ) ,
188+ )
189+ } ;
190+ assert ! ( ret == 1 ) ;
191+
192+ TicketKeyCallbackResult :: Success
193+ } else {
194+ NOOP_DECRYPTION_CALLED_BACK . fetch_add ( 1 , Ordering :: SeqCst ) ;
195+ TicketKeyCallbackResult :: Noop
196+ }
96197}
97198
98199// Custom callback to encrypt and decrypt session tickets
99- fn test_tickey_key_callback (
200+ fn test_success_tickey_key_callback (
100201 _ssl : & SslRef ,
101202 _key_name : & mut [ u8 ; 16 ] ,
102203 _iv : * mut u8 ,
@@ -113,7 +214,7 @@ fn test_tickey_key_callback(
113214 let cipher = Cipher :: aes_128_cbc ( ) ;
114215
115216 if encrypt {
116- CUSTOM_ENCRYPTION_CALLED_BACK . fetch_add ( 1 , Ordering :: SeqCst ) ;
217+ SUCCESS_ENCRYPTION_CALLED_BACK . fetch_add ( 1 , Ordering :: SeqCst ) ;
117218 // Set the encryption context.
118219 let ret = unsafe {
119220 ffi:: EVP_EncryptInit_ex (
@@ -140,7 +241,7 @@ fn test_tickey_key_callback(
140241 } ;
141242 assert ! ( ret == 1 ) ;
142243 } else {
143- CUSTOM_DECRYPTION_CALLED_BACK . fetch_add ( 1 , Ordering :: SeqCst ) ;
244+ SUCCESS_DECRYPTION_CALLED_BACK . fetch_add ( 1 , Ordering :: SeqCst ) ;
144245 let ret = unsafe {
145246 ffi:: EVP_DecryptInit_ex (
146247 evp_ctx,
0 commit comments