AWS SDK

AWS SDK

rev. 3964b40d6806bc3f52bd311e2c791030c3325f2c (ignoring whitespace)

Files changed:

tmp-codegen-diff/aws-sdk/sdk/aws-sigv4/aws-signing-test-suite/v4a/post-x-www-form-urlencoded/query-signed-request.txt

@@ -0,1 +0,6 @@
           1  +
POST /?X-Amz-Algorithm=AWS4-ECDSA-P256-SHA256&X-Amz-Credential=AKIDEXAMPLE%2F20150830%2Fservice%2Faws4_request&X-Amz-Date=20150830T123600Z&X-Amz-SignedHeaders=content-length%3Bcontent-type%3Bhost&X-Amz-Expires=3600&X-Amz-Region-Set=us-east-1&X-Amz-Signature=30450221008d8a6aa0bc3f651e6c14c52e9e24dbca58964641c9cb6e55169f9dc74766ae3d022016126756ce1523ac972f66f6bf6e981f44572d3c8916f1f43d428fb2caa0e1ea HTTP/1.1
           2  +
Content-Type:application/x-www-form-urlencoded
           3  +
Host:example.amazonaws.com
           4  +
Content-Length:13
           5  +
           6  +
Param1=value1

tmp-codegen-diff/aws-sdk/sdk/aws-sigv4/aws-signing-test-suite/v4a/post-x-www-form-urlencoded/request.txt

@@ -0,1 +0,6 @@
           1  +
POST / HTTP/1.1
           2  +
Content-Type:application/x-www-form-urlencoded
           3  +
Host:example.amazonaws.com
           4  +
Content-Length:13
           5  +
           6  +
Param1=value1

tmp-codegen-diff/aws-sdk/sdk/aws-sigv4/src/http_request/canonical_request.rs

@@ -346,346 +412,423 @@
  366    366   
            if let Some(security_token) = values.security_token {
  367    367   
                add_param(
  368    368   
                    &mut params,
  369    369   
                    settings
  370    370   
                        .session_token_name_override
  371    371   
                        .unwrap_or(param::X_AMZ_SECURITY_TOKEN),
  372    372   
                    security_token,
  373    373   
                );
  374    374   
            }
  375    375   
        }
  376         -
        // Sort by param name, and then by param value
         376  +
         377  +
        // Sort on the _encoded_ key/value pairs
         378  +
        let mut params: Vec<(String, String)> = params
         379  +
            .into_iter()
         380  +
            .map(|x| {
         381  +
                use aws_smithy_http::query::fmt_string;
         382  +
                let enc_k = fmt_string(&x.0);
         383  +
                let enc_v = fmt_string(&x.1);
         384  +
                (enc_k, enc_v)
         385  +
            })
         386  +
            .collect();
         387  +
  377    388   
        params.sort();
  378    389   
  379    390   
        let mut query = QueryWriter::new(uri);
  380    391   
        query.clear_params();
  381    392   
        for (key, value) in params {
  382         -
            query.insert(&key, &value);
         393  +
            query.insert_encoded(&key, &value);
  383    394   
        }
  384    395   
  385    396   
        let query = query.build_query();
  386    397   
        if query.is_empty() {
  387    398   
            None
  388    399   
        } else {
  389    400   
            Some(query)
  390    401   
        }
  391    402   
    }
  392    403   
@@ -642,653 +951,972 @@
  662    673   
    }
  663    674   
}
  664    675   
  665    676   
#[cfg(test)]
  666    677   
