All Downloads are FREE. Search and download functionalities are using the official Maven repository.

software.amazon.awssdk.authcrt.signer.internal.DefaultAwsCrtS3V4aSigner Maven / Gradle / Ivy

/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

package software.amazon.awssdk.authcrt.signer.internal;

import static software.amazon.awssdk.auth.signer.internal.AbstractAwsS3V4Signer.STREAMING_UNSIGNED_PAYLOAD_TRAILER;
import static software.amazon.awssdk.auth.signer.internal.Aws4SignerUtils.calculateRequestContentLength;
import static software.amazon.awssdk.http.Header.CONTENT_LENGTH;

import java.nio.charset.StandardCharsets;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.auth.credentials.CredentialUtils;
import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute;
import software.amazon.awssdk.auth.signer.S3SignerExecutionAttribute;
import software.amazon.awssdk.auth.signer.internal.chunkedencoding.AwsSignedChunkedEncodingInputStream;
import software.amazon.awssdk.auth.signer.params.SignerChecksumParams;
import software.amazon.awssdk.authcrt.signer.AwsCrtS3V4aSigner;
import software.amazon.awssdk.authcrt.signer.internal.chunkedencoding.AwsS3V4aChunkSigner;
import software.amazon.awssdk.core.checksums.ChecksumSpecs;
import software.amazon.awssdk.core.checksums.SdkChecksum;
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
import software.amazon.awssdk.core.internal.chunked.AwsChunkedEncodingConfig;
import software.amazon.awssdk.core.internal.util.HttpChecksumUtils;
import software.amazon.awssdk.crt.auth.signing.AwsSigningConfig;
import software.amazon.awssdk.http.ContentStreamProvider;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.regions.RegionScope;

@SdkInternalApi
public final class DefaultAwsCrtS3V4aSigner implements AwsCrtS3V4aSigner {

    private final AwsCrt4aSigningAdapter signerAdapter;
    private final SigningConfigProvider configProvider;
    private final RegionScope defaultRegionScope;

    DefaultAwsCrtS3V4aSigner(AwsCrt4aSigningAdapter signerAdapter, SigningConfigProvider signingConfigProvider) {
        this(signerAdapter, signingConfigProvider, null);
    }

    DefaultAwsCrtS3V4aSigner(AwsCrt4aSigningAdapter signerAdapter, SigningConfigProvider signingConfigProvider,
                             RegionScope defaultRegionScope) {
        this.signerAdapter = signerAdapter;
        this.configProvider = signingConfigProvider;
        this.defaultRegionScope = defaultRegionScope;
    }

    private DefaultAwsCrtS3V4aSigner(BuilderImpl builder) {
        this(new AwsCrt4aSigningAdapter(), new SigningConfigProvider(), builder.defaultRegionScope);
    }

    public static AwsCrtS3V4aSigner create() {
        return builder().build();
    }

    public static Builder builder() {
        return new BuilderImpl();
    }

    @Override
    public SdkHttpFullRequest sign(SdkHttpFullRequest request, ExecutionAttributes executionAttributes) {
        if (credentialsAreAnonymous(executionAttributes)) {
            return request;
        }
        ExecutionAttributes defaultsApplied = applyDefaults(executionAttributes);
        AwsSigningConfig requestSigningConfig = configProvider.createS3CrtSigningConfig(defaultsApplied);
        SignerChecksumParams signerChecksumParams = signerChecksumParamsFromAttributes(defaultsApplied);

        if (shouldSignPayload(request, defaultsApplied)) {
            SdkHttpFullRequest.Builder mutableRequest = request.toBuilder();
            if (signerChecksumParams != null) {
                requestSigningConfig.setSignedBodyValue(
                    AwsSigningConfig.AwsSignedBodyValue.STREAMING_AWS4_ECDSA_P256_SHA256_PAYLOAD_TRAILER);
                updateRequestWithTrailer(signerChecksumParams, mutableRequest);
            } else {
                requestSigningConfig.setSignedBodyValue(
                    AwsSigningConfig.AwsSignedBodyValue.STREAMING_AWS4_ECDSA_P256_SHA256_PAYLOAD);
            }
            setHeaderContentLength(mutableRequest, signerChecksumParams);
            SdkSigningResult signingResult = signerAdapter.sign(mutableRequest.build(), requestSigningConfig);
            AwsSigningConfig chunkConfig = configProvider.createChunkedSigningConfig(defaultsApplied);
            return enablePayloadSigning(signingResult, chunkConfig, signerChecksumParams);
        } else {
            requestSigningConfig.setSignedBodyValue(signerChecksumParams != null
                                                    ? STREAMING_UNSIGNED_PAYLOAD_TRAILER
                                                    : AwsSigningConfig.AwsSignedBodyValue.UNSIGNED_PAYLOAD);
            return signerAdapter.signRequest(request, requestSigningConfig);
        }
    }

    private static SignerChecksumParams signerChecksumParamsFromAttributes(ExecutionAttributes executionAttributes) {
        ChecksumSpecs checksumSpecs = HttpChecksumUtils.checksumSpecWithRequestAlgorithm(executionAttributes).orElse(null);
        if (checksumSpecs == null) {
            return null;
        }
        return SignerChecksumParams.builder()
                                   .isStreamingRequest(checksumSpecs.isRequestStreaming())
                                   .algorithm(checksumSpecs.algorithm())
                                   .checksumHeaderName(checksumSpecs.headerName()).build();
    }

