Unverified Commit 8dbf48cf authored by Zelda Hessler's avatar Zelda Hessler Committed by GitHub
Browse files

Add operation customization for disabling payload signing (#3915)

## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here -->
https://github.com/smithy-lang/smithy-rs/pull/3583

## Description
<!--- Describe your changes in detail -->
This PR adds the ability for users to disable payload signing with an
operation customization.

## Testing
<!--- Please describe in detail how you tested your changes -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->
This PR includes tests.

## Checklist
<!--- If a checkbox below is not applicable, then please DELETE it
rather than leaving it unchecked -->
- [x] For changes to the AWS SDK, generated SDK code, or SDK runtime
crates, I have created a changelog entry Markdown file in the
`.changelog` directory, specifying "aws-sdk-rust" in the `applies_to`
key.

----

_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._
parent 208ca795
Loading
Loading
Loading
Loading
+28 −0
Original line number Diff line number Diff line
---
applies_to: ["client"]
authors: ["Velfi"]
references: ["smithy-rs#3583"]
breaking: false
new_feature: true
bug_fix: false
---

It is now possible to disable payload signing through an operation customization.

```rust
async fn put_example_object(client: &aws_sdk_s3::Client) {
    let res = client
        .put_object()
        .bucket("test-bucket")
        .key("test-key")
        .body(ByteStream::from_static(b"Hello, world!"))
        .customize()
        // Setting this will disable payload signing.
        .disable_payload_signing()
        .send()
        .await;
}
```

Disabling payload signing will result in a small speedup at the cost of removing a data integrity check.
However, this is an advanced feature and **may not be supported by all services/operations**.
+1 −1
Original line number Diff line number Diff line
@@ -138,7 +138,7 @@ dependencies = [

[[package]]
name = "aws-runtime"
version = "1.4.4"
version = "1.5.0"
dependencies = [
 "arbitrary",
 "aws-credential-types",
+4 −7
Original line number Diff line number Diff line
@@ -7,9 +7,9 @@

//! Interceptor for handling Smithy `@httpChecksum` request checksumming with AWS SigV4

use aws_runtime::auth::PayloadSigningOverride;
use aws_runtime::content_encoding::header_value::AWS_CHUNKED;
use aws_runtime::content_encoding::{AwsChunkedBody, AwsChunkedBodyOptions};
use aws_runtime::{auth::SigV4OperationSigningConfig, content_encoding::header_value::AWS_CHUNKED};
use aws_sigv4::http_request::SignableBody;
use aws_smithy_checksums::ChecksumAlgorithm;
use aws_smithy_checksums::{body::calculate, http::HttpChecksum};
use aws_smithy_runtime_api::box_error::BoxError;
@@ -190,11 +190,8 @@ fn add_checksum_for_request_body(
        // Body is streaming: wrap the body so it will emit a checksum as a trailer.
        None => {
            tracing::debug!("applying {checksum_algorithm:?} of the request body as a trailer");
            if let Some(mut signing_config) = cfg.load::<SigV4OperationSigningConfig>().cloned() {
                signing_config.signing_options.payload_override =
                    Some(SignableBody::StreamingUnsignedPayloadTrailer);
                cfg.interceptor_state().store_put(signing_config);
            }
            cfg.interceptor_state()
                .store_put(PayloadSigningOverride::StreamingUnsignedPayloadTrailer);
            wrap_streaming_request_body_in_checksum_calculating_body(request, checksum_algorithm)?;
        }
    }
+1 −1
Original line number Diff line number Diff line
[package]
name = "aws-runtime"
version = "1.4.4"
version = "1.5.0"
authors = ["AWS Rust SDK Team <aws-sdk-rust@amazon.com>"]
description = "Runtime support code for the AWS SDK. This crate isn't intended to be used directly."
edition = "2021"
+69 −1
Original line number Diff line number Diff line
@@ -11,7 +11,8 @@ use aws_smithy_runtime_api::box_error::BoxError;
use aws_smithy_runtime_api::client::auth::AuthSchemeEndpointConfig;
use aws_smithy_runtime_api::client::identity::Identity;
use aws_smithy_runtime_api::client::orchestrator::HttpRequest;
use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace};
use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin;
use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer, Storable, StoreReplace};
use aws_smithy_types::Document;
use aws_types::region::{Region, SigningRegion, SigningRegionSet};
use aws_types::SigningName;
@@ -265,3 +266,70 @@ fn apply_signing_instructions(
    }
    Ok(())
}

/// When present in the config bag, this type will signal that the default
/// payload signing should be overridden.
#[non_exhaustive]
#[derive(Clone, Debug)]
pub enum PayloadSigningOverride {
    /// An unsigned payload
    ///
    /// UnsignedPayload is used for streaming requests where the contents of the body cannot be
    /// known prior to signing
    UnsignedPayload,

    /// A precomputed body checksum. The checksum should be a SHA256 checksum of the body,
    /// lowercase hex encoded. Eg:
    /// `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`
    Precomputed(String),

    /// Set when a streaming body has checksum trailers.
    StreamingUnsignedPayloadTrailer,
}

impl PayloadSigningOverride {
    /// Create a payload signing override that will prevent the payload from
    /// being signed.
    pub fn unsigned_payload() -> Self {
        Self::UnsignedPayload
    }

    /// Convert this type into the type used by the signer to determine how a
    /// request body should be signed.
    pub fn to_signable_body(self) -> SignableBody<'static> {
        match self {
            Self::UnsignedPayload => SignableBody::UnsignedPayload,
            Self::Precomputed(checksum) => SignableBody::Precomputed(checksum),
            Self::StreamingUnsignedPayloadTrailer => SignableBody::StreamingUnsignedPayloadTrailer,
        }
    }
}

impl Storable for PayloadSigningOverride {
    type Storer = StoreReplace<Self>;
}

/// A runtime plugin that, when set, will override how the signer signs request payloads.
#[derive(Debug)]
pub struct PayloadSigningOverrideRuntimePlugin {
    inner: FrozenLayer,
}

impl PayloadSigningOverrideRuntimePlugin {
    /// Create a new runtime plugin that will force the signer to skip signing
    /// the request payload when signing an HTTP request.
    pub fn unsigned() -> Self {
        let mut layer = Layer::new("PayloadSigningOverrideRuntimePlugin");
        layer.store_put(PayloadSigningOverride::UnsignedPayload);

        Self {
            inner: layer.freeze(),
        }
    }
}

impl RuntimePlugin for PayloadSigningOverrideRuntimePlugin {
    fn config(&self) -> Option<FrozenLayer> {
        Some(self.inner.clone())
    }
}
Loading