• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 
16 package software.amazon.awssdk.authcrt.signer.internal;
17 
18 import static org.assertj.core.api.Assertions.assertThat;
19 import static org.junit.Assert.assertTrue;
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.Mockito.when;
22 import static software.amazon.awssdk.authcrt.signer.internal.SigningUtils.SIGNING_CLOCK;
23 import static software.amazon.awssdk.authcrt.signer.internal.SigningUtils.buildCredentials;
24 import static software.amazon.awssdk.authcrt.signer.internal.SigningUtils.getSigningClock;
25 
26 import java.io.ByteArrayInputStream;
27 import java.io.ByteArrayOutputStream;
28 import java.net.URI;
29 import java.nio.charset.StandardCharsets;
30 import java.text.ParseException;
31 import java.text.SimpleDateFormat;
32 import java.time.Clock;
33 import java.time.ZoneId;
34 import java.util.Arrays;
35 import java.util.Collections;
36 import java.util.LinkedHashMap;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.TimeZone;
40 import java.util.stream.Collectors;
41 import java.util.stream.Stream;
42 import org.apache.commons.io.IOUtils;
43 import org.junit.Before;
44 import org.junit.Test;
45 import org.junit.runner.RunWith;
46 import org.mockito.Mock;
47 import org.mockito.junit.MockitoJUnitRunner;
48 import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
49 import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute;
50 import software.amazon.awssdk.core.checksums.Algorithm;
51 import software.amazon.awssdk.core.checksums.SdkChecksum;
52 import software.amazon.awssdk.core.internal.chunked.AwsChunkedEncodingConfig;
53 import software.amazon.awssdk.auth.signer.internal.chunkedencoding.AwsSignedChunkedEncodingInputStream;
54 import software.amazon.awssdk.authcrt.signer.internal.chunkedencoding.AwsS3V4aChunkSigner;
55 import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
56 import software.amazon.awssdk.crt.auth.signing.AwsSigningConfig;
57 import software.amazon.awssdk.crt.auth.signing.AwsSigningResult;
58 import software.amazon.awssdk.crt.auth.signing.AwsSigningUtils;
59 import software.amazon.awssdk.http.SdkHttpFullRequest;
60 import software.amazon.awssdk.http.SdkHttpMethod;
61 import software.amazon.awssdk.regions.Region;
62 
63 /**
64  * Functional tests for the S3 specific Sigv4a signer. These tests call the CRT native signer code. Because
65  * Sigv4 Asymmetric does not yield deterministic results, the signatures can only be verified by using
66  * a pre-calculated test case and calling a verification method with information from that test case.
67  */
68 @RunWith(MockitoJUnitRunner.class)
69 public class ChunkedEncodingFunctionalTest {
70 
71     private static final String CHUNKED_SIGV4A_CANONICAL_REQUEST = "PUT\n" +
72                                                                    "/examplebucket/chunkObject.txt\n" +
73                                                                    "\n" +
74                                                                    "content-encoding:aws-chunked\n" +
75                                                                    "content-length:66824\n" +
76                                                                    "host:s3.amazonaws.com\n" +
77                                                                    "x-amz-content-sha256:STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD\n" +
78                                                                    "x-amz-date:20130524T000000Z\n" +
79                                                                    "x-amz-decoded-content-length:66560\n" +
80                                                                    "x-amz-region-set:us-east-1\n" +
81                                                                    "x-amz-storage-class:REDUCED_REDUNDANCY\n" +
82                                                                    "\n" +
83                                                                    "content-encoding;content-length;host;x-amz-content-sha256;x-amz-date;x-amz-decoded-content-length;x-amz-region-set;x-amz-storage-class\n" +
84                                                                    "STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD";
85 
86     private static final String CHUNKED_TRAILER_SIGV4A_CANONICAL_REQUEST = "PUT\n" +
87                                                                            "/examplebucket/chunkObject.txt\n" +
88                                                                            "\n" +
89                                                                            "content-encoding:aws-chunked\n" +
90                                                                            "content-length:66824\n" +
91                                                                            "host:s3.amazonaws.com\n" +
92                                                                            "x-amz-content-sha256:STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD-TRAILER\n" +
93                                                                            "x-amz-date:20130524T000000Z\n" +
94                                                                            "x-amz-decoded-content-length:66560\n" +
95                                                                            "x-amz-region-set:us-east-1\n" +
96                                                                            "x-amz-storage-class:REDUCED_REDUNDANCY\n" +
97                                                                            "x-amz-trailer:first,second,third\n" +
98                                                                            "\n" +
99                                                                            "content-encoding;content-length;host;x-amz-content-sha256;x-amz-date;x-amz-decoded-content-length;x-amz-region-set;x-amz-storage-class;x-amz-trailer\n" +
100                                                                            "STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD-TRAILER";
101 
102 
103 
104 
105     private static final String CHUNKED_ACCESS_KEY_ID = "AKIAIOSFODNN7EXAMPLE";
106     private static final String CHUNKED_SECRET_ACCESS_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
107     private static final String CHUNKED_SIGV4A_TEST_ECC_PUB_X = "18b7d04643359f6ec270dcbab8dce6d169d66ddc9778c75cfb08dfdb701637ab";
108     private static final String CHUNKED_SIGV4A_TEST_ECC_PUB_Y = "fa36b35e4fe67e3112261d2e17a956ef85b06e44712d2850bcd3c2161e9993f2";
109     private static final String CHUNKED_TEST_REGION = "us-east-1";
110     private static final String CHUNKED_TEST_SERVICE = "s3";
111     private static final String CHUNKED_TEST_SIGNING_TIME = "2013-05-24T00:00:00Z";
112 
113     private static final String CHUNK_STS_PRE_SIGNATURE = "AWS4-ECDSA-P256-SHA256-PAYLOAD\n" +
114                                                           "20130524T000000Z\n" +
115                                                           "20130524/s3/aws4_request\n";
116 
117     private static final String CHUNK1_STS_POST_SIGNATURE = "\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n" +
118                                                             "bf718b6f653bebc184e1479f1935b8da974d701b893afcf49e701f3e2f9f9c5a";
119 
120     private static final String CHUNK2_STS_POST_SIGNATURE = "\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n" +
121                                                             "2edc986847e209b4016e141a6dc8716d3207350f416969382d431539bf292e4a";
122 
123     private static final String CHUNK3_STS_POST_SIGNATURE = "\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n" +
124                                                             "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
125     public static final String X_AMZ_TRAILER_SIGNATURE = "x-amz-trailer-signature:";
126 
127     private static String TRAILING_HEADERS_STS_POST_SIGNATURE = "\n83d8f190334fb741bc8daf73c891689d320bd8017756bc730c540021ed48001f";
128 
129     private static String TRAILING_HEADERS_STS_PRE_SIGNATURE = "AWS4-ECDSA-P256-SHA256-TRAILER\n" + "20130524T000000Z\n" +
130                                                                "20130524/s3/aws4_request\n";
131 
132 
133     private static final String CRLF = "\r\n";
134     private static final String SIGNATURE_KEY = "chunk-signature=";
135 
136     private static final int DATA_SIZE = 66560;
137     /**
138      * This content-length is actually incorrect; the correct calculated length should be 67064. However, since
139      * the test case was developed using this number, it must be used when calculating the signatures or else
140      * the test will fail verification. This is also the reason the sigv4a signer cannot be called directly in
141      * a test since it would calculate a different content length.
142      */
143     private static final int TOTAL_CONTENT_LENGTH = 66824;
144     private static final int STREAM_CHUNK_SIZE = 65536;
145     private static final int CHUNK2_SIZE = 1024;
146     private static final byte[] data;
147 
148     private static final SimpleDateFormat DATE_FORMAT;
149 
150     @Mock
151     SigningConfigProvider configProvider;
152 
153     CrtHttpRequestConverter converter;
154     AwsCrt4aSigningAdapter adapter;
155     AwsSigningConfig chunkedRequestSigningConfig;
156     AwsSigningConfig chunkSigningConfig;
157     ExecutionAttributes executionAttributes;
158 
159     static {
160         DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
161         DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
162 
163         data = new byte[DATA_SIZE];
Arrays.fill(data, (byte) 'a')164         Arrays.fill(data, (byte) 'a');
165     }
166 
167     @Before
setup()168     public void setup() throws Exception {
169         converter = new CrtHttpRequestConverter();
170         adapter = new AwsCrt4aSigningAdapter();
171         executionAttributes = buildBasicExecutionAttributes();
172         chunkedRequestSigningConfig = createChunkedRequestSigningConfig(executionAttributes);
173         chunkSigningConfig = createChunkSigningConfig(executionAttributes);
174     }
175 
176     @Test
calling_adapter_APIs_directly_creates_correct_signatures()177     public void calling_adapter_APIs_directly_creates_correct_signatures() {
178         byte[] previousSignature = createAndVerifyRequestSignature(defaultHttpRequest().build());
179 
180         for (int i = 0; i < 3; i++) {
181             byte[] currentSignature = adapter.signChunk(getChunkData(i), previousSignature, chunkSigningConfig);
182             assertTrue(AwsSigningUtils.verifyRawSha256EcdsaSignature(createStringToSign(i, previousSignature),
183                                                                      currentSignature,
184                                                                      CHUNKED_SIGV4A_TEST_ECC_PUB_X,
185                                                                      CHUNKED_SIGV4A_TEST_ECC_PUB_Y));
186             previousSignature = currentSignature;
187         }
188     }
189 
190     @Test
using_a_request_stream_creates_correct_signatures()191     public void using_a_request_stream_creates_correct_signatures() throws Exception {
192         SdkHttpFullRequest request = defaultHttpRequest()
193             .contentStreamProvider(() -> new ByteArrayInputStream(data))
194             .build();
195         byte[] requestSignature = createAndVerifyRequestSignature(request);
196 
197         AwsS3V4aChunkSigner chunkSigner = new AwsS3V4aChunkSigner(adapter, chunkSigningConfig);
198         AwsChunkedEncodingConfig chunkedEncodingConfig = AwsChunkedEncodingConfig.builder()
199                                                                                  .chunkSize(STREAM_CHUNK_SIZE)
200                                                                                  .build();
201 
202         AwsSignedChunkedEncodingInputStream stream =
203 
204             AwsSignedChunkedEncodingInputStream.builder()
205                                                .inputStream(request.contentStreamProvider().get().newStream())
206                                                .awsChunkSigner(chunkSigner)
207                                                .awsChunkedEncodingConfig(chunkedEncodingConfig)
208                                                .headerSignature(new String(requestSignature, StandardCharsets.UTF_8))
209                                                .build();
210 
211         ByteArrayOutputStream output = new ByteArrayOutputStream();
212         IOUtils.copy(stream, output);
213         String result = new String(output.toByteArray(), StandardCharsets.UTF_8);
214 
215         assertChunks(result, 3, requestSignature, false);
216     }
217 
218     @Test
calling_adapter_APIs_directly_creates_correct_signatures_for_trailer_headers()219     public void calling_adapter_APIs_directly_creates_correct_signatures_for_trailer_headers() throws Exception {
220         SdkHttpFullRequest sdkHttpFullRequest = createChunkedTrailerTestRequest().build();
221         chunkedRequestSigningConfig.setAlgorithm(AwsSigningConfig.AwsSigningAlgorithm.SIGV4_ASYMMETRIC);
222         chunkedRequestSigningConfig.setSignedBodyValue(AwsSigningConfig.AwsSignedBodyValue.STREAMING_AWS4_ECDSA_P256_SHA256_PAYLOAD_TRAILER);
223         SdkSigningResult result = adapter.sign(sdkHttpFullRequest, chunkedRequestSigningConfig);
224         byte[] requestSignature = result.getSignature();
225 
226         assertTrue(AwsSigningUtils.verifySigv4aEcdsaSignature(converter.requestToCrt(sdkHttpFullRequest),
227                                                               CHUNKED_TRAILER_SIGV4A_CANONICAL_REQUEST,
228                                                               chunkedRequestSigningConfig, requestSignature,
229                                                               CHUNKED_SIGV4A_TEST_ECC_PUB_X,
230                                                               CHUNKED_SIGV4A_TEST_ECC_PUB_Y));
231         byte[] previousSignature = result.getSignature();
232 
233         for (int i = 0; i < 3; i++) {
234             byte[] currentSignature = adapter.signChunk(getChunkData(i), previousSignature, chunkSigningConfig);
235             assertTrue(AwsSigningUtils.verifyRawSha256EcdsaSignature(createStringToSign(i, previousSignature),
236                                                                      currentSignature,
237                                                                      CHUNKED_SIGV4A_TEST_ECC_PUB_X,
238                                                                      CHUNKED_SIGV4A_TEST_ECC_PUB_Y));
239             previousSignature = currentSignature;
240         }
241         updateTrailerHeaderSigningConfig();
242         Map<String, List<String>> trailerHeader = getTrailerHeaderMap();
243         AwsSigningResult trailingHeadersStringToSignResult = adapter.signTrailerHeaders(trailerHeader, previousSignature,
244                                                                                         chunkedRequestSigningConfig);
245         byte[] trailingHeadersStringToSign = buildTrailingHeadersStringToSign(previousSignature,
246                                                                               TRAILING_HEADERS_STS_POST_SIGNATURE);
247         assertTrue(AwsSigningUtils.verifyRawSha256EcdsaSignature(trailingHeadersStringToSign,
248                                                                  trailingHeadersStringToSignResult.getSignature(),
249                                                                  CHUNKED_SIGV4A_TEST_ECC_PUB_X,
250                                                                  CHUNKED_SIGV4A_TEST_ECC_PUB_Y));
251     }
252 
253 
254     @Test
using_a_request_stream_with_checksum_trailer_creates_correct_signatures()255     public void using_a_request_stream_with_checksum_trailer_creates_correct_signatures() throws Exception {
256         SdkHttpFullRequest request = defaultHttpRequest()
257             .contentStreamProvider(() -> new ByteArrayInputStream(data))
258             .build();
259         byte[] requestSignature = createAndVerifyRequestSignature(request);
260 
261         AwsS3V4aChunkSigner chunkSigner = new AwsS3V4aChunkSigner(adapter, chunkSigningConfig);
262         AwsChunkedEncodingConfig chunkedEncodingConfig = AwsChunkedEncodingConfig.builder()
263                                                                                  .chunkSize(STREAM_CHUNK_SIZE)
264                                                                                  .build();
265 
266         AwsSignedChunkedEncodingInputStream stream =
267             AwsSignedChunkedEncodingInputStream.builder()
268                 .checksumHeaderForTrailer("x-amz-checksum-crc32")
269                 .sdkChecksum(SdkChecksum.forAlgorithm(Algorithm.CRC32))
270                                                .inputStream(request.contentStreamProvider().get().newStream())
271                                                .awsChunkSigner(chunkSigner)
272                                                .awsChunkedEncodingConfig(chunkedEncodingConfig)
273                                                .headerSignature(new String(requestSignature, StandardCharsets.UTF_8))
274                                                .build();
275 
276         ByteArrayOutputStream output = new ByteArrayOutputStream();
277         IOUtils.copy(stream, output);
278         String result = new String(output.toByteArray(), StandardCharsets.UTF_8);
279         assertChunks(result, 3, requestSignature, true);
280     }
281 
getTrailerHeaderMap()282     private Map<String, List<String>> getTrailerHeaderMap() {
283         Map<String, List<String>> trailerHeader = new LinkedHashMap<>();
284         trailerHeader.put("first", Collections.singletonList("1st"));
285         trailerHeader.put("second", Collections.singletonList("2nd"));
286         trailerHeader.put("third", Collections.singletonList("3rd"));
287         return trailerHeader;
288     }
289 
updateTrailerHeaderSigningConfig()290     private void updateTrailerHeaderSigningConfig() {
291         chunkedRequestSigningConfig.setAlgorithm(AwsSigningConfig.AwsSigningAlgorithm.SIGV4_ASYMMETRIC);
292         chunkedRequestSigningConfig.setSignatureType(AwsSigningConfig.AwsSignatureType.HTTP_REQUEST_TRAILING_HEADERS);
293         chunkedRequestSigningConfig.setSignedBodyHeader(AwsSigningConfig.AwsSignedBodyHeaderType.NONE);
294     }
295 
296 
assertChunks(String result, int numExpectedChunks, byte[] requestSignature, boolean isTrailerChecksum)297     private void assertChunks(String result, int numExpectedChunks, byte[] requestSignature, boolean isTrailerChecksum) {
298         List<String> lines = Stream.of(result.split(CRLF)).collect(Collectors.toList());
299         assertThat(lines).hasSize(numExpectedChunks * 2 - 1 + (isTrailerChecksum ? 2 : 0));
300         byte[] previousSignature = requestSignature;
301 
302         int index = 0;
303         for (String line : lines) {
304 
305             int chunk = lines.indexOf(line) / 2;
306             if (lines.indexOf(line) % 2 == 0) {
307                 boolean isFinalTrailerSignature = isTrailerChecksum && index == lines.size() - 1;
308                 String signatureValue = isFinalTrailerSignature ?
309                                         line.substring(line.indexOf(X_AMZ_TRAILER_SIGNATURE)
310                                                        + X_AMZ_TRAILER_SIGNATURE.length()) :
311                                         line.substring(line.indexOf(SIGNATURE_KEY) + SIGNATURE_KEY.length());
312                 byte[] currentSignature = signatureValue.getBytes(StandardCharsets.UTF_8);
313                 assertThat(signatureValue.length()).isEqualTo(AwsS3V4aChunkSigner.getSignatureLength());
314                 if (!isFinalTrailerSignature) {
315                     assertTrue(AwsSigningUtils.verifyRawSha256EcdsaSignature(createStringToSign(chunk, previousSignature),
316                                                                              currentSignature,
317                                                                              CHUNKED_SIGV4A_TEST_ECC_PUB_X,
318                                                                              CHUNKED_SIGV4A_TEST_ECC_PUB_Y));
319                 } else {
320                     assertThat(signatureValue).hasSize(144);
321                 }
322                 previousSignature = currentSignature;
323             }
324             index++;
325         }
326     }
327 
createAndVerifyRequestSignature(SdkHttpFullRequest request)328     private byte[] createAndVerifyRequestSignature(SdkHttpFullRequest request) {
329         SdkSigningResult result = adapter.sign(request, chunkedRequestSigningConfig);
330         byte[] requestSignature = result.getSignature();
331         assertTrue(AwsSigningUtils.verifySigv4aEcdsaSignature(converter.requestToCrt(request),
332                                                               CHUNKED_SIGV4A_CANONICAL_REQUEST,
333                                                               chunkedRequestSigningConfig,
334                                                               requestSignature,
335                                                               CHUNKED_SIGV4A_TEST_ECC_PUB_X,
336                                                               CHUNKED_SIGV4A_TEST_ECC_PUB_Y));
337         return requestSignature;
338     }
339 
buildBasicExecutionAttributes()340     private ExecutionAttributes buildBasicExecutionAttributes() throws ParseException {
341         ExecutionAttributes executionAttributes = new ExecutionAttributes();
342         executionAttributes.putAttribute(SIGNING_CLOCK, Clock.fixed(DATE_FORMAT.parse(CHUNKED_TEST_SIGNING_TIME).toInstant(), ZoneId.systemDefault()));
343         executionAttributes.putAttribute(AwsSignerExecutionAttribute.SERVICE_SIGNING_NAME, CHUNKED_TEST_SERVICE);
344         executionAttributes.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION, Region.of(CHUNKED_TEST_REGION));
345         executionAttributes.putAttribute(AwsSignerExecutionAttribute.AWS_CREDENTIALS,
346                                          AwsBasicCredentials.create(CHUNKED_ACCESS_KEY_ID, CHUNKED_SECRET_ACCESS_KEY));
347         executionAttributes.putAttribute(AwsSignerExecutionAttribute.SIGNER_DOUBLE_URL_ENCODE, false);
348         return executionAttributes;
349     }
350 
createChunkedRequestSigningConfig(ExecutionAttributes executionAttributes)351     private AwsSigningConfig createChunkedRequestSigningConfig(ExecutionAttributes executionAttributes) throws Exception {
352         AwsSigningConfig config = createBasicSigningConfig(executionAttributes);
353 
354         config.setUseDoubleUriEncode(executionAttributes.getAttribute(AwsSignerExecutionAttribute.SIGNER_DOUBLE_URL_ENCODE));
355         config.setShouldNormalizeUriPath(true);
356 
357         config.setSignedBodyHeader(AwsSigningConfig.AwsSignedBodyHeaderType.X_AMZ_CONTENT_SHA256);
358         config.setSignatureType(AwsSigningConfig.AwsSignatureType.HTTP_REQUEST_VIA_HEADERS);
359 
360         config.setSignedBodyValue(AwsSigningConfig.AwsSignedBodyValue.STREAMING_AWS4_ECDSA_P256_SHA256_PAYLOAD);
361 
362         return config;
363     }
364 
createChunkSigningConfig(ExecutionAttributes executionAttributes)365     private AwsSigningConfig createChunkSigningConfig(ExecutionAttributes executionAttributes) {
366         AwsSigningConfig config = createBasicSigningConfig(executionAttributes);
367 
368         config.setSignedBodyHeader(AwsSigningConfig.AwsSignedBodyHeaderType.NONE);
369         config.setSignatureType(AwsSigningConfig.AwsSignatureType.HTTP_REQUEST_CHUNK);
370 
371         return config;
372     }
373 
createBasicSigningConfig(ExecutionAttributes executionAttributes)374     private AwsSigningConfig createBasicSigningConfig(ExecutionAttributes executionAttributes) {
375         AwsSigningConfig config = new AwsSigningConfig();
376         config.setCredentials(buildCredentials(executionAttributes));
377         config.setService(executionAttributes.getAttribute(AwsSignerExecutionAttribute.SERVICE_SIGNING_NAME));
378         config.setRegion(executionAttributes.getAttribute(AwsSignerExecutionAttribute.SIGNING_REGION).id());
379         config.setTime(getSigningClock(executionAttributes).instant().toEpochMilli());
380         config.setAlgorithm(AwsSigningConfig.AwsSigningAlgorithm.SIGV4_ASYMMETRIC);
381         return config;
382     }
383 
defaultHttpRequest()384     private SdkHttpFullRequest.Builder defaultHttpRequest() {
385         return SdkHttpFullRequest.builder()
386                                  .method(SdkHttpMethod.PUT)
387                                  .putHeader("x-amz-storage-class", "REDUCED_REDUNDANCY")
388                                  .putHeader("Content-Encoding", "aws-chunked")
389                                  .uri(URI.create("https://s3.amazonaws.com/examplebucket/chunkObject.txt"))
390                                  .putHeader("x-amz-decoded-content-length", Integer.toString(DATA_SIZE))
391                                  .putHeader("Content-Length", Integer.toString(TOTAL_CONTENT_LENGTH));
392     }
393 
createChunkedTrailerTestRequest()394     private SdkHttpFullRequest.Builder createChunkedTrailerTestRequest(){
395         return defaultHttpRequest().putHeader("x-amz-trailer", "first,second,third");
396     }
397 
getChunkData(int chunk)398     private byte[] getChunkData(int chunk) {
399         switch(chunk) {
400             case 0: return Arrays.copyOfRange(data, 0, STREAM_CHUNK_SIZE);
401             case 1: return Arrays.copyOfRange(data, STREAM_CHUNK_SIZE, STREAM_CHUNK_SIZE + CHUNK2_SIZE);
402             default: return new byte[0];
403         }
404     }
405 
createStringToSign(int chunk, byte[] previousSignature)406     private byte[] createStringToSign(int chunk, byte[] previousSignature) {
407         switch(chunk) {
408             case 0: return buildChunkStringToSign(previousSignature, CHUNK1_STS_POST_SIGNATURE);
409             case 1: return buildChunkStringToSign(previousSignature, CHUNK2_STS_POST_SIGNATURE);
410             default: return buildChunkStringToSign(previousSignature, CHUNK3_STS_POST_SIGNATURE);
411         }
412     }
413 
buildChunkStringToSign(byte[] previousSignature, String stsPostSignature)414     private byte[] buildChunkStringToSign(byte[] previousSignature, String stsPostSignature) {
415         StringBuilder stsBuilder = new StringBuilder();
416 
417         stsBuilder.append(CHUNK_STS_PRE_SIGNATURE);
418         String signature = new String(previousSignature, StandardCharsets.UTF_8);
419         int paddingIndex = signature.indexOf('*');
420         if (paddingIndex != -1) {
421             signature = signature.substring(0, paddingIndex);
422         }
423         stsBuilder.append(signature);
424         stsBuilder.append(stsPostSignature);
425 
426         return stsBuilder.toString().getBytes(StandardCharsets.UTF_8);
427     }
buildTrailingHeadersStringToSign(byte[] previousSignature, String stsPostSignature)428     private byte[] buildTrailingHeadersStringToSign(byte[] previousSignature, String stsPostSignature) {
429         StringBuilder stsBuilder = new StringBuilder();
430 
431         stsBuilder.append(TRAILING_HEADERS_STS_PRE_SIGNATURE);
432         String signature = new String(previousSignature, StandardCharsets.UTF_8);
433         int paddingIndex = signature.indexOf('*');
434         if (paddingIndex != -1) {
435             signature = signature.substring(0, paddingIndex);
436         }
437         stsBuilder.append(signature);
438         stsBuilder.append(stsPostSignature);
439 
440         return stsBuilder.toString().getBytes(StandardCharsets.UTF_8);
441     }
442 
443 }
444