2323import io
2424import logging
2525from binascii import crc32
26- from hashlib import sha1 , sha256
26+ from hashlib import sha1 , sha256 , sha512
2727
2828from awscrt import checksums as crt_checksums
2929from botocore .compat import urlparse
3030from botocore .exceptions import AwsChunkedWrapperError , FlexibleChecksumError
3131from botocore .model import StructureShape
3232from botocore .response import StreamingBody
3333from botocore .useragent import register_feature_id
34- from botocore .utils import determine_content_length , has_checksum_header
34+ from botocore .utils import (
35+ determine_content_length ,
36+ get_checksum_algorithm_headers ,
37+ has_checksum_header ,
38+ )
3539
3640logger = logging .getLogger (__name__ )
3741
@@ -115,6 +119,42 @@ def digest(self):
115119 return self ._int_crc64nvme .to_bytes (8 , byteorder = "big" )
116120
117121
122+ class CrtXxhash64Checksum (BaseChecksum ):
123+ # Note: This class is only used if the CRT is available
124+ def __init__ (self ):
125+ self ._xxhash = crt_checksums .XXHash .new_xxhash64 ()
126+
127+ def update (self , chunk ):
128+ self ._xxhash .update (chunk )
129+
130+ def digest (self ):
131+ return self ._xxhash .finalize ()
132+
133+
134+ class CrtXxhash3Checksum (BaseChecksum ):
135+ # Note: This class is only used if the CRT is available
136+ def __init__ (self ):
137+ self ._xxhash = crt_checksums .XXHash .new_xxhash3_64 ()
138+
139+ def update (self , chunk ):
140+ self ._xxhash .update (chunk )
141+
142+ def digest (self ):
143+ return self ._xxhash .finalize ()
144+
145+
146+ class CrtXxhash128Checksum (BaseChecksum ):
147+ # Note: This class is only used if the CRT is available
148+ def __init__ (self ):
149+ self ._xxhash = crt_checksums .XXHash .new_xxhash3_128 ()
150+
151+ def update (self , chunk ):
152+ self ._xxhash .update (chunk )
153+
154+ def digest (self ):
155+ return self ._xxhash .finalize ()
156+
157+
118158class Sha1Checksum (BaseChecksum ):
119159 def __init__ (self ):
120160 self ._checksum = sha1 ()
@@ -137,6 +177,17 @@ def digest(self):
137177 return self ._checksum .digest ()
138178
139179
180+ class Sha512Checksum (BaseChecksum ):
181+ def __init__ (self ):
182+ self ._checksum = sha512 ()
183+
184+ def update (self , chunk ):
185+ self ._checksum .update (chunk )
186+
187+ def digest (self ):
188+ return self ._checksum .digest ()
189+
190+
140191class AwsChunkedWrapper :
141192 _DEFAULT_CHUNK_SIZE = 1024 * 1024
142193
@@ -241,6 +292,7 @@ def _validate_checksum(self):
241292def resolve_checksum_context (request , operation_model , params ):
242293 resolve_request_checksum_algorithm (request , operation_model , params )
243294 resolve_response_checksum_algorithms (request , operation_model , params )
295+ _register_checksum_feature_ids (request )
244296
245297
246298def resolve_request_checksum_algorithm (
@@ -361,7 +413,6 @@ def _apply_request_header_checksum(request):
361413 checksum_cls = _CHECKSUM_CLS .get (algorithm ["algorithm" ])
362414 digest = checksum_cls ().handle (request ["body" ])
363415 request ["headers" ][location_name ] = digest
364- _register_checksum_algorithm_feature_id (algorithm )
365416
366417
367418def _apply_request_trailer_checksum (request ):
@@ -385,7 +436,6 @@ def _apply_request_trailer_checksum(request):
385436 else :
386437 headers ["Content-Encoding" ] = "aws-chunked"
387438 headers ["X-Amz-Trailer" ] = location_name
388- _register_checksum_algorithm_feature_id (algorithm )
389439
390440 content_length = determine_content_length (body )
391441 if content_length is not None :
@@ -409,8 +459,29 @@ def _apply_request_trailer_checksum(request):
409459 )
410460
411461
462+ def _register_checksum_feature_ids (request ):
463+ """Register feature IDs for checksum algorithms used in the request."""
464+ if algorithm_headers := get_checksum_algorithm_headers (request ):
465+ for header in algorithm_headers :
466+ header = header .upper ()
467+ if header not in (
468+ "X-AMZ-CHECKSUM-ALGORITHM" ,
469+ "X-AMZ-CHECKSUM-MODE" ,
470+ "X-AMZ-CHECKSUM-TYPE" ,
471+ ):
472+ algorithm_name = header .removeprefix ("X-AMZ-CHECKSUM-" )
473+ _register_checksum_algorithm_feature_id (algorithm_name )
474+ return
475+ # If no checksum header exists yet, check the resolved context for
476+ # an algorithm that will be applied later by apply_request_checksum.
477+ checksum_context = request .get ("context" , {}).get ("checksum" , {})
478+ algorithm = checksum_context .get ("request_algorithm" )
479+ if algorithm and isinstance (algorithm , dict ):
480+ _register_checksum_algorithm_feature_id (algorithm ["algorithm" ])
481+
482+
412483def _register_checksum_algorithm_feature_id (algorithm ):
413- checksum_algorithm_name = algorithm [ "algorithm" ] .upper ()
484+ checksum_algorithm_name = algorithm .upper ()
414485 if checksum_algorithm_name == "CRC64NVME" :
415486 checksum_algorithm_name = "CRC64"
416487 checksum_algorithm_name_feature_id = (
@@ -514,8 +585,22 @@ def _handle_bytes_response(http_response, response, algorithm):
514585 "crc32" : CrtCrc32Checksum ,
515586 "sha1" : Sha1Checksum ,
516587 "sha256" : Sha256Checksum ,
588+ 'sha512' : Sha512Checksum ,
589+ 'xxhash64' : CrtXxhash64Checksum ,
590+ 'xxhash3' : CrtXxhash3Checksum ,
591+ 'xxhash128' : CrtXxhash128Checksum ,
517592}
518593
519594
520595_SUPPORTED_CHECKSUM_ALGORITHMS = list (_CHECKSUM_CLS .keys ())
521- _ALGORITHMS_PRIORITY_LIST = ['crc64nvme' , 'crc32c' , 'crc32' , 'sha1' , 'sha256' ]
596+ _ALGORITHMS_PRIORITY_LIST = [
597+ 'xxhash128' ,
598+ 'xxhash3' ,
599+ 'crc64nvme' ,
600+ 'xxhash64' ,
601+ 'crc32c' ,
602+ 'crc32' ,
603+ 'sha1' ,
604+ 'sha256' ,
605+ 'sha512' ,
606+ ]
0 commit comments