mod tests {
  667    678   
    use crate::date_time::test_parsers::parse_date_time;
  668    679   
    use crate::http_request::canonical_request::{
  669    680   
        normalize_header_value, trim_all, CanonicalRequest, SigningScope, StringToSign,
  670    681   
    };
  671    682   
    use crate::http_request::test;
         683  +
    use crate::http_request::test::SigningSuiteTest;
  672    684   
    use crate::http_request::{
  673    685   
        PayloadChecksumKind, SessionTokenMode, SignableBody, SignableRequest, SignatureLocation,
  674    686   
        SigningParams, SigningSettings,
  675    687   
    };
  676    688   
    use crate::sign::v4;
  677    689   
    use crate::sign::v4::sha256_hex_string;
  678    690   
    use aws_credential_types::Credentials;
  679    691   
    use aws_smithy_http::query_writer::QueryWriter;
  680    692   
    use aws_smithy_runtime_api::client::identity::Identity;
  681    693   
    use http0::{HeaderValue, Uri};
  682    694   
    use pretty_assertions::assert_eq;
  683    695   
    use proptest::{prelude::*, proptest};
  684    696   
    use std::borrow::Cow;
  685    697   
    use std::time::Duration;
  686    698   
  687    699   
    fn signing_params(identity: &Identity, settings: SigningSettings) -> SigningParams<'_> {
  688    700   
        v4::signing_params::Builder::default()
  689    701   
            .identity(identity)
  690    702   
            .region("test-region")
  691    703   
            .name("testservicename")
  692    704   
            .time(parse_date_time("20210511T154045Z").unwrap())
  693    705   
            .settings(settings)
  694    706   
            .build()
  695    707   
            .unwrap()
  696    708   
            .into()
  697    709   
    }
  698    710   
  699    711   
    #[test]
  700    712   
    fn test_repeated_header() {
  701         -
        let mut req = test::v4::test_request("get-vanilla-query-order-key-case");
         713  +
        let test = test::SigningSuiteTest::v4("get-vanilla-query-order-key-case");
         714  +
        let mut req = test.request();
  702    715   
        req.headers.push((
  703    716   
            "x-amz-object-attributes".to_string(),
  704    717   
            "Checksum".to_string(),
  705    718   
        ));
  706    719   
        req.headers.push((
  707    720   
            "x-amz-object-attributes".to_string(),
  708    721   
            "ObjectSize".to_string(),
  709    722   
        ));
  710    723   
        let req = SignableRequest::from(&req);
  711    724   
        let settings = SigningSettings {
  712    725   
            payload_checksum_kind: PayloadChecksumKind::XAmzSha256,
  713    726   
            session_token_mode: SessionTokenMode::Exclude,
  714    727   
            ..Default::default()
  715    728   
        };
  716    729   
        let identity = Credentials::for_tests().into();
  717    730   
        let signing_params = signing_params(&identity, settings);
  718    731   
        let creq = CanonicalRequest::from(&req, &signing_params).unwrap();
  719    732   
  720    733   
        assert_eq!(
  721    734   
            creq.values.signed_headers().to_string(),
  722    735   
            "host;x-amz-content-sha256;x-amz-date;x-amz-object-attributes"
  723    736   
        );
  724    737   
        assert_eq!(
  725    738   
            creq.header_values_for("x-amz-object-attributes"),
  726    739   
            "Checksum,ObjectSize",
  727    740   
        );
  728    741   
    }
  729    742   
  730    743   
    #[test]
  731    744   
    fn test_host_header_properly_handles_ports() {
  732    745   
        fn host_header_test_setup(endpoint: String) -> String {
  733         -
            let mut req = test::v4::test_request("get-vanilla");
         746  +
            let test = SigningSuiteTest::v4("get-vanilla");
         747  +
            let mut req = test.request();
  734    748   
            req.uri = endpoint;
  735    749   
            let req = SignableRequest::from(&req);
  736    750   
            let settings = SigningSettings {
  737    751   
                payload_checksum_kind: PayloadChecksumKind::XAmzSha256,
  738    752   
                session_token_mode: SessionTokenMode::Exclude,
  739    753   
                ..Default::default()
  740    754   
            };
  741    755   
            let identity = Credentials::for_tests().into();
  742    756   
            let signing_params = signing_params(&identity, settings);
  743    757   
            let creq = CanonicalRequest::from(&req, &signing_params).unwrap();
  744    758   
            creq.header_values_for("host")
  745    759   
        }
  746    760   
  747    761   
        // HTTP request with 80 port should not be signed with that port
  748    762   
        let http_80_host_header = host_header_test_setup("http://localhost:80".into());
  749    763   
        assert_eq!(http_80_host_header, "localhost",);
  750    764   
  751    765   
        // HTTP request with non-80 port should be signed with that port
  752    766   
        let http_1234_host_header = host_header_test_setup("http://localhost:1234".into());
  753    767   
        assert_eq!(http_1234_host_header, "localhost:1234",);
  754    768   
  755    769   
        // HTTPS request with 443 port should not be signed with that port
  756    770   
        let https_443_host_header = host_header_test_setup("https://localhost:443".into());
  757    771   
        assert_eq!(https_443_host_header, "localhost",);
  758    772   
  759    773   
        // HTTPS request with non-443 port should be signed with that port
  760    774   
        let https_1234_host_header = host_header_test_setup("https://localhost:1234".into());
  761    775   
        assert_eq!(https_1234_host_header, "localhost:1234",);
  762    776   
    }
  763    777   
  764    778   
    #[test]
  765    779   
    fn test_set_xamz_sha_256() {
  766         -
        let req = test::v4::test_request("get-vanilla-query-order-key-case");
         780  +
        let test = SigningSuiteTest::v4("get-vanilla-query-order-key-case");
         781  +
        let req = test.request();
  767    782   
        let req = SignableRequest::from(&req);
  768    783   
        let settings = SigningSettings {
  769    784   
            payload_checksum_kind: PayloadChecksumKind::XAmzSha256,
  770    785   
            session_token_mode: SessionTokenMode::Exclude,
  771    786   
            ..Default::default()
  772    787   
        };
  773    788   
        let identity = Credentials::for_tests().into();
  774    789   
        let mut signing_params = signing_params(&identity, settings);
  775    790   
        let creq = CanonicalRequest::from(&req, &signing_params).unwrap();
  776    791   
        assert_eq!(
  777    792   
            creq.values.content_sha256(),
  778    793   
            "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
  779    794   
        );
  780    795   
        // assert that the sha256 header was added
  781    796   
        assert_eq!(
  782    797   
            creq.values.signed_headers().as_str(),
  783    798   
            "host;x-amz-content-sha256;x-amz-date"
  784    799   
        );
  785    800   
  786    801   
        signing_params.set_payload_checksum_kind(PayloadChecksumKind::NoHeader);
  787    802   
        let creq = CanonicalRequest::from(&req, &signing_params).unwrap();
  788    803   
        assert_eq!(creq.values.signed_headers().as_str(), "host;x-amz-date");
  789    804   
    }
  790    805   
  791    806   
    #[test]
  792    807   
    fn test_unsigned_payload() {
  793         -
        let mut req = test::v4::test_request("get-vanilla-query-order-key-case");
         808  +
        let test = SigningSuiteTest::v4("get-vanilla-query-order-key-case");
         809  +
        let mut req = test.request();
  794    810   
        req.set_body(SignableBody::UnsignedPayload);
  795    811   
        let req: SignableRequest<'_> = SignableRequest::from(&req);
  796    812   
  797    813   
        let settings = SigningSettings {
  798    814   
            payload_checksum_kind: PayloadChecksumKind::XAmzSha256,
  799    815   
            ..Default::default()
  800    816   
        };
  801    817   
        let identity = Credentials::for_tests().into();
  802    818   
        let signing_params = signing_params(&identity, settings);
  803    819   
        let creq = CanonicalRequest::from(&req, &signing_params).unwrap();
  804    820   
        assert_eq!(creq.values.content_sha256(), "UNSIGNED-PAYLOAD");
  805    821   
        assert!(creq.to_string().ends_with("UNSIGNED-PAYLOAD"));
  806    822   
    }
  807    823   
  808    824   
    #[test]
  809    825   
    fn test_precomputed_payload() {
  810    826   
        let payload_hash = "44ce7dd67c959e0d3524ffac1771dfbba87d2b6b4b4e99e42034a8b803f8b072";
  811         -
        let mut req = test::v4::test_request("get-vanilla-query-order-key-case");
         827  +
        let test = SigningSuiteTest::v4("get-vanilla-query-order-key-case");
         828  +
        let mut req = test.request();
  812    829   
        req.set_body(SignableBody::Precomputed(String::from(payload_hash)));
  813    830   
        let req = SignableRequest::from(&req);
  814    831   
        let settings = SigningSettings {
  815    832   
            payload_checksum_kind: PayloadChecksumKind::XAmzSha256,
  816    833   
            ..Default::default()
  817    834   
        };
  818    835   
        let identity = Credentials::for_tests().into();
  819    836   
        let signing_params = signing_params(&identity, settings);
  820    837   
        let creq = CanonicalRequest::from(&req, &signing_params).unwrap();
  821    838   
        assert_eq!(creq.values.content_sha256(), payload_hash);
  822    839   
        assert!(creq.to_string().ends_with(payload_hash));
  823    840   
    }
  824    841   
  825    842   
    #[test]
  826    843   
    fn test_generate_scope() {
  827    844   
        let expected = "20150830/us-east-1/iam/aws4_request\n";
  828    845   
        let scope = SigningScope {
  829    846   
            time: parse_date_time("20150830T123600Z").unwrap(),
  830    847   
            region: "us-east-1",
  831    848   
            service: "iam",
  832    849   
        };
  833    850   
        assert_eq!(format!("{}\n", scope), expected);
  834    851   
    }
  835    852   
  836    853   
    #[test]
  837    854   
    fn test_string_to_sign() {
  838    855   
        let time = parse_date_time("20150830T123600Z").unwrap();
  839         -
        let creq = test::v4::test_canonical_request("get-vanilla-query-order-key-case");
  840         -
        let expected_sts = test::v4::test_sts("get-vanilla-query-order-key-case");
         856  +
        let test = SigningSuiteTest::v4("get-vanilla-query-order-key-case");
         857  +
        let creq = test.canonical_request(SignatureLocation::Headers);
         858  +
        let expected_sts = test.string_to_sign(SignatureLocation::Headers);
  841    859   
        let encoded = sha256_hex_string(creq.as_bytes());
  842    860   
  843    861   
        let actual = StringToSign::new_v4(time, "us-east-1", "service", &encoded);
  844    862   
        assert_eq!(expected_sts, actual.to_string());
  845    863   
    }
  846    864   
  847    865   
    #[test]
  848    866   
    fn test_digest_of_canonical_request() {
  849         -
        let creq = test::v4::test_canonical_request("get-vanilla-query-order-key-case");
         867  +
        let test = SigningSuiteTest::v4("get-vanilla-query-order-key-case");
         868  +
        let creq = test.canonical_request(SignatureLocation::Headers);
  850    869   
        let expected = "816cd5b414d056048ba4f7c5386d6e0533120fb1fcfa93762cf0fc39e2cf19e0";
  851    870   
        let actual = sha256_hex_string(creq.as_bytes());
  852    871   
        assert_eq!(expected, actual);
  853    872   
    }
  854    873   
  855    874   
    #[test]
  856    875   
    fn test_double_url_encode_path() {
  857         -
        let req = test::v4::test_request("double-encode-path");
         876  +
        let test = SigningSuiteTest::v4("double-encode-path");
         877  +
        let req = test.request();
  858    878   
        let req = SignableRequest::from(&req);
  859    879   
        let identity = Credentials::for_tests().into();
  860    880   
        let signing_params = signing_params(&identity, SigningSettings::default());
  861    881   
        let creq = CanonicalRequest::from(&req, &signing_params).unwrap();
  862    882   
  863         -
        let expected = test::v4::test_canonical_request("double-encode-path");
         883  +
        let expected = test.canonical_request(SignatureLocation::Headers);
  864    884   
        let actual = format!("{}", creq);
  865    885   
        assert_eq!(actual, expected);
  866    886   
    }
  867    887   
  868    888   
    #[test]
  869    889   
    fn test_double_url_encode() {
  870         -
        let req = test::v4::test_request("double-url-encode");
         890  +
        let test = SigningSuiteTest::v4("double-url-encode");
         891  +
        let req = test.request();
  871    892   
        let req = SignableRequest::from(&req);
  872    893   
        let identity = Credentials::for_tests().into();
  873    894   
        let signing_params = signing_params(&identity, SigningSettings::default());
  874    895   
        let creq = CanonicalRequest::from(&req, &signing_params).unwrap();
  875         -
  876         -
        let expected = test::v4::test_canonical_request("double-url-encode");
         896  +
        let expected = test.canonical_request(SignatureLocation::Headers);
  877    897   
        let actual = format!("{}", creq);
  878    898   
        assert_eq!(actual, expected);
  879    899   
    }
  880    900   
  881    901   
    #[test]
  882    902   
    fn test_tilde_in_uri() {
  883    903   
        let req = http0::Request::builder()
  884    904   
            .uri("https://s3.us-east-1.amazonaws.com/my-bucket?list-type=2&prefix=~objprefix&single&k=&unreserved=-_.~").body("").unwrap().into();
  885    905   
        let req = SignableRequest::from(&req);
  886    906   
        let identity = Credentials::for_tests().into();
  887    907   
        let signing_params = signing_params(&identity, SigningSettings::default());
  888    908   
        let creq = CanonicalRequest::from(&req, &signing_params).unwrap();
  889    909   
        assert_eq!(
  890    910   
            Some("k=&list-type=2&prefix=~objprefix&single=&unreserved=-_.~"),
  891    911   
            creq.params.as_deref(),
  892    912   
        );
  893    913   
    }
  894    914   
  895    915   
    #[test]
  896    916   
    fn test_signing_urls_with_percent_encoded_query_strings() {
  897    917   
        let all_printable_ascii_chars: String = (32u8..127).map(char::from).collect();
  898    918   
        let uri = Uri::from_static("https://s3.us-east-1.amazonaws.com/my-bucket");
  899    919   
  900    920   
        let mut query_writer = QueryWriter::new(&uri);
  901    921   
        query_writer.insert("list-type", "2");
  902    922   
        query_writer.insert("prefix", &all_printable_ascii_chars);
  903    923   
  904    924   
        let req = http0::Request::builder()
  905    925   
            .uri(query_writer.build_uri())
  906    926   
            .body("")
  907    927   
            .unwrap()
  908    928   
            .into();
  909    929   
        let req = SignableRequest::from(&req);
  910    930   
        let identity = Credentials::for_tests().into();
  911    931   
        let signing_params = signing_params(&identity, SigningSettings::default());
  912    932   
        let creq = CanonicalRequest::from(&req, &signing_params).unwrap();
  913    933   
  914    934   
        let expected = "list-type=2&prefix=%20%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~";
  915    935   
        let actual = creq.params.unwrap();
  916    936   
        assert_eq!(expected, actual);
  917    937   
    }
  918    938   
  919    939   
    #[test]
  920    940   
    fn test_omit_session_token() {
  921         -
        let req = test::v4::test_request("get-vanilla-query-order-key-case");
         941  +
        let test = SigningSuiteTest::v4("get-vanilla-query-order-key-case");
         942  +
        let req = test.request();
  922    943   
        let req = SignableRequest::from(&req);
  923    944   
        let settings = SigningSettings {
  924    945   
            session_token_mode: SessionTokenMode::Include,
  925    946   
            ..Default::default()
  926    947   
        };
  927    948   
        let identity = Credentials::for_tests_with_session_token().into();
  928    949   
        let mut signing_params = signing_params(&identity, settings);
  929    950   
  930    951   
        let creq = CanonicalRequest::from(&req, &signing_params).unwrap();
  931    952   
        assert_eq!(

tmp-codegen-diff/aws-sdk/sdk/aws-sigv4/src/http_request/sign.rs

@@ -478,478 +913,801 @@
  498    498   
        access_key,
  499    499   
        scope,
  500    500   
        creq.values.signed_headers().as_str(),
  501    501   
        signature
  502    502   
    )
  503    503   
}
  504    504   
#[cfg(test)]
  505    505   
