@@ -234,7 +234,7 @@ def testing_context(server_cert=SIGNED_CERTFILE):
234234
235235 server_context = ssl .SSLContext (ssl .PROTOCOL_TLS_SERVER )
236236 server_context .load_cert_chain (server_cert )
237- client_context .load_verify_locations (SIGNING_CA )
237+ server_context .load_verify_locations (SIGNING_CA )
238238
239239 return client_context , server_context , hostname
240240
@@ -2282,6 +2282,23 @@ def run(self):
22822282 sys .stdout .write (" server: read CB tls-unique from client, sending our CB data...\n " )
22832283 data = self .sslconn .get_channel_binding ("tls-unique" )
22842284 self .write (repr (data ).encode ("us-ascii" ) + b"\n " )
2285+ elif stripped == b'PHA' :
2286+ if support .verbose and self .server .connectionchatty :
2287+ sys .stdout .write (" server: initiating post handshake auth\n " )
2288+ try :
2289+ self .sslconn .verify_client_post_handshake ()
2290+ except ssl .SSLError as e :
2291+ self .write (repr (e ).encode ("us-ascii" ) + b"\n " )
2292+ else :
2293+ self .write (b"OK\n " )
2294+ elif stripped == b'HASCERT' :
2295+ if self .sslconn .getpeercert () is not None :
2296+ self .write (b'TRUE\n ' )
2297+ else :
2298+ self .write (b'FALSE\n ' )
2299+ elif stripped == b'GETCERT' :
2300+ cert = self .sslconn .getpeercert ()
2301+ self .write (repr (cert ).encode ("us-ascii" ) + b"\n " )
22852302 else :
22862303 if (support .verbose and
22872304 self .server .connectionchatty ):
@@ -4175,6 +4192,179 @@ def test_session_handling(self):
41754192 'Session refers to a different SSLContext.' )
41764193
41774194
4195+ @unittest .skipUnless (ssl .HAS_TLSv1_3 , "Test needs TLS 1.3" )
4196+ class TestPostHandshakeAuth (unittest .TestCase ):
4197+ def test_pha_setter (self ):
4198+ protocols = [
4199+ ssl .PROTOCOL_TLS , ssl .PROTOCOL_TLS_SERVER , ssl .PROTOCOL_TLS_CLIENT
4200+ ]
4201+ for protocol in protocols :
4202+ ctx = ssl .SSLContext (protocol )
4203+ self .assertEqual (ctx .post_handshake_auth , False )
4204+
4205+ ctx .post_handshake_auth = True
4206+ self .assertEqual (ctx .post_handshake_auth , True )
4207+
4208+ ctx .verify_mode = ssl .CERT_REQUIRED
4209+ self .assertEqual (ctx .verify_mode , ssl .CERT_REQUIRED )
4210+ self .assertEqual (ctx .post_handshake_auth , True )
4211+
4212+ ctx .post_handshake_auth = False
4213+ self .assertEqual (ctx .verify_mode , ssl .CERT_REQUIRED )
4214+ self .assertEqual (ctx .post_handshake_auth , False )
4215+
4216+ ctx .verify_mode = ssl .CERT_OPTIONAL
4217+ ctx .post_handshake_auth = True
4218+ self .assertEqual (ctx .verify_mode , ssl .CERT_OPTIONAL )
4219+ self .assertEqual (ctx .post_handshake_auth , True )
4220+
4221+ def test_pha_required (self ):
4222+ client_context , server_context , hostname = testing_context ()
4223+ server_context .post_handshake_auth = True
4224+ server_context .verify_mode = ssl .CERT_REQUIRED
4225+ client_context .post_handshake_auth = True
4226+ client_context .load_cert_chain (SIGNED_CERTFILE )
4227+
4228+ server = ThreadedEchoServer (context = server_context , chatty = False )
4229+ with server :
4230+ with client_context .wrap_socket (socket .socket (),
4231+ server_hostname = hostname ) as s :
4232+ s .connect ((HOST , server .port ))
4233+ s .write (b'HASCERT' )
4234+ self .assertEqual (s .recv (1024 ), b'FALSE\n ' )
4235+ s .write (b'PHA' )
4236+ self .assertEqual (s .recv (1024 ), b'OK\n ' )
4237+ s .write (b'HASCERT' )
4238+ self .assertEqual (s .recv (1024 ), b'TRUE\n ' )
4239+ # PHA method just returns true when cert is already available
4240+ s .write (b'PHA' )
4241+ self .assertEqual (s .recv (1024 ), b'OK\n ' )
4242+ s .write (b'GETCERT' )
4243+ cert_text = s .recv (4096 ).decode ('us-ascii' )
4244+ self .assertIn ('Python Software Foundation CA' , cert_text )
4245+
4246+ def test_pha_required_nocert (self ):
4247+ client_context , server_context , hostname = testing_context ()
4248+ server_context .post_handshake_auth = True
4249+ server_context .verify_mode = ssl .CERT_REQUIRED
4250+ client_context .post_handshake_auth = True
4251+
4252+ server = ThreadedEchoServer (context = server_context , chatty = False )
4253+ with server :
4254+ with client_context .wrap_socket (socket .socket (),
4255+ server_hostname = hostname ) as s :
4256+ s .connect ((HOST , server .port ))
4257+ s .write (b'PHA' )
4258+ # receive CertificateRequest
4259+ self .assertEqual (s .recv (1024 ), b'OK\n ' )
4260+ # send empty Certificate + Finish
4261+ s .write (b'HASCERT' )
4262+ # receive alert
4263+ with self .assertRaisesRegex (
4264+ ssl .SSLError ,
4265+ 'tlsv13 alert certificate required' ):
4266+ s .recv (1024 )
4267+
4268+ def test_pha_optional (self ):
4269+ if support .verbose :
4270+ sys .stdout .write ("\n " )
4271+
4272+ client_context , server_context , hostname = testing_context ()
4273+ server_context .post_handshake_auth = True
4274+ server_context .verify_mode = ssl .CERT_REQUIRED
4275+ client_context .post_handshake_auth = True
4276+ client_context .load_cert_chain (SIGNED_CERTFILE )
4277+
4278+ # check CERT_OPTIONAL
4279+ server_context .verify_mode = ssl .CERT_OPTIONAL
4280+ server = ThreadedEchoServer (context = server_context , chatty = False )
4281+ with server :
4282+ with client_context .wrap_socket (socket .socket (),
4283+ server_hostname = hostname ) as s :
4284+ s .connect ((HOST , server .port ))
4285+ s .write (b'HASCERT' )
4286+ self .assertEqual (s .recv (1024 ), b'FALSE\n ' )
4287+ s .write (b'PHA' )
4288+ self .assertEqual (s .recv (1024 ), b'OK\n ' )
4289+ s .write (b'HASCERT' )
4290+ self .assertEqual (s .recv (1024 ), b'TRUE\n ' )
4291+
4292+ def test_pha_optional_nocert (self ):
4293+ if support .verbose :
4294+ sys .stdout .write ("\n " )
4295+
4296+ client_context , server_context , hostname = testing_context ()
4297+ server_context .post_handshake_auth = True
4298+ server_context .verify_mode = ssl .CERT_OPTIONAL
4299+ client_context .post_handshake_auth = True
4300+
4301+ server = ThreadedEchoServer (context = server_context , chatty = False )
4302+ with server :
4303+ with client_context .wrap_socket (socket .socket (),
4304+ server_hostname = hostname ) as s :
4305+ s .connect ((HOST , server .port ))
4306+ s .write (b'HASCERT' )
4307+ self .assertEqual (s .recv (1024 ), b'FALSE\n ' )
4308+ s .write (b'PHA' )
4309+ self .assertEqual (s .recv (1024 ), b'OK\n ' )
4310+ # optional doens't fail when client does not have a cert
4311+ s .write (b'HASCERT' )
4312+ self .assertEqual (s .recv (1024 ), b'FALSE\n ' )
4313+
4314+ def test_pha_no_pha_client (self ):
4315+ client_context , server_context , hostname = testing_context ()
4316+ server_context .post_handshake_auth = True
4317+ server_context .verify_mode = ssl .CERT_REQUIRED
4318+ client_context .load_cert_chain (SIGNED_CERTFILE )
4319+
4320+ server = ThreadedEchoServer (context = server_context , chatty = False )
4321+ with server :
4322+ with client_context .wrap_socket (socket .socket (),
4323+ server_hostname = hostname ) as s :
4324+ s .connect ((HOST , server .port ))
4325+ with self .assertRaisesRegex (ssl .SSLError , 'not server' ):
4326+ s .verify_client_post_handshake ()
4327+ s .write (b'PHA' )
4328+ self .assertIn (b'extension not received' , s .recv (1024 ))
4329+
4330+ def test_pha_no_pha_server (self ):
4331+ # server doesn't have PHA enabled, cert is requested in handshake
4332+ client_context , server_context , hostname = testing_context ()
4333+ server_context .verify_mode = ssl .CERT_REQUIRED
4334+ client_context .post_handshake_auth = True
4335+ client_context .load_cert_chain (SIGNED_CERTFILE )
4336+
4337+ server = ThreadedEchoServer (context = server_context , chatty = False )
4338+ with server :
4339+ with client_context .wrap_socket (socket .socket (),
4340+ server_hostname = hostname ) as s :
4341+ s .connect ((HOST , server .port ))
4342+ s .write (b'HASCERT' )
4343+ self .assertEqual (s .recv (1024 ), b'TRUE\n ' )
4344+ # PHA doesn't fail if there is already a cert
4345+ s .write (b'PHA' )
4346+ self .assertEqual (s .recv (1024 ), b'OK\n ' )
4347+ s .write (b'HASCERT' )
4348+ self .assertEqual (s .recv (1024 ), b'TRUE\n ' )
4349+
4350+ def test_pha_not_tls13 (self ):
4351+ # TLS 1.2
4352+ client_context , server_context , hostname = testing_context ()
4353+ server_context .verify_mode = ssl .CERT_REQUIRED
4354+ client_context .maximum_version = ssl .TLSVersion .TLSv1_2
4355+ client_context .post_handshake_auth = True
4356+ client_context .load_cert_chain (SIGNED_CERTFILE )
4357+
4358+ server = ThreadedEchoServer (context = server_context , chatty = False )
4359+ with server :
4360+ with client_context .wrap_socket (socket .socket (),
4361+ server_hostname = hostname ) as s :
4362+ s .connect ((HOST , server .port ))
4363+ # PHA fails for TLS != 1.3
4364+ s .write (b'PHA' )
4365+ self .assertIn (b'WRONG_SSL_VERSION' , s .recv (1024 ))
4366+
4367+
41784368def test_main (verbose = False ):
41794369 if support .verbose :
41804370 import warnings
@@ -4218,6 +4408,7 @@ def test_main(verbose=False):
42184408 tests = [
42194409 ContextTests , BasicSocketTests , SSLErrorTests , MemoryBIOTests ,
42204410 SSLObjectTests , SimpleBackgroundTests , ThreadedTests ,
4411+ TestPostHandshakeAuth
42214412 ]
42224413
42234414 if support .is_resource_enabled ('network' ):
0 commit comments