1212import itertools
1313import logging
1414import os
15+ import random
1516import re
1617import sys
1718import sysconfig
@@ -102,6 +103,20 @@ def read_vectors(hash_name):
102103)
103104
104105
106+ def make_hash_objects (* digestmods , buf = b"" , ** kwargs ):
107+ objects = []
108+ for digestmod in digestmods :
109+ try :
110+ if callable (digestmod ):
111+ obj = digestmod (buf )
112+ else :
113+ obj = hashlib .new (digestmod , buf , ** kwargs )
114+ except ValueError :
115+ continue
116+ objects .append ((digestmod , obj ))
117+ return objects
118+
119+
105120class HashLibTestCase (unittest .TestCase ):
106121 supported_hash_names = ( 'md5' , 'MD5' , 'sha1' , 'SHA1' ,
107122 'sha224' , 'SHA224' , 'sha256' , 'SHA256' ,
@@ -524,7 +539,8 @@ def test_blake2_update_over_4gb(self):
524539 self .assertEqual (h .hexdigest (), "8a268e83dd30528bc0907fa2008c91de8f090a0b6e0e60a5ff0d999d8485526f" )
525540
526541 def check (self , name , data , hexdigest , shake = False , ** kwargs ):
527- length = len (hexdigest )// 2
542+ n = len (data )
543+ length = len (hexdigest ) // 2
528544 hexdigest = hexdigest .lower ()
529545 constructors = self .constructors_to_test [name ]
530546 # 2 is for hashlib.name(...) and hashlib.new(name, ...)
@@ -533,47 +549,47 @@ def check(self, name, data, hexdigest, shake=False, **kwargs):
533549 m = hash_object_constructor (data , ** kwargs )
534550 computed = m .hexdigest () if not shake else m .hexdigest (length )
535551 self .assertEqual (
536- computed , hexdigest ,
537- "Hash algorithm %s constructed using %s returned hexdigest"
538- " %r for %d byte input data that should have hashed to %r."
539- % (name , hash_object_constructor ,
540- computed , len ( data ), hexdigest ) )
552+ computed , hexdigest ,
553+ "Hash algorithm %s constructed using %s returned hexdigest"
554+ " %r for %d byte input data that should have hashed to %r."
555+ % (name , hash_object_constructor , computed , n , hexdigest )
556+ )
541557 computed = m .digest () if not shake else m .digest (length )
542558 digest = bytes .fromhex (hexdigest )
543559 self .assertEqual (computed , digest )
544560 if not shake :
545561 self .assertEqual (len (digest ), m .digest_size )
546562
563+ def generate_sub_hexdigest (pos = - 1 ):
564+ if pos < 0 :
565+ pos = 0 if n == 0 else random .randrange (0 , n )
566+ pos += 1 # ensure pos is at least 1 (allowed to be out of range)
567+ buf = data [pos :]
568+ objects = make_hash_objects (name , * constructors , buf = buf , ** kwargs )
569+ hexdigests = {obj .hexdigest () for _ , obj in objects }
570+ self .assertEqual (len (hexdigests ), 1 , f"bad digests: { objects } " )
571+ return pos , hexdigests .pop ()
572+
547573 if not shake and kwargs .get ("key" ) is None :
548574 # skip shake and blake2 extended parameter tests
549575 self .check_file_digest (name , data , hexdigest )
576+ for pos in sorted ({- 1 , 1 , n - 1 , n , n + 1 }):
577+ with self .subTest (pos = pos ):
578+ pos , hexdigest2 = generate_sub_hexdigest (pos = pos )
579+ self .check_file_digest (name , data , hexdigest2 , pos )
550580
551- def check_file_digest (self , name , data , hexdigest ):
581+ def check_file_digest (self , name , data , hexdigest , pos = 0 ):
552582 hexdigest = hexdigest .lower ()
553- digests = []
554- for digest in [name , * self .constructors_to_test [name ]]:
555- try :
556- if callable (digest ):
557- digest (b"" )
558- else :
559- hashlib .new (digest )
560- except ValueError :
561- # skip, algorithm is blocked by security policy.
562- continue
563- digests .append (digest )
564-
583+ digests = make_hash_objects (name , * self .constructors_to_test [name ])
565584 with tempfile .TemporaryFile () as f :
566585 f .write (data )
586+ buf = io .BytesIO (data )
567587
568- for digest in digests :
569- buf = io .BytesIO (data )
570- buf .seek (0 )
571- self .assertEqual (
572- hashlib .file_digest (buf , digest ).hexdigest (), hexdigest
573- )
574- f .seek (0 )
575- digestobj = hashlib .file_digest (f , digest )
576- self .assertEqual (digestobj .hexdigest (), hexdigest )
588+ for digest , _ in digests :
589+ for fobj in [buf , f ]:
590+ fobj .seek (pos )
591+ digestobj = hashlib .file_digest (fobj , digest )
592+ self .assertEqual (digestobj .hexdigest (), hexdigest )
577593
578594 def check_no_unicode (self , algorithm_name ):
579595 # Unicode objects are not allowed as input.
0 commit comments