@@ -39,7 +39,7 @@ mod _ssl {
3939 socket:: { self , PySocket } ,
4040 vm:: {
4141 Py , PyObjectRef , PyPayload , PyRef , PyResult , VirtualMachine ,
42- builtins:: { PyBaseExceptionRef , PyListRef , PyStrRef , PyType , PyTypeRef , PyWeak } ,
42+ builtins:: { PyBaseExceptionRef , PyBytesRef , PyListRef , PyStrRef , PyType , PyTypeRef , PyWeak } ,
4343 class_or_notimplemented,
4444 convert:: { ToPyException , ToPyObject } ,
4545 exceptions,
@@ -1139,6 +1139,49 @@ mod _ssl {
11391139 }
11401140 }
11411141
1142+ #[ pymethod]
1143+ fn get_channel_binding (
1144+ & self ,
1145+ cb_type : OptionalArg < PyStrRef > ,
1146+ vm : & VirtualMachine ,
1147+ ) -> PyResult < Option < PyBytesRef > > {
1148+ const CB_MAXLEN : usize = 512 ;
1149+
1150+ let cb_type_str = cb_type. as_ref ( ) . map_or ( "tls-unique" , |s| s. as_str ( ) ) ;
1151+
1152+ if cb_type_str != "tls-unique" {
1153+ return Err ( vm. new_value_error ( format ! (
1154+ "Unsupported channel binding type '{}'" ,
1155+ cb_type_str
1156+ ) ) ) ;
1157+ }
1158+
1159+ let stream = self . stream . read ( ) ;
1160+ let ssl_ptr = stream. ssl ( ) . as_ptr ( ) ;
1161+
1162+ unsafe {
1163+ let session_reused = sys:: SSL_session_reused ( ssl_ptr) != 0 ;
1164+ let is_client = matches ! ( self . socket_type, SslServerOrClient :: Client ) ;
1165+
1166+ // Use XOR logic from CPython
1167+ let use_finished = session_reused ^ is_client;
1168+
1169+ let mut buf = vec ! [ 0u8 ; CB_MAXLEN ] ;
1170+ let len = if use_finished {
1171+ sys:: SSL_get_finished ( ssl_ptr, buf. as_mut_ptr ( ) as * mut _ , CB_MAXLEN )
1172+ } else {
1173+ sys:: SSL_get_peer_finished ( ssl_ptr, buf. as_mut_ptr ( ) as * mut _ , CB_MAXLEN )
1174+ } ;
1175+
1176+ if len == 0 {
1177+ Ok ( None )
1178+ } else {
1179+ buf. truncate ( len) ;
1180+ Ok ( Some ( vm. ctx . new_bytes ( buf) ) )
1181+ }
1182+ }
1183+ }
1184+
11421185 #[ cfg( osslconf = "OPENSSL_NO_COMP" ) ]
11431186 #[ pymethod]
11441187 fn compression ( & self ) -> Option < & ' static str > {
0 commit comments