mod tests {
  506    506   
    use crate::date_time::test_parsers::parse_date_time;
  507    507   
    use crate::http_request::sign::{add_header, SignableRequest};
         508  +
    use crate::http_request::test::SigningSuiteTest;
  508    509   
    use crate::http_request::{
  509         -
        sign, test, SessionTokenMode, SignableBody, SignatureLocation, SigningInstructions,
         510  +
        sign, SessionTokenMode, SignableBody, SignatureLocation, SigningInstructions,
  510    511   
        SigningSettings,
  511    512   
    };
  512    513   
    use crate::sign::v4;
  513    514   
    use aws_credential_types::Credentials;
  514    515   
    use http0::{HeaderValue, Request};
  515    516   
    use pretty_assertions::assert_eq;
  516    517   
    use proptest::proptest;
  517    518   
    use std::borrow::Cow;
  518    519   
    use std::iter;
  519         -
    use std::time::Duration;
  520    520   
  521    521   
    macro_rules! assert_req_eq {
  522    522   
        (http: $expected:expr, $actual:expr) => {
  523    523   
            let mut expected = ($expected).map(|_b|"body");
  524    524   
            let mut actual = ($actual).map(|_b|"body");
  525    525   
            make_headers_comparable(&mut expected);
  526    526   
            make_headers_comparable(&mut actual);
  527    527   
            assert_eq!(format!("{:?}", expected), format!("{:?}", actual));
  528    528   
        };
  529    529   
        ($expected:tt, $actual:tt) => {
  530    530   
            assert_req_eq!(http: ($expected).as_http_request(), $actual);
  531    531   
        };
  532    532   
    }
  533    533   
  534    534   
    pub(crate) fn make_headers_comparable<B>(request: &mut Request<B>) {
  535    535   
        for (_name, value) in request.headers_mut() {
  536    536   
            value.set_sensitive(false);
  537    537   
        }
  538    538   
    }
  539    539   
  540         -
    #[test]
  541         -
    fn test_sign_vanilla_with_headers() {
  542         -
        let settings = SigningSettings::default();
  543         -
        let identity = &Credentials::for_tests().into();
  544         -
        let params = v4::SigningParams {
  545         -
            identity,
  546         -
            region: "us-east-1",
  547         -
            name: "service",
  548         -
            time: parse_date_time("20150830T123600Z").unwrap(),
  549         -
            settings,
  550         -
        }
  551         -
        .into();
  552         -
  553         -
        let original = test::v4::test_request("get-vanilla-query-order-key-case");
  554         -
        let signable = SignableRequest::from(&original);
  555         -
        let out = sign(signable, &params).unwrap();
  556         -
        assert_eq!(
  557         -
            "5557820e7380d585310524bd93d51a08d7757fb5efd7344ee12088f2b0860947",
  558         -
            out.signature
  559         -
        );
  560         -
  561         -
        let mut signed = original.as_http_request();
  562         -
        out.output.apply_to_request_http0x(&mut signed);
  563         -
  564         -
        let expected = test::v4::test_signed_request("get-vanilla-query-order-key-case");
  565         -
        assert_req_eq!(expected, signed);
  566         -
    }
  567         -
         540  +
    // Sigv4A suite tests
  568    541   
    #[cfg(feature = "sigv4a")]
  569         -
    mod sigv4a_tests {
  570         -
        use super::*;
  571         -
        use crate::http_request::canonical_request::{CanonicalRequest, StringToSign};
  572         -
        use crate::http_request::{sign, test, SigningParams};
  573         -
        use crate::sign::v4a;
  574         -
        use p256::ecdsa::signature::{Signature, Verifier};
  575         -
        use p256::ecdsa::{DerSignature, SigningKey};
  576         -
        use pretty_assertions::assert_eq;
  577         -
  578         -
        fn new_v4a_signing_params_from_context(
  579         -
            test_context: &'_ test::v4a::TestContext,
  580         -
            signature_location: SignatureLocation,
  581         -
        ) -> SigningParams<'_> {
  582         -
            let mut params = v4a::SigningParams::from(test_context);
  583         -
            params.settings.signature_location = signature_location;
  584         -
  585         -
            params.into()
  586         -
        }
  587         -
  588         -
        fn run_v4a_test_suite(test_name: &str, signature_location: SignatureLocation) {
  589         -
            let tc = test::v4a::test_context(test_name);
  590         -
            let params = new_v4a_signing_params_from_context(&tc, signature_location);
  591         -
  592         -
            let req = test::v4a::test_request(test_name);
  593         -
            let expected_creq = test::v4a::test_canonical_request(test_name, signature_location);
  594         -
            let signable_req = SignableRequest::from(&req);
  595         -
            let actual_creq = CanonicalRequest::from(&signable_req, &params).unwrap();
  596         -
  597         -
            assert_eq!(expected_creq, actual_creq.to_string(), "creq didn't match");
         542  +
    mod v4a_suite {
         543  +
        use crate::http_request::test::v4a::run_test_suite_v4a;
  598    544   
  599         -
            let expected_string_to_sign =
  600         -
                test::v4a::test_string_to_sign(test_name, signature_location);
  601         -
            let hashed_creq = &v4::sha256_hex_string(actual_creq.to_string().as_bytes());
  602         -
            let actual_string_to_sign = StringToSign::new_v4a(
  603         -
                *params.time(),
  604         -
                params.region_set().unwrap(),
  605         -
                params.name(),
  606         -
                hashed_creq,
  607         -
            )
  608         -
            .to_string();
  609         -
  610         -
            assert_eq!(
  611         -
                expected_string_to_sign, actual_string_to_sign,
  612         -
                "'string to sign' didn't match"
  613         -
            );
  614         -
  615         -
            let out = sign(signable_req, &params).unwrap();
  616         -
            // Sigv4a signatures are non-deterministic, so we can't compare the signature directly.
  617         -
            out.output
  618         -
                .apply_to_request_http0x(&mut req.as_http_request());
  619         -
  620         -
            let creds = params.credentials().unwrap();
  621         -
            let signing_key =
  622         -
                v4a::generate_signing_key(creds.access_key_id(), creds.secret_access_key());
  623         -
            let sig = DerSignature::from_bytes(&hex::decode(out.signature).unwrap()).unwrap();
  624         -
            let sig = sig
  625         -
                .try_into()
  626         -
                .expect("DER-style signatures are always convertible into fixed-size signatures");
  627         -
  628         -
            let signing_key = SigningKey::from_bytes(signing_key.as_ref()).unwrap();
  629         -
            let peer_public_key = signing_key.verifying_key();
  630         -
            let sts = actual_string_to_sign.as_bytes();
  631         -
            peer_public_key.verify(sts, &sig).unwrap();
         545  +
        #[test]
         546  +
        fn test_get_header_key_duplicate() {
         547  +
            run_test_suite_v4a("get-header-key-duplicate")
  632    548   
        }
  633    549   
  634    550   
        #[test]
  635         -
        fn test_get_header_key_duplicate() {
  636         -
            run_v4a_test_suite("get-header-key-duplicate", SignatureLocation::Headers);
         551  +
        #[ignore = "httpparse doesn't support parsing multiline headers since they are deprecated in RFC7230"]
         552  +
        fn test_get_header_value_multiline() {
         553  +
            run_test_suite_v4a("get-header-value-multiline")
  637    554   
        }
  638    555   
  639    556   
        #[test]
  640    557   
        fn test_get_header_value_order() {
  641         -
            run_v4a_test_suite("get-header-value-order", SignatureLocation::Headers);
         558  +
            run_test_suite_v4a("get-header-value-order")
  642    559   
        }
  643    560   
  644    561   
        #[test]
  645    562   
        fn test_get_header_value_trim() {
  646         -
            run_v4a_test_suite("get-header-value-trim", SignatureLocation::Headers);
         563  +
            run_test_suite_v4a("get-header-value-trim");
  647    564   
        }
  648    565   
  649    566   
        #[test]
  650    567   
        fn test_get_relative_normalized() {
  651         -
            run_v4a_test_suite("get-relative-normalized", SignatureLocation::Headers);
         568  +
            run_test_suite_v4a("get-relative-normalized");
  652    569   
        }
  653    570   
  654    571   
        #[test]
  655    572   
        fn test_get_relative_relative_normalized() {
  656         -
            run_v4a_test_suite(
  657         -
                "get-relative-relative-normalized",
  658         -
                SignatureLocation::Headers,
  659         -
            );
         573  +
            run_test_suite_v4a("get-relative-relative-normalized");
  660    574   
        }
  661    575   
  662    576   
        #[test]
  663    577   
        fn test_get_relative_relative_unnormalized() {
  664         -
            run_v4a_test_suite(
  665         -
                "get-relative-relative-unnormalized",
  666         -
                SignatureLocation::Headers,
  667         -
            );
         578  +
            run_test_suite_v4a("get-relative-relative-unnormalized");
  668    579   
        }
  669    580   
  670    581   
        #[test]
  671    582   
        fn test_get_relative_unnormalized() {
  672         -
            run_v4a_test_suite("get-relative-unnormalized", SignatureLocation::Headers);
         583  +
            run_test_suite_v4a("get-relative-unnormalized");
  673    584   
        }
  674    585   
  675    586   
        #[test]
  676    587   
        fn test_get_slash_dot_slash_normalized() {
  677         -
            run_v4a_test_suite("get-slash-dot-slash-normalized", SignatureLocation::Headers);
         588  +
            run_test_suite_v4a("get-slash-dot-slash-normalized");
  678    589   
        }
  679    590   
  680    591   
        #[test]
  681    592   
        fn test_get_slash_dot_slash_unnormalized() {
  682         -
            run_v4a_test_suite(
  683         -
                "get-slash-dot-slash-unnormalized",
  684         -
                SignatureLocation::Headers,
  685         -
            );
         593  +
            run_test_suite_v4a("get-slash-dot-slash-unnormalized");
  686    594   
        }
  687    595   
  688    596   
        #[test]
  689    597   
        fn test_get_slash_normalized() {
  690         -
            run_v4a_test_suite("get-slash-normalized", SignatureLocation::Headers);
         598  +
            run_test_suite_v4a("get-slash-normalized");
  691    599   
        }
  692    600   
  693    601   
        #[test]
  694    602   
        fn test_get_slash_pointless_dot_normalized() {
  695         -
            run_v4a_test_suite(
  696         -
                "get-slash-pointless-dot-normalized",
  697         -
                SignatureLocation::Headers,
  698         -
            );
         603  +
            run_test_suite_v4a("get-slash-pointless-dot-normalized");
  699    604   
        }
  700    605   
  701    606   
        #[test]
  702    607   
        fn test_get_slash_pointless_dot_unnormalized() {
  703         -
            run_v4a_test_suite(
  704         -
                "get-slash-pointless-dot-unnormalized",
  705         -
                SignatureLocation::Headers,
  706         -
            );
         608  +
            run_test_suite_v4a("get-slash-pointless-dot-unnormalized");
  707    609   
        }
  708    610   
  709    611   
        #[test]
  710    612   
        fn test_get_slash_unnormalized() {
  711         -
            run_v4a_test_suite("get-slash-unnormalized", SignatureLocation::Headers);
         613  +
            run_test_suite_v4a("get-slash-unnormalized");
  712    614   
        }
  713    615   
  714    616   
        #[test]
  715    617   
        fn test_get_slashes_normalized() {
  716         -
            run_v4a_test_suite("get-slashes-normalized", SignatureLocation::Headers);
         618  +
            run_test_suite_v4a("get-slashes-normalized");
  717    619   
        }
  718    620   
  719    621   
        #[test]
  720    622   
        fn test_get_slashes_unnormalized() {
  721         -
            run_v4a_test_suite("get-slashes-unnormalized", SignatureLocation::Headers);
         623  +
            run_test_suite_v4a("get-slashes-unnormalized");
         624  +
        }
         625  +
         626  +
        #[test]
         627  +
        #[ignore = "relies on single encode of path segments"]
         628  +
        // rely on single encoding of path segments, i.e. string-to-sign contains %20 for spaces rather than %25%20 as it should.
         629  +
        // skipped until we add control over double_uri_encode in context.json
         630  +
        fn test_get_space_normalized() {
         631  +
            run_test_suite_v4a("get-space-normalized");
         632  +
        }
         633  +
         634  +
        #[test]
         635  +
        #[ignore = "httpparse fails on unencoded spaces in path"]
         636  +
        // the input request has unencoded space ' ' in the path which fails to parse
         637  +
        fn test_get_space_unnormalized() {
         638  +
            run_test_suite_v4a("get-space-unnormalized");
  722    639   
        }
  723    640   
  724    641   
        #[test]
  725    642   
        fn test_get_unreserved() {
  726         -
            run_v4a_test_suite("get-unreserved", SignatureLocation::Headers);
         643  +
            run_test_suite_v4a("get-unreserved");
         644  +
        }
         645  +
         646  +
        #[test]
         647  +
        #[ignore = "httparse fails on invalid uri character"]
         648  +
        // relies on /ሴ canonicalized as /%E1%88%B4 when it should be /%25%E1%25%88%25%B4
         649  +
        fn test_get_utf8() {
         650  +
            run_test_suite_v4a("get-utf8");
  727    651   
        }
  728    652   
  729    653   
        #[test]
  730    654   
        fn test_get_vanilla() {
  731         -
            run_v4a_test_suite("get-vanilla", SignatureLocation::Headers);
         655  +
            run_test_suite_v4a("get-vanilla");
  732    656   
        }
  733    657   
  734    658   
        #[test]
  735    659   
        fn test_get_vanilla_empty_query_key() {
  736         -
            run_v4a_test_suite(
  737         -
                "get-vanilla-empty-query-key",
  738         -
                SignatureLocation::QueryParams,
  739         -
            );
         660  +
            run_test_suite_v4a("get-vanilla-empty-query-key");
  740    661   
        }
  741    662   
  742    663   
        #[test]
  743    664   
        fn test_get_vanilla_query() {
  744         -
            run_v4a_test_suite("get-vanilla-query", SignatureLocation::QueryParams);
         665  +
            run_test_suite_v4a("get-vanilla-query");
         666  +
        }
         667  +
         668  +
        #[test]
         669  +
        fn test_get_vanilla_query_order_encoded() {
         670  +
            run_test_suite_v4a("get-vanilla-query-order-encoded");
  745    671   
        }
  746    672   
  747    673   
        #[test]
  748    674   
        fn test_get_vanilla_query_order_key_case() {
  749         -
            run_v4a_test_suite(
  750         -
                "get-vanilla-query-order-key-case",
  751         -
                SignatureLocation::QueryParams,
  752         -
            );
         675  +
            run_test_suite_v4a("get-vanilla-query-order-key-case");
  753    676   
        }
  754    677   
  755    678   
        #[test]
  756    679   
        fn test_get_vanilla_query_unreserved() {
  757         -
            run_v4a_test_suite(
  758         -
                "get-vanilla-query-unreserved",
  759         -
                SignatureLocation::QueryParams,
  760         -
            );
         680  +
            run_test_suite_v4a("get-vanilla-query-unreserved");
         681  +
        }
         682  +
         683  +
        #[test]
         684  +
        #[ignore = "httparse fails on invalid uri character"]
         685  +
        // relies on /ሴ canonicalized as /%E1%88%B4 when it should be /%25%E1%25%88%25%B4
         686  +
        fn test_get_vanilla_utf8_query() {
         687  +
            run_test_suite_v4a("get-vanilla-utf8-query");
  761    688   
        }
  762    689   
  763    690   
        #[test]
  764    691   
        fn test_get_vanilla_with_session_token() {
  765         -
            run_v4a_test_suite("get-vanilla-with-session-token", SignatureLocation::Headers);
         692  +
            run_test_suite_v4a("get-vanilla-with-session-token")
  766    693   
        }
  767    694   
  768    695   
        #[test]
  769    696   
        fn test_post_header_key_case() {
  770         -
            run_v4a_test_suite("post-header-key-case", SignatureLocation::Headers);
         697  +
            run_test_suite_v4a("post-header-key-case");
  771    698   
        }
  772    699   
  773    700   
        #[test]
  774    701   
        fn test_post_header_key_sort() {
  775         -
            run_v4a_test_suite("post-header-key-sort", SignatureLocation::Headers);
         702  +
            run_test_suite_v4a("post-header-key-sort");
  776    703   
        }
  777    704   
  778    705   
        #[test]
  779    706   
        fn test_post_header_value_case() {
  780         -
            run_v4a_test_suite("post-header-value-case", SignatureLocation::Headers);
         707  +
            run_test_suite_v4a("post-header-value-case");
  781    708   
        }
  782    709   
  783    710   
        #[test]
  784    711   
        fn test_post_sts_header_after() {
  785         -
            run_v4a_test_suite("post-sts-header-after", SignatureLocation::Headers);
         712  +
            run_test_suite_v4a("post-sts-header-after");
  786    713   
        }
  787    714   
  788    715   
        #[test]
  789    716   
        fn test_post_sts_header_before() {
  790         -
            run_v4a_test_suite("post-sts-header-before", SignatureLocation::Headers);
         717  +
            run_test_suite_v4a("post-sts-header-before");
  791    718   
        }
  792    719   
  793    720   
        #[test]
  794    721   
        fn test_post_vanilla() {
  795         -
            run_v4a_test_suite("post-vanilla", SignatureLocation::Headers);
         722  +
            run_test_suite_v4a("post-vanilla");
  796    723   
        }
  797    724   
  798    725   
        #[test]
  799    726   
        fn test_post_vanilla_empty_query_value() {
  800         -
            run_v4a_test_suite(
  801         -
                "post-vanilla-empty-query-value",
  802         -
                SignatureLocation::QueryParams,
  803         -
            );
         727  +
            run_test_suite_v4a("post-vanilla-empty-query-value");
  804    728   
        }
  805    729   
  806    730   
        #[test]
  807    731   
        fn test_post_vanilla_query() {
  808         -
            run_v4a_test_suite("post-vanilla-query", SignatureLocation::QueryParams);
         732  +
            run_test_suite_v4a("post-vanilla-query");
  809    733   
        }
  810    734   
  811    735   
        #[test]
  812    736   
        fn test_post_x_www_form_urlencoded() {
  813         -
            run_v4a_test_suite("post-x-www-form-urlencoded", SignatureLocation::Headers);
         737  +
            run_test_suite_v4a("post-x-www-form-urlencoded");
  814    738   
        }
  815    739   
  816    740   
        #[test]
  817    741   
        fn test_post_x_www_form_urlencoded_parameters() {
  818         -
            run_v4a_test_suite(
  819         -
                "post-x-www-form-urlencoded-parameters",
  820         -
                SignatureLocation::QueryParams,
  821         -
            );
         742  +
            run_test_suite_v4a("post-x-www-form-urlencoded-parameters");
  822    743   
        }
  823    744   
    }
  824    745   
  825    746   
    #[test]
  826    747   
    fn test_sign_url_escape() {
  827         -
        let test = "double-encode-path";
         748  +
        let test = SigningSuiteTest::v4("double-encode-path");
  828    749   
        let settings = SigningSettings::default();
  829    750   
        let identity = &Credentials::for_tests().into();
  830    751   
        let params = v4::SigningParams {
  831    752   
            identity,
  832    753   
            region: "us-east-1",
  833    754   
            name: "service",
  834    755   
            time: parse_date_time("20150830T123600Z").unwrap(),
  835    756   
            settings,
  836    757   
        }
  837    758   
        .into();
  838    759   
  839         -
        let original = test::v4::test_request(test);
         760  +
        let original = test.request();
  840    761   
        let signable = SignableRequest::from(&original);
  841    762   
        let out = sign(signable, &params).unwrap();
  842    763   
        assert_eq!(
  843    764   
            "57d157672191bac40bae387e48bbe14b15303c001fdbb01f4abf295dccb09705",
  844    765   
            out.signature
  845    766   
        );
  846    767   
  847    768   
        let mut signed = original.as_http_request();
  848    769   
        out.output.apply_to_request_http0x(&mut signed);
  849    770   
  850         -
        let expected = test::v4::test_signed_request(test);
  851         -
        assert_req_eq!(expected, signed);
  852         -
    }
  853         -
  854         -
    #[test]
  855         -
    fn test_sign_vanilla_with_query_params() {
  856         -
        let settings = SigningSettings {
  857         -
            signature_location: SignatureLocation::QueryParams,
  858         -
            expires_in: Some(Duration::from_secs(35)),
  859         -
            ..Default::default()
  860         -
        };
  861         -
        let identity = &Credentials::for_tests().into();
  862         -
        let params = v4::SigningParams {
  863         -
            identity,
  864         -
            region: "us-east-1",
  865         -
            name: "service",
  866         -
            time: parse_date_time("20150830T123600Z").unwrap(),
  867         -
            settings,
  868         -
        }
  869         -
        .into();
  870         -
  871         -
        let original = test::v4::test_request("get-vanilla-query-order-key-case");
  872         -
        let signable = SignableRequest::from(&original);
  873         -
        let out = sign(signable, &params).unwrap();
  874         -
        assert_eq!(
  875         -
            "ecce208e4b4f7d7e3a4cc22ced6acc2ad1d170ee8ba87d7165f6fa4b9aff09ab",
  876         -
            out.signature
  877         -
        );
  878         -
  879         -
        let mut signed = original.as_http_request();
  880         -
        out.output.apply_to_request_http0x(&mut signed);
  881         -
  882         -
        let expected =
  883         -
            test::v4::test_signed_request_query_params("get-vanilla-query-order-key-case");
         771  +
        let expected = test.signed_request(SignatureLocation::Headers);
  884    772   
        assert_req_eq!(expected, signed);
  885    773   
    }
  886    774   
  887    775   
    #[test]
  888    776   
    fn test_sign_headers_utf8() {
  889    777   
        let settings = SigningSettings::default();
  890    778   
        let identity = &Credentials::for_tests().into();
  891    779   
        let params = v4::SigningParams {
  892    780   
            identity,
  893    781   
            region: "us-east-1",
@@ -1138,1026 +1168,1261 @@
 1158   1046   
 1159   1047   
        let sut = SignableBody::UnsignedPayload;
 1160   1048   
        assert_eq!("UnsignedPayload", format!("{sut:?}"));
 1161   1049   
 1162   1050   
        let sut = SignableBody::Precomputed("precomputed".to_owned());
 1163   1051   
        assert_eq!("Precomputed(\"precomputed\")", format!("{sut:?}"));
 1164   1052   
 1165   1053   
        let sut = SignableBody::StreamingUnsignedPayloadTrailer;
 1166   1054   
        assert_eq!("StreamingUnsignedPayloadTrailer", format!("{sut:?}"));
 1167   1055   
    }
        1056  +
        1057  +
    // v4 test suite
        1058  +
    mod v4_suite {
        1059  +
        use crate::http_request::test::run_test_suite_v4;
        1060  +
        1061  +
        #[test]
        1062  +
        fn test_get_header_key_duplicate() {
        1063  +
            run_test_suite_v4("get-header-key-duplicate");
        1064  +
        }
        1065  +
        1066  +
        #[test]
        1067  +
        #[ignore = "httpparse doesn't support parsing multiline headers since they are deprecated in RFC7230"]
        1068  +
        fn test_get_header_value_multiline() {
        1069  +
            run_test_suite_v4("get-header-value-multiline");
        1070  +
        }
        1071  +
        1072  +
        #[test]
        1073  +
        fn test_get_header_value_order() {
        1074  +
            run_test_suite_v4("get-header-value-order");
        1075  +
        }
        1076  +
        1077  +
        #[test]
        1078  +
        fn test_get_header_value_trim() {
        1079  +
            run_test_suite_v4("get-header-value-trim");
        1080  +
        }
        1081  +
        1082  +
        #[test]
        1083  +
        fn test_get_relative_normalized() {
        1084  +
            run_test_suite_v4("get-relative-normalized");
        1085  +
        }
        1086  +
        1087  +
        #[test]
        1088  +
        fn test_get_relative_relative_normalized() {
        1089  +
            run_test_suite_v4("get-relative-relative-normalized");
        1090  +
        }
        1091  +
        1092  +
        #[test]
        1093  +
        fn test_get_relative_relative_unnormalized() {
        1094  +
            run_test_suite_v4("get-relative-relative-unnormalized");
        1095  +
        }
        1096  +
        1097  +
        #[test]
        1098  +
        fn test_get_relative_unnormalized() {
        1099  +
            run_test_suite_v4("get-relative-unnormalized");
        1100  +
        }
        1101  +
        1102  +
        #[test]
        1103  +
        fn test_get_slash_dot_slash_normalized() {
        1104  +
            run_test_suite_v4("get-slash-dot-slash-normalized");
        1105  +
        }
        1106  +
        1107  +
        #[test]
        1108  +
        fn test_get_slash_dot_slash_unnormalized() {
        1109  +
            run_test_suite_v4("get-slash-dot-slash-unnormalized");
        1110  +
        }
        1111  +
        1112  +
        #[test]
        1113  +
        fn test_get_slash_normalized() {
        1114  +
            run_test_suite_v4("get-slash-normalized");
        1115  +
        }
        1116  +
        1117  +
        #[test]
        1118  +
        fn test_get_slash_pointless_dot_normalized() {
        1119  +
            run_test_suite_v4("get-slash-pointless-dot-normalized");
        1120  +
        }
        1121  +
        1122  +
        #[test]
        1123  +
        fn test_get_slash_pointless_dot_unnormalized() {
        1124  +
            run_test_suite_v4("get-slash-pointless-dot-unnormalized");
        1125  +
        }
        1126  +
        1127  +
        #[test]
        1128  +
        fn test_get_slash_unnormalized() {
        1129  +
            run_test_suite_v4("get-slash-unnormalized");
        1130  +
        }
        1131  +
        1132  +
        #[test]
        1133  +
        fn test_get_slashes_normalized() {
        1134  +
            run_test_suite_v4("get-slashes-normalized");
        1135  +
        }
        1136  +
        1137  +
        #[test]
        1138  +
        fn test_get_slashes_unnormalized() {
        1139  +
            run_test_suite_v4("get-slashes-unnormalized");
        1140  +
        }
        1141  +
        1142  +
        #[test]
        1143  +
        #[ignore = "relies on single encode of path segments"]
        1144  +
        // rely on single encoding of path segments, i.e. string-to-sign contains %20 for spaces rather than %25%20 as it should.
        1145  +
        // skipped until we add control over double_uri_encode in context.json
        1146  +
        fn test_get_space_normalized() {
        1147  +
            run_test_suite_v4("get-space-normalized");
        1148  +
        }
        1149  +
        1150  +
        #[test]
        1151  +
        #[ignore = "httpparse fails on unencoded spaces in path"]
        1152  +
        // the input request has unencoded space ' ' in the path which fails to parse
        1153  +
        fn test_get_space_unnormalized() {
        1154  +
            run_test_suite_v4("get-space-unnormalized");
        1155  +
        }
        1156  +
        1157  +
        #[test]
        1158  +
        fn test_get_unreserved() {
        1159  +
            run_test_suite_v4("get-unreserved");
        1160  +
        }
        1161  +
        1162  +
        #[test]
        1163  +
        #[ignore = "httparse fails on invalid uri character"]
        1164  +
        // relies on /ሴ canonicalized as /%E1%88%B4 when it should be /%25%E1%25%88%25%B4
        1165  +
        fn test_get_utf8() {
        1166  +
            run_test_suite_v4("get-utf8");
        1167  +
        }
        1168  +
        1169  +
        #[test]
        1170  +
        fn test_get_vanilla() {
        1171  +
            run_test_suite_v4("get-vanilla");
        1172  +
        }
        1173  +
        1174  +
        #[test]
        1175  +
        fn test_get_vanilla_empty_query_key() {
        1176  +
            run_test_suite_v4("get-vanilla-empty-query-key");
        1177  +
        }
        1178  +
        1179  +
        #[test]
        1180  +
        fn test_get_vanilla_query() {
        1181  +
            run_test_suite_v4("get-vanilla-query");
        1182  +
        }
        1183  +
        1184  +
        #[test]
        1185  +
        fn test_get_vanilla_query_order_encoded() {
        1186  +
            run_test_suite_v4("get-vanilla-query-order-encoded");
        1187  +
        }
        1188  +
        1189  +
        #[test]
        1190  +
        fn test_get_vanilla_query_order_key_case() {
        1191  +
            run_test_suite_v4("get-vanilla-query-order-key-case");
        1192  +
        }
        1193  +
        1194  +
        #[test]
        1195  +
        fn test_get_vanilla_query_unreserved() {
        1196  +
            run_test_suite_v4("get-vanilla-query-unreserved");
        1197  +
        }
        1198  +
        1199  +
        #[test]
        1200  +
        #[ignore = "httparse fails on invalid uri character"]
        1201  +
        // relies on /ሴ canonicalized as /%E1%88%B4 when it should be /%25%E1%25%88%25%B4
        1202  +
        fn test_get_vanilla_utf8_query() {
        1203  +
            run_test_suite_v4("get-vanilla-utf8-query");
        1204  +
        }
        1205  +
        1206  +
        #[test]
        1207  +
        fn test_get_vanilla_with_session_token() {
        1208  +
            run_test_suite_v4("get-vanilla-with-session-token");
        1209  +
        }
        1210  +
        1211  +
        #[test]
        1212  +
        fn test_post_header_key_case() {
        1213  +
            run_test_suite_v4("post-header-key-case");
        1214  +
        }
        1215  +
        1216  +
        #[test]
        1217  +
        fn test_post_header_key_sort() {
        1218  +
            run_test_suite_v4("post-header-key-sort");
        1219  +
        }
        1220  +
        1221  +
        #[test]
        1222  +
        fn test_post_header_value_case() {
        1223  +
            run_test_suite_v4("post-header-value-case");
        1224  +
        }
        1225  +
        1226  +
        #[test]
        1227  +
        fn test_post_sts_header_after() {
        1228  +
            run_test_suite_v4("post-sts-header-after");
        1229  +
        }
        1230  +
        1231  +
        #[test]
        1232  +
        fn test_post_sts_header_before() {
        1233  +
            run_test_suite_v4("post-sts-header-before");
        1234  +
        }
        1235  +
        1236  +
        #[test]
        1237  +
        fn test_post_vanilla() {
        1238  +
            run_test_suite_v4("post-vanilla");
        1239  +
        }
        1240  +
        1241  +
        #[test]
        1242  +
        fn test_post_vanilla_empty_query_value() {
        1243  +
            run_test_suite_v4("post-vanilla-empty-query-value");
        1244  +
        }
        1245  +
        1246  +
        #[test]
        1247  +
        fn test_post_vanilla_query() {
        1248  +
            run_test_suite_v4("post-vanilla-query");
        1249  +
        }
        1250  +
        1251  +
        #[test]
        1252  +
        fn test_post_x_www_form_urlencoded() {
        1253  +
            run_test_suite_v4("post-x-www-form-urlencoded");
        1254  +
        }
        1255  +
        1256  +
        #[test]
        1257  +
        fn test_post_x_www_form_urlencoded_parameters() {
        1258  +
            run_test_suite_v4("post-x-www-form-urlencoded-parameters");
        1259  +
        }
        1260  +
    }
 1168   1261   
}

tmp-codegen-diff/aws-sdk/sdk/aws-sigv4/src/http_request/test.rs

@@ -1,1 +242,453 @@
    1      1   
/*
    2      2   
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
    3      3   
 * SPDX-License-Identifier: Apache-2.0
    4      4   
 */
    5      5   
    6      6   
//! Functions shared between the tests of several modules.
    7      7   
    8         -
use crate::http_request::{SignableBody, SignableRequest};
           8  +
use crate::http_request::canonical_request::{CanonicalRequest, StringToSign};
           9  +
use crate::http_request::{
          10  +
    PayloadChecksumKind, SessionTokenMode, SignableBody, SignableRequest, SignatureLocation,
          11  +
    SigningSettings,
          12  +
};
          13  +
use aws_credential_types::Credentials;
          14  +
use aws_smithy_runtime_api::client::identity::Identity;
    9     15   
use http0::{Method, Uri};
          16  +
use std::borrow::Cow;
   10     17   
use std::error::Error as StdError;
          18  +
use std::time::{Duration, SystemTime};
          19  +
use time::format_description::well_known::Rfc3339;
          20  +
use time::OffsetDateTime;
          21  +
          22  +
/// Common test suite collection
          23  +
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
          24  +
enum Collection {
          25  +
    V4,
          26  +
    V4A,
          27  +
}
   11     28   
   12         -
pub(crate) mod v4 {
   13         -
    use super::*;
          29  +
/// A test from the common CRT test suite
          30  +
#[derive(Debug, Clone)]
          31  +
pub(crate) struct SigningSuiteTest {
          32  +
    test_name: &'static str,
          33  +
    collection: Collection,
          34  +
}
   14     35   
   15         -
    fn path(name: &str, ext: &str) -> String {
   16         -
        format!("aws-sig-v4-test-suite/{}/{}.{}", name, name, ext)
          36  +
impl SigningSuiteTest {
          37  +
    /// Create a new test from the V4 test suite
          38  +
    pub(crate) fn v4(test_name: &'static str) -> Self {
          39  +
        Self {
          40  +
            test_name,
          41  +
            collection: Collection::V4,
   17     42   
        }
   18         -
   19         -
    pub(crate) fn test_canonical_request(name: &str) -> String {
   20         -
        // Tests fail if there's a trailing newline in the file, and pre-commit requires trailing newlines
   21         -
        read(&path(name, "creq")).trim().to_string()
   22     43   
    }
   23     44   
   24         -
    pub(crate) fn test_sts(name: &str) -> String {
   25         -
        read(&path(name, "sts"))
          45  +
    /// Create a new test from the V4a test suite
          46  +
    pub(crate) fn v4a(test_name: &'static str) -> Self {
          47  +
        Self {
          48  +
            test_name,
          49  +
            collection: Collection::V4A,
   26     50   
        }
   27         -
   28         -
    pub(crate) fn test_request(name: &str) -> TestRequest {
   29         -
        test_parsed_request(name, "req")
   30     51   
    }
   31     52   
   32         -
    pub(crate) fn test_signed_request(name: &str) -> TestRequest {
   33         -
        test_parsed_request(name, "sreq")
          53  +
    /// Get the path to a file in this test suite directory
          54  +
    fn path(&self, filename: &str) -> String {
          55  +
        let dir = match self.collection {
          56  +
            Collection::V4 => "v4",
          57  +
            Collection::V4A => "v4a",
          58  +
        };
          59  +
        format!("aws-signing-test-suite/{dir}/{}/{filename}", self.test_name)
   34     60   
    }
   35     61   
   36         -
    pub(crate) fn test_signed_request_query_params(name: &str) -> TestRequest {
   37         -
        test_parsed_request(name, "qpsreq")
          62  +
    /// Get the HTTP request for the test
          63  +
    pub(crate) fn request(&self) -> TestRequest {
          64  +
        test_parsed_request(&self.path("request.txt"))
   38     65   
    }
   39     66   
   40         -
    fn test_parsed_request(name: &str, ext: &str) -> TestRequest {
   41         -
        let path = path(name, ext);
   42         -
        match parse_request(read(&path).as_bytes()) {
   43         -
            Ok(parsed) => parsed,
   44         -
            Err(err) => panic!("Failed to parse {}: {}", path, err),
          67  +
    /// Get the signed HTTP request for the test
          68  +
    pub(crate) fn signed_request(&self, signature_location: SignatureLocation) -> TestRequest {
          69  +
        match signature_location {
          70  +
            SignatureLocation::QueryParams => {
          71  +
                test_parsed_request(&self.path("query-signed-request.txt"))
   45     72   
            }
          73  +
            SignatureLocation::Headers => {
          74  +
                test_parsed_request(&self.path("header-signed-request.txt"))
   46     75   
            }
   47         -
   48         -
    #[test]
   49         -
    fn test_parse() {
   50         -
        test_request("post-header-key-case");
   51     76   
        }
   52         -
   53         -
    #[test]
   54         -
    fn test_read_query_params() {
   55         -
        test_request("get-vanilla-query-order-key-case");
   56     77   
    }
   57         -
}
   58         -
   59         -
#[cfg(feature = "sigv4a")]
   60         -
pub(crate) mod v4a {
   61         -
    use super::*;
   62         -
    use crate::http_request::{
   63         -
        PayloadChecksumKind, SessionTokenMode, SignatureLocation, SigningSettings,
   64         -
    };
   65         -
    use aws_credential_types::Credentials;
   66         -
    use aws_smithy_runtime_api::client::identity::Identity;
   67         -
    use serde_derive::Deserialize;
   68         -
    use std::time::{Duration, SystemTime};
   69         -
    use time::format_description::well_known::Rfc3339;
   70         -
    use time::OffsetDateTime;
   71     78   
   72         -
    fn path(test_name: &str, definition_name: &str) -> String {
   73         -
        format!("aws-sig-v4a-test-suite/{test_name}/{definition_name}.txt")
          79  +
    /// Get the canonical request for the test
          80  +
    pub(crate) fn canonical_request(&self, signature_location: SignatureLocation) -> String {
          81  +
        match signature_location {
          82  +
            SignatureLocation::QueryParams => read(&self.path("query-canonical-request.txt")),
          83  +
            SignatureLocation::Headers => read(&self.path("header-canonical-request.txt")),
   74     84   
        }
   75         -
   76         -
    pub(crate) fn test_request(name: &str) -> TestRequest {
   77         -
        test_parsed_request(&path(name, "request"))
   78     85   
    }
   79     86   
   80         -
    pub(crate) fn test_canonical_request(
   81         -
        name: &str,
   82         -
        signature_location: SignatureLocation,
   83         -
    ) -> String {
          87  +
    /// Get the string to sign for the test
          88  +
    pub(crate) fn string_to_sign(&self, signature_location: SignatureLocation) -> String {
   84     89   
        match signature_location {
   85         -
            SignatureLocation::QueryParams => read(&path(name, "query-canonical-request")),
   86         -
            SignatureLocation::Headers => read(&path(name, "header-canonical-request")),
          90  +
            SignatureLocation::QueryParams => read(&self.path("query-string-to-sign.txt")),
          91  +
            SignatureLocation::Headers => read(&self.path("header-string-to-sign.txt")),
   87     92   
        }
   88     93   
    }
   89     94   
   90         -
    pub(crate) fn test_string_to_sign(name: &str, signature_location: SignatureLocation) -> String {
          95  +
    /// Get the signature for the test
          96  +
    pub(crate) fn signature(&self, signature_location: SignatureLocation) -> String {
   91     97   
        match signature_location {
   92         -
            SignatureLocation::QueryParams => read(&path(name, "query-string-to-sign")),
   93         -
            SignatureLocation::Headers => read(&path(name, "header-string-to-sign")),
          98  +
            SignatureLocation::QueryParams => read(&self.path("query-signature.txt")),
          99  +
            SignatureLocation::Headers => read(&self.path("header-signature.txt")),
         100  +
        }
   94    101   
    }
         102  +
         103  +
    /// Get the test context for the test
         104  +
    pub(crate) fn context(&self) -> TestContext {
         105  +
        let context = read(&self.path("context.json"));
         106  +
        let tc_builder: TestContextBuilder = serde_json::from_str(&context).unwrap();
         107  +
        tc_builder.build()
   95    108   
    }
         109  +
}
   96    110   
   97         -
    fn test_parsed_request(path: &str) -> TestRequest {
         111  +
fn test_parsed_request(path: &str) -> TestRequest {
   98    112   
    match parse_request(read(path).as_bytes()) {
   99    113   
        Ok(parsed) => parsed,
  100    114   
        Err(err) => panic!("Failed to parse {}: {}", path, err),
  101    115   
    }
  102         -
    }
         116  +
}
  103    117   
  104         -
    pub(crate) fn test_context(test_name: &str) -> TestContext {
  105         -
        let path = format!("aws-sig-v4a-test-suite/{test_name}/context.json");
  106         -
        let context = read(&path);
  107         -
        let tc_builder: TestContextBuilder = serde_json::from_str(&context).unwrap();
  108         -
        tc_builder.build()
  109         -
    }
         118  +
fn new_v4_signing_params_from_context(
         119  +
    test_context: &'_ TestContext,
         120  +
    signature_location: SignatureLocation,
         121  +
) -> crate::http_request::SigningParams<'_> {
         122  +
    let mut params = crate::sign::v4::SigningParams::from(test_context);
         123  +
    params.settings.signature_location = signature_location;
         124  +
    params.into()
         125  +
}
         126  +
         127  +
/// Run the given test from the v4 suite for both header and query
         128  +
/// signature locations
         129  +
pub(crate) fn run_test_suite_v4(test_name: &'static str) {
         130  +
    run_v4_test(test_name, SignatureLocation::Headers);
         131  +
    run_v4_test(test_name, SignatureLocation::QueryParams);
         132  +
}
         133  +
         134  +
fn assert_uri_eq(expected: &Uri, actual: &Uri) {
         135  +
    assert_eq!(expected.scheme(), actual.scheme());
         136  +
    assert_eq!(expected.authority(), actual.authority());
         137  +
    assert_eq!(expected.path(), actual.path());
         138  +
         139  +
    // query params may be out of order
         140  +
    let mut expected_params: Vec<(Cow<'_, str>, Cow<'_, str>)> =
         141  +
        form_urlencoded::parse(expected.query().unwrap_or_default().as_bytes()).collect();
         142  +
    expected_params.sort();
         143  +
         144  +
    let mut actual_params: Vec<(Cow<'_, str>, Cow<'_, str>)> =
         145  +
        form_urlencoded::parse(actual.query().unwrap_or_default().as_bytes()).collect();
         146  +
    actual_params.sort();
         147  +
         148  +
    assert_eq!(expected_params, actual_params);
         149  +
}
  110    150   
  111         -
    pub(crate) struct TestContext {
         151  +
fn assert_requests_eq(expected: TestRequest, actual: http0::Request<&str>) {
         152  +
    let expected = expected.as_http_request();
         153  +
    let actual = actual;
         154  +
    assert_eq!(expected.method(), actual.method());
         155  +
    assert_eq!(
         156  +
        expected.headers().len(),
         157  +
        actual.headers().len(),
         158  +
        "extra or missing headers"
         159  +
    );
         160  +
    assert_eq!(expected.headers(), actual.headers(), "headers mismatch");
         161  +
    assert_uri_eq(expected.uri(), actual.uri());
         162  +
    assert_eq!(*expected.body(), *actual.body(), "body mismatch");
         163  +
}
         164  +
         165  +
/// Run the given test from the v4 suite for the given signature location
         166  +
pub(crate) fn run_v4_test(test_name: &'static str, signature_location: SignatureLocation) {
         167  +
    let test = SigningSuiteTest::v4(test_name);
         168  +
    let tc = test.context();
         169  +
    let params = new_v4_signing_params_from_context(&tc, signature_location);
         170  +
         171  +
    let req = test.request();
         172  +
    let expected_creq = test.canonical_request(signature_location);
         173  +
    let signable_req = SignableRequest::from(&req);
         174  +
    let actual_creq = CanonicalRequest::from(&signable_req, &params).unwrap();
         175  +
         176  +
    // check canonical request
         177  +
    assert_eq!(
         178  +
        expected_creq,
         179  +
        actual_creq.to_string(),
         180  +
        "canonical request didn't match (signature location: {signature_location:?})"
         181  +
    );
         182  +
         183  +
    let expected_string_to_sign = test.string_to_sign(signature_location);
         184  +
    let hashed_creq = &crate::sign::v4::sha256_hex_string(actual_creq.to_string().as_bytes());
         185  +
    let actual_string_to_sign = StringToSign::new_v4(
         186  +
        *params.time(),
         187  +
        params.region().unwrap(),
         188  +
        params.name(),
         189  +
        hashed_creq,
         190  +
    )
         191  +
    .to_string();
         192  +
         193  +
    // check string to sign
         194  +
    assert_eq!(
         195  +
        expected_string_to_sign, actual_string_to_sign,
         196  +
        "'string to sign' didn't match (signature location: {signature_location:?})"
         197  +
    );
         198  +
         199  +
    let out = crate::http_request::sign(signable_req, &params).unwrap();
         200  +
    let mut signed = req.as_http_request();
         201  +
    out.output.apply_to_request_http0x(&mut signed);
         202  +
         203  +
    // check signature
         204  +
    assert_eq!(
         205  +
        test.signature(signature_location),
         206  +
        out.signature,
         207  +
        "signature didn't match (signature location: {signature_location:?})"
         208  +
    );
         209  +
         210  +
    let expected = test.signed_request(signature_location);
         211  +
    assert_requests_eq(expected, signed);
         212  +
}
         213  +
         214  +
/// Test suite context.json
         215  +
pub(crate) struct TestContext {
  112    216   
    pub(crate) identity: Identity,
  113    217   
    pub(crate) expiration_in_seconds: u64,
  114    218   
    pub(crate) normalize: bool,
  115    219   
    pub(crate) region: String,
  116    220   
    pub(crate) service: String,
  117    221   
    pub(crate) timestamp: String,
  118    222   
    pub(crate) omit_session_token: bool,
  119    223   
    pub(crate) sign_body: bool,
  120         -
    }
  121         -
  122         -
    impl<'a> From<&'a TestContext> for crate::sign::v4a::SigningParams<'a, SigningSettings> {
  123         -
        fn from(tc: &'a TestContext) -> Self {
  124         -
            crate::sign::v4a::SigningParams {
  125         -
                identity: &tc.identity,
  126         -
                region_set: &tc.region,
  127         -
                name: &tc.service,
  128         -
                time: OffsetDateTime::parse(&tc.timestamp, &Rfc3339)
  129         -
                    .unwrap()
  130         -
                    .into(),
  131         -
                settings: SigningSettings {
  132         -
                    // payload_checksum_kind: PayloadChecksumKind::XAmzSha256,
  133         -
                    expires_in: Some(Duration::from_secs(tc.expiration_in_seconds)),
  134         -
                    uri_path_normalization_mode: tc.normalize.into(),
  135         -
                    session_token_mode: if tc.omit_session_token {
  136         -
                        SessionTokenMode::Exclude
  137         -
                    } else {
  138         -
                        SessionTokenMode::Include
  139         -
                    },
  140         -
                    payload_checksum_kind: if tc.sign_body {
  141         -
                        PayloadChecksumKind::XAmzSha256
  142         -
                    } else {
  143         -
                        PayloadChecksumKind::NoHeader
  144         -
                    },
  145         -
                    ..Default::default()
  146         -
                },
  147         -
            }
  148         -
        }
  149         -
    }
         224  +
}
  150    225   
  151         -
    // Serde has limitations requiring this odd workaround.
  152         -
    // See https://github.com/serde-rs/serde/issues/368 for more info.
  153         -
    fn return_true() -> bool {
         226  +
// Serde has limitations requiring this odd workaround.
         227  +
// See https://github.com/serde-rs/serde/issues/368 for more info.
         228  +
fn return_true() -> bool {
  154    229   
    true
  155         -
    }
         230  +
}
  156    231   
  157         -
    #[derive(Deserialize)]
  158         -
    pub(crate) struct TestContextBuilder {
         232  +
#[derive(serde_derive::Deserialize)]
         233  +
pub(crate) struct TestContextBuilder {
  159    234   
    credentials: TestContextCreds,
  160    235   
    expiration_in_seconds: u64,
  161    236   
    normalize: bool,
  162    237   
    region: String,
  163    238   
    service: String,
  164    239   
    timestamp: String,
  165    240   
    #[serde(default)]
  166    241   
    omit_session_token: bool,
  167    242   
    #[serde(default = "return_true")]
  168    243   
    sign_body: bool,
  169         -
    }
         244  +
}
  170    245   
  171         -
    impl TestContextBuilder {
         246  +
impl TestContextBuilder {
  172    247   
    pub(crate) fn build(self) -> TestContext {
  173    248   
        let identity = Identity::new(
  174    249   
            Credentials::from_keys(
  175    250   
                &self.credentials.access_key_id,
  176    251   
                &self.credentials.secret_access_key,
  177    252   
                self.credentials.token.clone(),
  178    253   
            ),
  179    254   
            Some(SystemTime::UNIX_EPOCH + Duration::from_secs(self.expiration_in_seconds)),
  180    255   
        );
  181    256   
  182    257   
        TestContext {
  183    258   
            identity,
  184    259   
            expiration_in_seconds: self.expiration_in_seconds,
  185    260   
            normalize: self.normalize,
  186    261   
            region: self.region,
  187    262   
            service: self.service,
  188    263   
            timestamp: self.timestamp,
  189    264   
            omit_session_token: self.omit_session_token,
  190    265   
            sign_body: self.sign_body,
  191    266   
        }
  192    267   
    }
  193         -
    }
         268  +
}
  194    269   
  195         -
    #[derive(Deserialize)]
  196         -
    pub(crate) struct TestContextCreds {
         270  +
#[derive(serde_derive::Deserialize)]
         271  +
pub(crate) struct TestContextCreds {
  197    272   
    access_key_id: String,
  198    273   
    secret_access_key: String,
  199    274   
    token: Option<String>,
         275  +
}
         276  +
         277  +
impl<'a> From<&'a TestContext> for crate::sign::v4::SigningParams<'a, SigningSettings> {
         278  +
    fn from(tc: &'a TestContext) -> Self {
         279  +
        crate::sign::v4::SigningParams {
         280  +
            identity: &tc.identity,
         281  +
            region: &tc.region,
         282  +
            name: &tc.service,
         283  +
            time: OffsetDateTime::parse(&tc.timestamp, &Rfc3339)
         284  +
                .unwrap()
         285  +
                .into(),
         286  +
            settings: SigningSettings {
         287  +
                // payload_checksum_kind: PayloadChecksumKind::XAmzSha256,
         288  +
                expires_in: Some(Duration::from_secs(tc.expiration_in_seconds)),
         289  +
                uri_path_normalization_mode: tc.normalize.into(),
         290  +
                session_token_mode: if tc.omit_session_token {
         291  +
                    SessionTokenMode::Exclude
         292  +
                } else {
         293  +
                    SessionTokenMode::Include
         294  +
                },
         295  +
                payload_checksum_kind: if tc.sign_body {
         296  +
                    PayloadChecksumKind::XAmzSha256
         297  +
                } else {
         298  +
                    PayloadChecksumKind::NoHeader
         299  +
                },
         300  +
                ..Default::default()
         301  +
            },
         302  +
        }
         303  +
    }
         304  +
}
         305  +
         306  +
#[cfg(feature = "sigv4a")]
         307  +
pub(crate) mod v4a {
         308  +
    use super::*;
         309  +
    use crate::http_request::{
         310  +
        sign, PayloadChecksumKind, SessionTokenMode, SignatureLocation, SigningSettings,
         311  +
    };
         312  +
    use crate::sign::v4a;
         313  +
    use p256::ecdsa::signature::{Signature, Verifier};
         314  +
    use p256::ecdsa::{DerSignature, SigningKey};
         315  +
    use std::time::Duration;
         316  +
    use time::format_description::well_known::Rfc3339;
         317  +
    use time::OffsetDateTime;
         318  +
         319  +
    fn new_v4a_signing_params_from_context(
         320  +
        test_context: &'_ TestContext,
         321  +
        signature_location: SignatureLocation,
         322  +
    ) -> crate::http_request::SigningParams<'_> {
         323  +
        let mut params = crate::sign::v4a::SigningParams::from(test_context);
         324  +
        params.settings.signature_location = signature_location;
         325  +
        params.into()
         326  +
    }
         327  +
         328  +
    pub(crate) fn run_test_suite_v4a(test_name: &'static str) {
         329  +
        run_v4a_test(test_name, SignatureLocation::Headers);
         330  +
        run_v4a_test(test_name, SignatureLocation::QueryParams);
         331  +
    }
         332  +
         333  +
    pub(crate) fn run_v4a_test(test_name: &'static str, signature_location: SignatureLocation) {
         334  +
        let test = SigningSuiteTest::v4a(test_name);
         335  +
        let tc = test.context();
         336  +
        let params = new_v4a_signing_params_from_context(&tc, signature_location);
         337  +
         338  +
        let req = test.request();
         339  +
        let expected_creq = test.canonical_request(signature_location);
         340  +
        let signable_req = SignableRequest::from(&req);
         341  +
        let actual_creq = CanonicalRequest::from(&signable_req, &params).unwrap();
         342  +
         343  +
        assert_eq!(
         344  +
            expected_creq,
         345  +
            actual_creq.to_string(),
         346  +
            "canonical request didn't match (signature location: {signature_location:?})"
         347  +
        );
         348  +
         349  +
        let expected_string_to_sign = test.string_to_sign(signature_location);
         350  +
        let hashed_creq = &crate::sign::v4::sha256_hex_string(actual_creq.to_string().as_bytes());
         351  +
        let actual_string_to_sign = StringToSign::new_v4a(
         352  +
            *params.time(),
         353  +
            params.region_set().unwrap(),
         354  +
            params.name(),
         355  +
            hashed_creq,
         356  +
        )
         357  +
        .to_string();
         358  +
         359  +
        assert_eq!(
         360  +
            expected_string_to_sign, actual_string_to_sign,
         361  +
            "'string to sign' didn't match (signature location: {signature_location:?})"
         362  +
        );
         363  +
         364  +
        let out = sign(signable_req, &params).unwrap();
         365  +
        // Sigv4a signatures are non-deterministic, so we can't compare the signature directly.
         366  +
        out.output
         367  +
            .apply_to_request_http0x(&mut req.as_http_request());
         368  +
         369  +
        let creds = params.credentials().unwrap();
         370  +
        let signing_key =
         371  +
            v4a::generate_signing_key(creds.access_key_id(), creds.secret_access_key());
         372  +
        let sig = DerSignature::from_bytes(&hex::decode(out.signature).unwrap()).unwrap();
         373  +
        let sig = sig
         374  +
            .try_into()
         375  +
            .expect("DER-style signatures are always convertible into fixed-size signatures");
         376  +
         377  +
        let signing_key = SigningKey::from_bytes(signing_key.as_ref()).unwrap();
         378  +
        let peer_public_key = signing_key.verifying_key();
         379  +
        let sts = actual_string_to_sign.as_bytes();
         380  +
        peer_public_key.verify(sts, &sig).unwrap();
         381  +
        // TODO(sigv4a) - use public.key.json as verifying key?
         382  +
    }
         383  +
         384  +
    impl<'a> From<&'a TestContext> for crate::sign::v4a::SigningParams<'a, SigningSettings> {
         385  +
        fn from(tc: &'a TestContext) -> Self {
         386  +
            crate::sign::v4a::SigningParams {
         387  +
                identity: &tc.identity,
         388  +
                region_set: &tc.region,
         389  +
                name: &tc.service,
         390  +
                time: OffsetDateTime::parse(&tc.timestamp, &Rfc3339)
         391  +
                    .unwrap()
         392  +
                    .into(),
         393  +
                settings: SigningSettings {
         394  +
                    // payload_checksum_kind: PayloadChecksumKind::XAmzSha256,
         395  +
                    expires_in: Some(Duration::from_secs(tc.expiration_in_seconds)),
         396  +
                    uri_path_normalization_mode: tc.normalize.into(),
         397  +
                    session_token_mode: if tc.omit_session_token {
         398  +
                        SessionTokenMode::Exclude
         399  +
                    } else {
         400  +
                        SessionTokenMode::Include
         401  +
                    },
         402  +
                    payload_checksum_kind: if tc.sign_body {
         403  +
                        PayloadChecksumKind::XAmzSha256
         404  +
                    } else {
         405  +
                        PayloadChecksumKind::NoHeader
         406  +
                    },
         407  +
                    ..Default::default()
         408  +
                },
         409  +
            }
         410  +
        }
  200    411   
    }
  201    412   
  202    413   
    #[test]
  203    414   
    fn test_parse() {
  204         -
        let req = test_request("post-header-key-case");
         415  +
        let req = SigningSuiteTest::v4a("post-header-key-case").request();
  205    416   
        assert_eq!(req.method, "POST");
  206    417   
        assert_eq!(req.uri, "https://example.amazonaws.com/");
  207    418   
        assert!(req.headers.is_empty());
  208    419   
    }
  209    420   
  210    421   
    #[test]
  211    422   
    fn test_read_query_params() {
  212         -
        let req = test_request("get-header-value-trim");
         423  +
        let req = SigningSuiteTest::v4a("get-header-value-trim").request();
  213    424   
        assert_eq!(req.method, "GET");
  214    425   
        assert_eq!(req.uri, "https://example.amazonaws.com/");
  215    426   
        assert!(!req.headers.is_empty());
  216    427   
    }
  217    428   
}
  218    429   
  219    430   
fn read(path: &str) -> String {
  220    431   
    println!("Loading `{}` for test case...", path);
  221    432   
    let v = {
  222    433   
        match std::fs::read_to_string(path) {
@@ -290,501 +368,587 @@
  310    521   
        .expect("URI MUST be valid")
  311    522   
    }
  312    523   
}
  313    524   
  314    525   
fn parse_request(s: &[u8]) -> Result<TestRequest, Box<dyn StdError + Send + Sync + 'static>> {
  315    526   
    let mut headers = [httparse::EMPTY_HEADER; 64];
  316    527   
    // httparse 1.5 requires two trailing newlines to head the header section.
  317    528   
    let mut with_newline = Vec::from(s);
  318    529   
    with_newline.push(b'\n');
  319    530   
    let mut req = httparse::Request::new(&mut headers);
  320         -
    let _ = req.parse(&with_newline).unwrap();
         531  +
    let status = req.parse(&with_newline).unwrap();
         532  +
         533  +
    let body = if status.is_complete() {
         534  +
        let body_offset = status.unwrap();
         535  +
        // ignore the newline we added, take from original
         536  +
        &s[body_offset..]
         537  +
    } else {
         538  +
        &[]
         539  +
    };
  321    540   
  322    541   
    let mut uri_builder = Uri::builder().scheme("https");
  323    542   
    if let Some(path) = req.path {
  324    543   
        uri_builder = uri_builder.path_and_query(path);
  325    544   
    }
  326    545   
  327    546   
    let mut headers = vec![];
  328    547   
    for header in req.headers {
  329    548   
        let name = header.name.to_lowercase();
  330    549   
        if name == "host" {
  331    550   
            uri_builder = uri_builder.authority(header.value);
  332    551   
        } else if !name.is_empty() {
  333    552   
            headers.push((
  334    553   
                header.name.to_string(),
  335    554   
                std::str::from_utf8(header.value)?.to_string(),
  336    555   
            ));
  337    556   
        }
  338    557   
    }
  339    558   
  340    559   
    Ok(TestRequest {
  341    560   
        uri: uri_builder.build()?.to_string(),
  342    561   
        method: req.method.unwrap().to_string(),
  343    562   
        headers,
  344         -
        body: TestSignedBody::Bytes(vec![]),
         563  +
        body: TestSignedBody::Bytes(Vec::from(body)),
  345    564   
    })
  346    565   
}
  347    566   
  348    567   
#[test]
  349    568   
fn test_parse_headers() {
  350    569   
    let buf = b"Host:example.amazonaws.com\nX-Amz-Date:20150830T123600Z\n\nblah blah";
  351    570   
    let mut headers = [httparse::EMPTY_HEADER; 4];
  352    571   
    assert_eq!(
  353    572   
        httparse::parse_headers(buf, &mut headers),
  354    573   
        Ok(httparse::Status::Complete((

tmp-codegen-diff/aws-sdk/sdk/aws-sigv4/src/sign/v4.rs

@@ -168,168 +219,221 @@
  188    188   
                    .ok_or_else(|| BuildError::new("settings are required"))?,
  189    189   
            })
  190    190   
        }
  191    191   
    }
  192    192   
}
  193    193   
  194    194   
#[cfg(test)]
  195    195   
mod tests {
  196    196   
    use super::{calculate_signature, generate_signing_key, sha256_hex_string};
  197    197   
    use crate::date_time::test_parsers::parse_date_time;
  198         -
    use crate::http_request::test;
  199    198   
  200    199   
    #[test]
  201    200   
    fn test_signature_calculation() {
  202    201   
        let secret = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";
  203         -
        let creq = test::v4::test_canonical_request("iam");
         202  +
        let creq = r#"AWS4-HMAC-SHA256
         203  +
20150830T123600Z
         204  +
20150830/us-east-1/iam/aws4_request
         205  +
f536975d06c0309214f805bb90ccff089219ecd68b2577efef23edd43b7e1a59"#;
  204    206   
        let time = parse_date_time("20150830T123600Z").unwrap();
  205    207   
  206    208   
        let derived_key = generate_signing_key(secret, time, "us-east-1", "iam");
  207    209   
        let signature = calculate_signature(derived_key, creq.as_bytes());
  208    210   
  209    211   
        let expected = "5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7";
  210    212   
        assert_eq!(expected, &signature);
  211    213   
    }
  212    214   
  213    215   
    #[test]

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-checksums/Cargo.toml

@@ -1,1 +43,43 @@
   20     20   
http = "0.2.9"
   21     21   
http-body = "0.4.5"
   22     22   
md-5 = "0.10"
   23     23   
pin-project-lite = "0.2.14"
   24     24   
sha1 = "0.10"
   25     25   
sha2 = "0.10"
   26     26   
tracing = "0.1.40"
   27     27   
   28     28   
[dependencies.aws-smithy-http]
   29     29   
path = "../aws-smithy-http"
   30         -
version = "0.62.2"
          30  +
version = "0.62.3"
   31     31   
   32     32   
[dependencies.aws-smithy-types]
   33     33   
path = "../aws-smithy-types"
   34     34   
version = "1.3.2"
   35     35   
   36     36   
[dev-dependencies]
   37     37   
bytes-utils = "0.1.2"
   38     38   
pretty_assertions = "1.3"
   39     39   
tracing-test = "0.2.1"
   40     40   

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http/Cargo.toml

@@ -1,1 +34,34 @@
    1      1   
# Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT.
    2      2   
[package]
    3      3   
name = "aws-smithy-http"
    4         -
version = "0.62.2"
           4  +
version = "0.62.3"
    5      5   
authors = ["AWS Rust SDK Team <aws-sdk-rust@amazon.com>", "Russell Cohen <rcoh@amazon.com>"]
    6      6   
description = "Smithy HTTP logic for smithy-rs."
    7      7   
edition = "2021"
    8      8   
license = "Apache-2.0"
    9      9   
repository = "https://github.com/smithy-lang/smithy-rs"
   10     10   
[package.metadata.docs.rs]
   11     11   
all-features = true
   12     12   
targets = ["x86_64-unknown-linux-gnu"]
   13     13   
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
   14     14   
rustdoc-args = ["--cfg", "docsrs"]

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http/fuzz/Cargo.toml

@@ -1,1 +27,27 @@
   14     14   
   15     15   
[package.metadata]
   16     16   
cargo-fuzz = true
   17     17   
   18     18   
[dependencies]
   19     19   
libfuzzer-sys = "=0.4.7"
   20     20   
http = "0.2.3"
   21     21   
   22     22   
[dependencies.aws-smithy-http]
   23     23   
path = ".."
   24         -
version = "0.62.2"
          24  +
version = "0.62.3"
   25     25   
   26     26   
[workspace]
   27     27   
members = ["."]

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-http/src/query_writer.rs

@@ -25,25 +92,98 @@
   45     45   
    pub fn clear_params(&mut self) {
   46     46   
        if let Some(index) = self.new_path_and_query.find('?') {
   47     47   
            self.new_path_and_query.truncate(index);
   48     48   
            self.prefix = Some('?');
   49     49   
        }
   50     50   
    }
   51     51   
   52     52   
    /// Inserts a new query parameter. The key and value are percent encoded
   53     53   
    /// by `QueryWriter`. Passing in percent encoded values will result in double encoding.
   54     54   
    pub fn insert(&mut self, k: &str, v: &str) {
          55  +
        self.insert_encoded(&percent_encode_query(k), &percent_encode_query(v));
          56  +
    }
          57  +
    
          58  +
    /// Inserts a new already encoded query parameter. The key and value will be inserted
          59  +
    /// as is.
          60  +
    pub fn insert_encoded(&mut self, encoded_k: &str, encoded_v: &str) {
   55     61   
        if let Some(prefix) = self.prefix {
   56     62   
            self.new_path_and_query.push(prefix);
   57     63   
        }
   58     64   
        self.prefix = Some('&');
   59         -
        self.new_path_and_query.push_str(&percent_encode_query(k));
          65  +
        self.new_path_and_query.push_str(encoded_k);
   60     66   
        self.new_path_and_query.push('=');
          67  +
        self.new_path_and_query.push_str(encoded_v)
   61     68   
        
   62         -
        self.new_path_and_query.push_str(&percent_encode_query(v));
   63     69   
    }
   64     70   
   65     71   
    /// Returns just the built query string.
   66     72   
    pub fn build_query(self) -> String {
   67     73   
        self.build_uri().query().unwrap_or_default().to_string()
   68     74   
    }
   69     75   
   70     76   
    /// Returns a full [`Uri`] with the query string updated.
   71     77   
    pub fn build_uri(self) -> Uri {
   72     78   
        let mut parts = self.base_uri.into_parts();