@@ -188,6 +188,11 @@ def __init__(self, sock):
188188 # used for post handshake authentication in TLS 1.3
189189 self ._client_keypair = None
190190
191+ # dictionary with CertificateRequest messages we (as a server) have
192+ # sent, the keys are the "certificate request context" from the
193+ # messages (which are the values)
194+ self ._cert_requests = {}
195+
191196 @property
192197 def _send_record_limit (self ):
193198 """Maximum size of payload that can be sent."""
@@ -303,13 +308,19 @@ def readAsync(self, max=None, min=1):
303308 :rtype: iterable
304309 :returns: A generator; see above for details.
305310 """
311+ constructor_type = None
306312 if self .version > (3 , 3 ):
307313 allowedTypes = (ContentType .application_data ,
308314 ContentType .handshake )
309315 if self ._client_keypair :
310316 allowedHsTypes = (HandshakeType .new_session_ticket ,
311317 HandshakeType .key_update ,
312318 HandshakeType .certificate_request )
319+ elif self ._cert_requests :
320+ allowedHsTypes = (HandshakeType .new_session_ticket ,
321+ HandshakeType .key_update ,
322+ HandshakeType .certificate )
323+ constructor_type = CertificateType .x509
313324 else :
314325 allowedHsTypes = (HandshakeType .new_session_ticket ,
315326 HandshakeType .key_update )
@@ -320,7 +331,8 @@ def readAsync(self, max=None, min=1):
320331 while len (self ._readBuffer ) < min and not self .closed :
321332 try :
322333 for result in self ._getMsg (allowedTypes ,
323- allowedHsTypes ):
334+ allowedHsTypes ,
335+ constructor_type ):
324336 if result in (0 , 1 ):
325337 yield result
326338 if isinstance (result , NewSessionTicket ):
@@ -331,10 +343,13 @@ def readAsync(self, max=None, min=1):
331343 for result in self ._handle_keyupdate_request (result ):
332344 yield result
333345 continue
346+ elif isinstance (result , Certificate ):
347+ for result in self ._handle_srv_pha (result ):
348+ yield result
349+ continue
334350 elif isinstance (result , CertificateRequest ):
335351 for result in self ._handle_pha (result ):
336- if result in (0 , 1 ):
337- yield
352+ yield result
338353 continue
339354 applicationData = result
340355 self ._readBuffer += applicationData .write ()
@@ -705,6 +720,117 @@ def _handle_pha(self, cert_request):
705720 for result in self ._sendMsgs (msgs ):
706721 yield result
707722
723+ def _handle_srv_pha (self , cert ):
724+ """Process the post-handshake authentication from client."""
725+ prf_name = 'sha256'
726+ prf_size = 32
727+ if self .session .cipherSuite in CipherSuite .sha384PrfSuites :
728+ prf_name = 'sha384'
729+ prf_size = 48
730+
731+ cr_context = cert .certificate_request_context
732+ if not cr_context :
733+ for result in self ._sendError (
734+ AlertDescription .illegal_parameter ,
735+ "Certificate Request context missing in Certificate "
736+ "message from client" ):
737+ yield result
738+
739+ try :
740+ cr = self ._cert_requests .pop (bytes (cr_context ))
741+ except KeyError :
742+ for result in self ._sendError (
743+ AlertDescription .illegal_parameter ,
744+ "Certificiate Request context is incorrect or was already "
745+ "handled previously" ):
746+ yield result
747+
748+ # TODO: verify that the extensions used by client were sent by us in
749+ # CertificateReuest
750+
751+ handshake_context = self ._first_handshake_hashes .copy ()
752+ handshake_context .update (cr .write ())
753+ handshake_context .update (cert .write ())
754+
755+ if cert .cert_chain :
756+ for result in self ._getMsg (ContentType .handshake ,
757+ HandshakeType .certificate_verify ):
758+ if result in (0 , 1 ):
759+ yield result
760+ else :
761+ break
762+ assert isinstance (result , CertificateVerify )
763+ cert_verify = result
764+
765+ valid_sig_algs = cr .supported_signature_algs
766+ if cert_verify .signatureAlgorithm not in valid_sig_algs :
767+ for result in self ._sendError (
768+ AlertDescription .illegal_parameter ,
769+ "Client selected signature algorithm we didn't "
770+ "advertise" ):
771+ yield result
772+ avail_sig_algs = self ._sigHashesToList (HandshakeSettings (), None ,
773+ cert .cert_chain ,
774+ version = (3 , 4 ))
775+ if cert_verify .signatureAlgorithm not in avail_sig_algs :
776+ for result in self ._sendError (
777+ AlertDescription .illegal_parameter ,
778+ "Client selected signature algorithm not consistent "
779+ "with public key in its certificate" ):
780+ yield result
781+ scheme = SignatureScheme .toRepr (cert_verify .signatureAlgorithm )
782+ sig_scheme = getattr (SignatureScheme , scheme )
783+
784+ signature_context = \
785+ KeyExchange .calcVerifyBytes ((3 , 4 ),
786+ handshake_context ,
787+ sig_scheme , None , None , None ,
788+ prf_name , b'client' )
789+
790+ if sig_scheme [1 ] == SignatureAlgorithm .ecdsa :
791+ pad_type = None
792+ hash_name = HashAlgorithm .toRepr (sig_scheme [0 ])
793+ salt_len = None
794+ else :
795+ pad_type = SignatureScheme .getPadding (scheme )
796+ hash_name = SignatureScheme .getHash (scheme )
797+ salt_len = getattr (hashlib , hash_name )().digest_size
798+
799+ if not cert .cert_chain .getEndEntityPublicKey ().verify (
800+ cert_verify .signature , signature_context , pad_type ,
801+ hash_name , salt_len ):
802+ for result in self ._sendError (
803+ AlertDescription .decrypt_error ,
804+ "Signature verification failed" ):
805+ yield result
806+ handshake_context .update (cert_verify .write ())
807+
808+ finished_key = HKDF_expand_label (self .session .cl_app_secret ,
809+ b'finished' , b'' ,
810+ prf_size , prf_name )
811+ verify_data = secureHMAC (finished_key ,
812+ handshake_context .digest (prf_name ),
813+ prf_name )
814+
815+ for result in self ._getMsg (ContentType .handshake ,
816+ HandshakeType .finished ,
817+ prf_size ):
818+ if result in (0 , 1 ):
819+ yield result
820+ else :
821+ break
822+ assert isinstance (result , Finished )
823+
824+ finished = result
825+
826+ if finished .verify_data != verify_data :
827+ for result in self ._sendError (
828+ AlertDescription .decrypt_error ,
829+ "Invalid Finished verify_data from client" ):
830+ yield result
831+
832+ self .session .clientCertChain = cert .cert_chain
833+
708834 def _shutdown (self , resumable ):
709835 self ._recordLayer .shutdown ()
710836 self .version = (0 ,0 )
0 commit comments