@@ -49,6 +49,12 @@ sign_v4(AccessKeyID, SecretAccessKey, Region, Service, DateTime, Method, URL, He
49
49
% % this results in each segment being URI-encoded twice, as expected
50
50
% % by AWS. Defaults to `true'.
51
51
% % </dd>
52
+ % % <dt>`body_digest'</dt>
53
+ % % <dd>
54
+ % % Optional SHA256 digest of the request body. This option can be used to provide
55
+ % % a fixed digest value, such as "UNSIGNED-PAYLOAD", when sending requests without
56
+ % % signing the body.
57
+ % % </dd>
52
58
% % </dl>
53
59
-spec sign_v4 (AccessKeyID , SecretAccessKey , Region , Service , DateTime , Method , URL , Headers , Body , Options ) -> FinalHeaders
54
60
when AccessKeyID :: binary (),
@@ -61,7 +67,9 @@ sign_v4(AccessKeyID, SecretAccessKey, Region, Service, DateTime, Method, URL, He
61
67
Headers :: headers (),
62
68
Body :: binary (),
63
69
Options :: [Option ],
64
- Option :: {uri_encode_path , boolean ()},
70
+ Option ::
71
+ {uri_encode_path , boolean ()}
72
+ | {body_digest , binary ()},
65
73
FinalHeaders :: headers ().
66
74
sign_v4 (AccessKeyID , SecretAccessKey , Region , Service , DateTime , Method , URL , Headers , Body , Options )
67
75
when is_binary (AccessKeyID ),
@@ -80,9 +88,16 @@ sign_v4(AccessKeyID, SecretAccessKey, Region, Service, DateTime, Method, URL, He
80
88
LongDate = format_datetime_long (DateTime ),
81
89
ShortDate = format_datetime_short (DateTime ),
82
90
FinalHeaders0 = add_date_header (Headers , LongDate ),
83
- FinalHeaders = add_content_hash_header (FinalHeaders0 , Body ),
84
91
85
- BodyDigest = aws_signature_utils :sha256_hexdigest (Body ),
92
+ BodyDigest =
93
+ case proplists :get_value (body_digest , Options , undefined ) of
94
+ undefined ->
95
+ aws_signature_utils :sha256_hexdigest (Body );
96
+ Digest ->
97
+ Digest
98
+ end ,
99
+
100
+ FinalHeaders = add_content_hash_header (FinalHeaders0 , BodyDigest ),
86
101
CanonicalRequest = canonical_request (Method , URLMap , FinalHeaders , BodyDigest , URIEncodePath ),
87
102
HashedCanonicalRequest = aws_signature_utils :sha256_hexdigest (CanonicalRequest ),
88
103
CredentialScope = credential_scope (ShortDate , Region , Service ),
@@ -344,9 +359,8 @@ build_final_url_with_signature(OriginalURL, URLMap, QueryParams, Signature) ->
344
359
% % This header is required for S3 when using the v4 signature. Adding it
345
360
% % in requests for all services does not cause any issues.
346
361
-spec add_content_hash_header (headers (), binary ()) -> headers ().
347
- add_content_hash_header (Headers , Body ) ->
348
- HashedBody = aws_signature_utils :sha256_hexdigest (Body ),
349
- [{<<" X-Amz-Content-SHA256" >>, HashedBody } | Headers ].
362
+ add_content_hash_header (Headers , BodyDigest ) ->
363
+ [{<<" X-Amz-Content-SHA256" >>, BodyDigest } | Headers ].
350
364
351
365
% % Generates an AWS4-HMAC-SHA256 authorization signature.
352
366
-spec authorization (binary (), binary (), binary (), binary ()) -> binary ().
@@ -624,6 +638,27 @@ sign_v4_reference_example_4_test() ->
624
638
625
639
? assertEqual (Actual , Expected ).
626
640
641
+ sign_v4_unsigned_payload_test () ->
642
+ AccessKeyID = <<" AKIAIOSFODNN7EXAMPLE" >>,
643
+ SecretAccessKey = <<" wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" >>,
644
+ Region = <<" us-east-1" >>,
645
+ Service = <<" s3" >>,
646
+ DateTime = {{2013 , 5 , 24 }, {0 , 0 , 0 }},
647
+ Method = <<" GET" >>,
648
+ URL = <<" https://examplebucket.s3.amazonaws.com?max-keys=2&prefix=J" >>,
649
+ Headers = [{<<" Host" >>, <<" examplebucket.s3.amazonaws.com" >>}],
650
+ Body = <<" foo" >>,
651
+
652
+ Actual = sign_v4 (AccessKeyID , SecretAccessKey , Region , Service , DateTime , Method , URL , Headers , Body , [{body_digest , <<" UNSIGNED-PAYLOAD" >>}]),
653
+
654
+ Expected = [
655
+ {<<" Authorization" >>, <<" AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=b1a076428fa68c2c42202ee5a5718b8207f725e451e2157d6b1c393e01fc2e68" >>},
656
+ {<<" X-Amz-Content-SHA256" >>, <<" UNSIGNED-PAYLOAD" >>},
657
+ {<<" X-Amz-Date" >>, <<" 20130524T000000Z" >>},
658
+ {<<" Host" >>, <<" examplebucket.s3.amazonaws.com" >>}],
659
+
660
+ ? assertEqual (Actual , Expected ).
661
+
627
662
sign_v4_event_test () ->
628
663
SecretAccessKey = <<" wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" >>,
629
664
Region = <<" us-east-1" >>,
0 commit comments