@@ -32,7 +32,10 @@ use crate::client::s3::{
3232 CompleteMultipartUpload , CompleteMultipartUploadResult , CopyPartResult ,
3333 InitiateMultipartUploadResult , ListResponse , PartMetadata ,
3434} ;
35- use crate :: client:: { GetOptionsExt , HttpClient , HttpError , HttpResponse } ;
35+ use crate :: client:: {
36+ CryptoProvider , DigestAlgorithm , GetOptionsExt , HttpClient , HttpError , HttpResponse ,
37+ crypto_provider,
38+ } ;
3639use crate :: list:: { PaginatedListOptions , PaginatedListResult } ;
3740use crate :: multipart:: PartId ;
3841use crate :: {
@@ -52,8 +55,6 @@ use itertools::Itertools;
5255use md5:: { Digest , Md5 } ;
5356use percent_encoding:: { PercentEncode , utf8_percent_encode} ;
5457use quick_xml:: events:: { self as xml_events} ;
55- use ring:: digest;
56- use ring:: digest:: Context ;
5758use serde:: { Deserialize , Serialize } ;
5859use std:: sync:: Arc ;
5960
@@ -198,6 +199,7 @@ pub(crate) struct S3Config {
198199 pub bucket : String ,
199200 pub bucket_endpoint : String ,
200201 pub credentials : AwsCredentialProvider ,
202+ pub crypto : Option < Arc < dyn CryptoProvider > > ,
201203 pub session_provider : Option < AwsCredentialProvider > ,
202204 pub retry_config : RetryConfig ,
203205 pub client_options : ClientOptions ,
@@ -216,19 +218,18 @@ impl S3Config {
216218 format ! ( "{}/{}" , self . bucket_endpoint, encode_path( path) )
217219 }
218220
219- async fn get_session_credential ( & self ) -> Result < SessionCredential < ' _ > > {
220- let credential = match self . skip_signature {
221+ async fn get_session_credential ( & self ) -> Result < Option < SessionCredential < ' _ > > > {
222+ Ok ( match self . skip_signature {
221223 false => {
222224 let provider = self . session_provider . as_ref ( ) . unwrap_or ( & self . credentials ) ;
223- Some ( provider. get_credential ( ) . await ?)
225+ let credential = provider. get_credential ( ) . await ?;
226+ Some ( SessionCredential {
227+ credential,
228+ session_token : self . session_provider . is_some ( ) ,
229+ config : self ,
230+ } )
224231 }
225232 true => None ,
226- } ;
227-
228- Ok ( SessionCredential {
229- credential,
230- session_token : self . session_provider . is_some ( ) ,
231- config : self ,
232233 } )
233234 }
234235
@@ -243,27 +244,32 @@ impl S3Config {
243244 pub ( crate ) fn is_s3_express ( & self ) -> bool {
244245 self . session_provider . is_some ( )
245246 }
247+
248+ pub ( crate ) fn crypto ( & self ) -> Result < & dyn CryptoProvider > {
249+ crypto_provider ( self . crypto . as_deref ( ) )
250+ }
246251}
247252
248253struct SessionCredential < ' a > {
249- credential : Option < Arc < AwsCredential > > ,
254+ credential : Arc < AwsCredential > ,
250255 session_token : bool ,
251256 config : & ' a S3Config ,
252257}
253258
254259impl SessionCredential < ' _ > {
255- fn authorizer ( & self ) -> Option < AwsAuthorizer < ' _ > > {
260+ fn authorizer ( & self ) -> Result < AwsAuthorizer < ' _ > > {
256261 let mut authorizer =
257- AwsAuthorizer :: new ( self . credential . as_deref ( ) ? , "s3" , & self . config . region )
262+ AwsAuthorizer :: new ( self . credential . as_ref ( ) , "s3" , & self . config . region )
258263 . with_sign_payload ( self . config . sign_payload )
259- . with_request_payer ( self . config . request_payer ) ;
264+ . with_request_payer ( self . config . request_payer )
265+ . with_crypto ( self . config . crypto ( ) ?) ;
260266
261267 if self . session_token {
262268 let token = HeaderName :: from_static ( "x-amz-s3session-token" ) ;
263269 authorizer = authorizer. with_token_header ( token)
264270 }
265271
266- Some ( authorizer)
272+ Ok ( authorizer)
267273 }
268274}
269275
@@ -296,7 +302,7 @@ pub(crate) struct Request<'a> {
296302 path : & ' a Path ,
297303 config : & ' a S3Config ,
298304 builder : HttpRequestBuilder ,
299- payload_sha256 : Option < digest :: Digest > ,
305+ payload_sha256 : Option < [ u8 ; 32 ] > ,
300306 payload : Option < PutPayload > ,
301307 use_session_creds : bool ,
302308 idempotent : bool ,
@@ -397,13 +403,13 @@ impl Request<'_> {
397403 Self { builder, ..self }
398404 }
399405
400- pub ( crate ) fn with_payload ( mut self , payload : PutPayload ) -> Self {
406+ pub ( crate ) fn with_payload ( mut self , payload : PutPayload ) -> Result < Self > {
401407 if ( !self . config . skip_signature && self . config . sign_payload )
402408 || self . config . checksum . is_some ( )
403409 {
404- let mut sha256 = Context :: new ( & digest:: SHA256 ) ;
405- payload. iter ( ) . for_each ( |x| sha256 . update ( x) ) ;
406- let payload_sha256 = sha256 . finish ( ) ;
410+ let mut ctx = self . config . crypto ( ) ? . digest ( DigestAlgorithm :: Sha256 ) ? ;
411+ payload. iter ( ) . for_each ( |x| ctx . update ( x) ) ;
412+ let payload_sha256 = ctx . finish ( ) ? . try_into ( ) . unwrap ( ) ;
407413
408414 if let Some ( Checksum :: SHA256 ) = self . config . checksum {
409415 self . builder = self
@@ -416,24 +422,28 @@ impl Request<'_> {
416422 let content_length = payload. content_length ( ) ;
417423 self . builder = self . builder . header ( CONTENT_LENGTH , content_length) ;
418424 self . payload = Some ( payload) ;
419- self
425+ Ok ( self )
420426 }
421427
422428 pub ( crate ) async fn send ( self ) -> Result < HttpResponse , RequestError > {
423429 let credential = match self . use_session_creds {
424430 true => self . config . get_session_credential ( ) . await ?,
425- false => SessionCredential {
426- credential : self . config . get_credential ( ) . await ?,
427- session_token : false ,
428- config : self . config ,
429- } ,
431+ false => {
432+ let credential = self . config . get_credential ( ) . await ?;
433+ credential. map ( |credential| SessionCredential {
434+ credential,
435+ session_token : false ,
436+ config : self . config ,
437+ } )
438+ }
430439 } ;
440+ let authorizer = credential. as_ref ( ) . map ( |x| x. authorizer ( ) ) . transpose ( ) ?;
431441
432442 let sha = self . payload_sha256 . as_ref ( ) . map ( |x| x. as_ref ( ) ) ;
433443
434444 let path = self . path . as_ref ( ) ;
435445 self . builder
436- . with_aws_sigv4 ( credential . authorizer ( ) , sha)
446+ . with_aws_sigv4 ( authorizer, sha) ?
437447 . retryable ( & self . config . retry_config )
438448 . retry_on_conflict ( self . retry_on_conflict )
439449 . idempotent ( self . idempotent )
@@ -493,6 +503,7 @@ impl S3Client {
493503 }
494504
495505 let credential = self . config . get_session_credential ( ) . await ?;
506+ let authorizer = credential. as_ref ( ) . map ( |x| x. authorizer ( ) ) . transpose ( ) ?;
496507 let url = format ! ( "{}?delete" , self . config. bucket_endpoint) ;
497508
498509 let mut buffer = Vec :: new ( ) ;
@@ -536,7 +547,11 @@ impl S3Client {
536547
537548 let mut builder = self . client . request ( Method :: POST , url) ;
538549
539- let digest = digest:: digest ( & digest:: SHA256 , & body) ;
550+ let crypto = self . config . crypto ( ) ?;
551+ let mut ctx = crypto. digest ( DigestAlgorithm :: Sha256 ) ?;
552+ ctx. update ( body. as_ref ( ) ) ;
553+ let digest = ctx. finish ( ) ?;
554+
540555 builder = builder. header ( SHA256_CHECKSUM , BASE64_STANDARD . encode ( digest) ) ;
541556
542557 // S3 *requires* DeleteObjects to include a Content-MD5 header:
@@ -550,7 +565,7 @@ impl S3Client {
550565 let response = builder
551566 . header ( CONTENT_TYPE , "application/xml" )
552567 . body ( body)
553- . with_aws_sigv4 ( credential . authorizer ( ) , Some ( digest. as_ref ( ) ) )
568+ . with_aws_sigv4 ( authorizer, Some ( digest) ) ?
554569 . send_retry ( & self . config . retry_config )
555570 . await
556571 . map_err ( |source| Error :: DeleteObjectsRequest {
@@ -690,7 +705,7 @@ impl S3Client {
690705 . idempotent ( true ) ;
691706
692707 request = match data {
693- PutPartPayload :: Part ( payload) => request. with_payload ( payload) ,
708+ PutPartPayload :: Part ( payload) => request. with_payload ( payload) ? ,
694709 PutPartPayload :: Copy ( path) => request. header (
695710 "x-amz-copy-source" ,
696711 & format ! ( "{}/{}" , self . config. bucket, encode_path( path) ) ,
@@ -775,14 +790,15 @@ impl S3Client {
775790 let body = quick_xml:: se:: to_string ( & request) . unwrap ( ) ;
776791
777792 let credential = self . config . get_session_credential ( ) . await ?;
793+ let authorizer = credential. as_ref ( ) . map ( |x| x. authorizer ( ) ) . transpose ( ) ?;
778794 let url = self . config . path_url ( location) ;
779795
780796 let request = self
781797 . client
782798 . post ( url)
783799 . query ( & [ ( "uploadId" , upload_id) ] )
784800 . body ( body)
785- . with_aws_sigv4 ( credential . authorizer ( ) , None ) ;
801+ . with_aws_sigv4 ( authorizer, None ) ? ;
786802
787803 let request = match mode {
788804 CompleteMultipartMode :: Overwrite => request,
@@ -821,11 +837,12 @@ impl S3Client {
821837 #[ cfg( test) ]
822838 pub ( crate ) async fn get_object_tagging ( & self , path : & Path ) -> Result < HttpResponse > {
823839 let credential = self . config . get_session_credential ( ) . await ?;
840+ let authorizer = credential. as_ref ( ) . map ( |x| x. authorizer ( ) ) . transpose ( ) ?;
824841 let url = format ! ( "{}?tagging" , self . config. path_url( path) ) ;
825842 let response = self
826843 . client
827844 . request ( Method :: GET , url)
828- . with_aws_sigv4 ( credential . authorizer ( ) , None )
845+ . with_aws_sigv4 ( authorizer, None ) ?
829846 . send_retry ( & self . config . retry_config )
830847 . await
831848 . map_err ( |e| e. error ( STORE , path. to_string ( ) ) ) ?;
@@ -856,6 +873,7 @@ impl GetClient for S3Client {
856873 options : GetOptions ,
857874 ) -> Result < HttpResponse > {
858875 let credential = self . config . get_session_credential ( ) . await ?;
876+ let authorizer = credential. as_ref ( ) . map ( |x| x. authorizer ( ) ) . transpose ( ) ?;
859877 let url = self . config . path_url ( path) ;
860878 let method = match options. head {
861879 true => Method :: HEAD ,
@@ -878,7 +896,7 @@ impl GetClient for S3Client {
878896
879897 let response = builder
880898 . with_get_options ( options)
881- . with_aws_sigv4 ( credential . authorizer ( ) , None )
899+ . with_aws_sigv4 ( authorizer, None ) ?
882900 . retryable_request ( )
883901 . send ( ctx)
884902 . await
@@ -897,6 +915,7 @@ impl ListClient for Arc<S3Client> {
897915 opts : PaginatedListOptions ,
898916 ) -> Result < PaginatedListResult > {
899917 let credential = self . config . get_session_credential ( ) . await ?;
918+ let authorizer = credential. as_ref ( ) . map ( |x| x. authorizer ( ) ) . transpose ( ) ?;
900919 let url = self . config . bucket_endpoint . clone ( ) ;
901920
902921 let mut query = Vec :: with_capacity ( 4 ) ;
@@ -930,7 +949,7 @@ impl ListClient for Arc<S3Client> {
930949 . request ( Method :: GET , & url)
931950 . extensions ( opts. extensions )
932951 . query ( & query)
933- . with_aws_sigv4 ( credential . authorizer ( ) , None )
952+ . with_aws_sigv4 ( authorizer, None ) ?
934953 . send_retry ( & self . config . retry_config )
935954 . await
936955 . map_err ( |source| Error :: ListRequest { source } ) ?
@@ -1000,6 +1019,7 @@ mod tests {
10001019 conditional_put : Default :: default ( ) ,
10011020 encryption_headers : Default :: default ( ) ,
10021021 request_payer : false ,
1022+ crypto : None ,
10031023 } ;
10041024
10051025 let client = S3Client :: new ( config, HttpClient :: new ( reqwest:: Client :: new ( ) ) ) ;
0 commit comments