    @Override
    public SdkHttpFullRequest presign(SdkHttpFullRequest request, ExecutionAttributes executionAttributes) {
        if (credentialsAreAnonymous(executionAttributes)) {
            return request;
        }
        ExecutionAttributes defaultsApplied = applyDefaults(executionAttributes);
        return signerAdapter.signRequest(request, configProvider.createS3CrtPresigningConfig(defaultsApplied));
    }

    private boolean credentialsAreAnonymous(ExecutionAttributes executionAttributes) {
        return CredentialUtils.isAnonymous(executionAttributes.getAttribute(AwsSignerExecutionAttribute.AWS_CREDENTIALS));
    }

    private boolean shouldSignPayload(SdkHttpFullRequest request, ExecutionAttributes executionAttributes) {
        if (!request.protocol().equals("https") && request.contentStreamProvider().isPresent()) {
            return true;
        }
        boolean payloadSigning =
            booleanValue(executionAttributes.getAttribute(S3SignerExecutionAttribute.ENABLE_PAYLOAD_SIGNING));
        boolean chunkedEncoding =
            booleanValue(executionAttributes.getAttribute(S3SignerExecutionAttribute.ENABLE_CHUNKED_ENCODING));

        return payloadSigning && chunkedEncoding;
    }

    private void setHeaderContentLength(SdkHttpFullRequest.Builder mutableRequest, SignerChecksumParams signerChecksumParams) {
        long originalContentLength = calculateRequestContentLength(mutableRequest);
        mutableRequest.putHeader("x-amz-decoded-content-length", Long.toString(originalContentLength));

        String totalLength = Long.toString(
            AwsSignedChunkedEncodingInputStream.calculateStreamContentLength(originalContentLength,
                                                                             AwsS3V4aChunkSigner.getSignatureLength(),
                                                                             AwsChunkedEncodingConfig.create(),
                                                                             signerChecksumParams != null)
            + getChecksumTrailerLength(signerChecksumParams, AwsS3V4aChunkSigner.getSignatureLength()));

        mutableRequest.putHeader(CONTENT_LENGTH, totalLength);

    }

    private SdkHttpFullRequest enablePayloadSigning(SdkSigningResult signingResult, AwsSigningConfig chunkConfig,
                                                    SignerChecksumParams signerChecksumParams) {
        SdkHttpFullRequest signedRequest = signingResult.getSignedRequest();
        byte[] signature = signingResult.getSignature();
        SdkHttpFullRequest.Builder mutableSignedRequest = signedRequest.toBuilder();
        ContentStreamProvider streamProvider = mutableSignedRequest.contentStreamProvider();
        AwsS3V4aChunkSigner chunkSigner = new AwsS3V4aChunkSigner(signerAdapter, chunkConfig);

        String checksumHeader = signerChecksumParams != null ? signerChecksumParams.checksumHeaderName() : null;
        SdkChecksum sdkChecksum = signerChecksumParams != null ?
                                       SdkChecksum.forAlgorithm(signerChecksumParams.algorithm()) : null;

        mutableSignedRequest.contentStreamProvider(
            () -> AwsSignedChunkedEncodingInputStream.builder()
                                                     .inputStream(streamProvider.newStream())
                                                     .awsChunkSigner(chunkSigner)
                                                     .checksumHeaderForTrailer(checksumHeader)
                                                     .sdkChecksum(sdkChecksum)
                                                     .headerSignature(new String(signature, StandardCharsets.UTF_8))
                                                     .awsChunkedEncodingConfig(AwsChunkedEncodingConfig.create())
                                                     .build());

        return mutableSignedRequest.build();
    }

    private boolean booleanValue(Boolean attribute) {
        return Boolean.TRUE.equals(attribute);
    }

    /**
     * Applies preconfigured defaults for values that are not present in {@code executionAttributes}.
     */
    private ExecutionAttributes applyDefaults(ExecutionAttributes executionAttributes) {
        return applyDefaultRegionScope(executionAttributes);
    }

    private ExecutionAttributes applyDefaultRegionScope(ExecutionAttributes executionAttributes) {
        if (executionAttributes.getAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE) != null) {
            return executionAttributes;
        }

        if (defaultRegionScope == null) {
            return executionAttributes;
        }

        return executionAttributes.copy()
                                  .putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION_SCOPE, defaultRegionScope);
    }

    private static class BuilderImpl implements Builder {
        private RegionScope defaultRegionScope;

        @Override
        public Builder defaultRegionScope(RegionScope defaultRegionScope) {
            this.defaultRegionScope = defaultRegionScope;
            return this;
        }

        @Override
        public AwsCrtS3V4aSigner build() {
            return new DefaultAwsCrtS3V4aSigner(this);
        }
    }

    private static long getChecksumTrailerLength(SignerChecksumParams signerParams, int signatureLength) {
        return signerParams == null ? 0
                                    : AwsSignedChunkedEncodingInputStream.calculateChecksumContentLength(
                                        signerParams.algorithm(),
                                        signerParams.checksumHeaderName(), signatureLength);
    }

    private static void updateRequestWithTrailer(SignerChecksumParams signerChecksumParams,
                                                 SdkHttpFullRequest.Builder mutableRequest) {
        mutableRequest.putHeader("x-amz-trailer", signerChecksumParams.checksumHeaderName());
        mutableRequest.putHeader("Content-Encoding", "aws-chunked");
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy