3333from struct import pack as struct_pack , unpack as struct_unpack , unpack_from as struct_unpack_from
3434
3535from .constants import DEFAULT_PORT , DEFAULT_USER_AGENT , KNOWN_HOSTS , MAGIC_PREAMBLE , \
36- SECURITY_NONE , SECURITY_TRUST_ON_FIRST_USE
36+ SECURITY_DEFAULT , SECURITY_TRUST_ON_FIRST_USE
3737from .compat import hex2
3838from .exceptions import ProtocolError
3939from .packstream import Packer , Unpacker
@@ -316,36 +316,51 @@ def close(self):
316316 self .closed = True
317317
318318
319- def verify_certificate (host , der_encoded_certificate ):
320- base64_encoded_certificate = b64encode (der_encoded_certificate )
321- if isfile (KNOWN_HOSTS ):
322- with open (KNOWN_HOSTS ) as f_in :
323- for line in f_in :
324- known_host , _ , known_cert = line .strip ().partition (":" )
325- if host == known_host :
326- if base64_encoded_certificate == known_cert :
327- # Certificate match
328- return
329- else :
330- # Certificate mismatch
331- print (base64_encoded_certificate )
332- print (known_cert )
333- raise ProtocolError ("Server certificate does not match known certificate for %r; check "
334- "details in file %r" % (host , KNOWN_HOSTS ))
335- # First use (no hosts match)
336- try :
337- makedirs (dirname (KNOWN_HOSTS ))
338- except OSError :
339- pass
340- f_out = os_open (KNOWN_HOSTS , O_CREAT | O_APPEND | O_WRONLY , 0o600 ) # TODO: Windows
341- if isinstance (host , bytes ):
342- os_write (f_out , host )
343- else :
344- os_write (f_out , host .encode ("utf-8" ))
345- os_write (f_out , b":" )
346- os_write (f_out , base64_encoded_certificate )
347- os_write (f_out , b"\n " )
348- os_close (f_out )
319+ class CertificateStore (object ):
320+
321+ def match_or_trust (self , host , der_encoded_certificate ):
322+ """ Check whether the supplied certificate matches that stored for the
323+ specified host. If it does, return ``True``, if it doesn't, return
324+ ``False``. If no entry for that host is found, add it to the store
325+ and return ``True``.
326+
327+ :arg host:
328+ :arg der_encoded_certificate:
329+ :return:
330+ """
331+ raise NotImplementedError ()
332+
333+
334+ class PersonalCertificateStore (CertificateStore ):
335+
336+ def __init__ (self , path = None ):
337+ self .path = path or KNOWN_HOSTS
338+
339+ def match_or_trust (self , host , der_encoded_certificate ):
340+ base64_encoded_certificate = b64encode (der_encoded_certificate )
341+ if isfile (self .path ):
342+ with open (self .path ) as f_in :
343+ for line in f_in :
344+ known_host , _ , known_cert = line .strip ().partition (":" )
345+ if host == known_host :
346+ print ("Received: %s" % base64_encoded_certificate )
347+ print ("Known: %s" % known_cert )
348+ return base64_encoded_certificate == known_cert
349+ # First use (no hosts match)
350+ try :
351+ makedirs (dirname (self .path ))
352+ except OSError :
353+ pass
354+ f_out = os_open (self .path , O_CREAT | O_APPEND | O_WRONLY , 0o600 ) # TODO: Windows
355+ if isinstance (host , bytes ):
356+ os_write (f_out , host )
357+ else :
358+ os_write (f_out , host .encode ("utf-8" ))
359+ os_write (f_out , b":" )
360+ os_write (f_out , base64_encoded_certificate )
361+ os_write (f_out , b"\n " )
362+ os_close (f_out )
363+ return True
349364
350365
351366def connect (host , port = None , ssl_context = None , ** config ):
@@ -372,9 +387,12 @@ def connect(host, port=None, ssl_context=None, **config):
372387 der_encoded_server_certificate = s .getpeercert (binary_form = True )
373388 if der_encoded_server_certificate is None :
374389 raise ProtocolError ("When using a secure socket, the server should always provide a certificate" )
375- security = config .get ("security" , SECURITY_NONE )
390+ security = config .get ("security" , SECURITY_DEFAULT )
376391 if security == SECURITY_TRUST_ON_FIRST_USE :
377- verify_certificate (host , der_encoded_server_certificate )
392+ store = PersonalCertificateStore ()
393+ if not store .match_or_trust (host , der_encoded_server_certificate ):
394+ raise ProtocolError ("Server certificate does not match known certificate for %r; check "
395+ "details in file %r" % (host , KNOWN_HOSTS ))
378396 else :
379397 der_encoded_server_certificate = None
380398
0 commit comments