diff options
author | nalpp <nalpp@yandex-team.ru> | 2022-02-10 16:46:46 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:46:46 +0300 |
commit | 30d1ef3941e0dc835be7609de5ebee66958f215a (patch) | |
tree | 49e222ea1c5804306084bb3ae065bb702625360f /contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/AWSAuthSigner.cpp | |
parent | 87f3eb38999df2d3c1cb77f8ffb9c52ec9c516fb (diff) | |
download | ydb-30d1ef3941e0dc835be7609de5ebee66958f215a.tar.gz |
Restoring authorship annotation for <nalpp@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/AWSAuthSigner.cpp')
-rw-r--r-- | contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/AWSAuthSigner.cpp | 964 |
1 files changed, 482 insertions, 482 deletions
diff --git a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/AWSAuthSigner.cpp b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/AWSAuthSigner.cpp index fa3f5583d5..de4826fa5b 100644 --- a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/AWSAuthSigner.cpp +++ b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/AWSAuthSigner.cpp @@ -2,536 +2,536 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ - -#include <aws/core/auth/AWSAuthSigner.h> - -#include <aws/core/auth/AWSCredentialsProvider.h> -#include <aws/core/client/ClientConfiguration.h> -#include <aws/core/http/HttpRequest.h> -#include <aws/core/http/HttpResponse.h> -#include <aws/core/utils/DateTime.h> -#include <aws/core/utils/HashingUtils.h> -#include <aws/core/utils/Outcome.h> -#include <aws/core/utils/StringUtils.h> -#include <aws/core/utils/logging/LogMacros.h> -#include <aws/core/utils/memory/AWSMemory.h> -#include <aws/core/utils/crypto/Sha256.h> -#include <aws/core/utils/crypto/Sha256HMAC.h> + +#include <aws/core/auth/AWSAuthSigner.h> + +#include <aws/core/auth/AWSCredentialsProvider.h> +#include <aws/core/client/ClientConfiguration.h> +#include <aws/core/http/HttpRequest.h> +#include <aws/core/http/HttpResponse.h> +#include <aws/core/utils/DateTime.h> +#include <aws/core/utils/HashingUtils.h> +#include <aws/core/utils/Outcome.h> +#include <aws/core/utils/StringUtils.h> +#include <aws/core/utils/logging/LogMacros.h> +#include <aws/core/utils/memory/AWSMemory.h> +#include <aws/core/utils/crypto/Sha256.h> +#include <aws/core/utils/crypto/Sha256HMAC.h> #include <aws/core/utils/stream/PreallocatedStreamBuf.h> #include <aws/core/utils/event/EventMessage.h> #include <aws/core/utils/event/EventHeader.h> - -#include <cstdio> -#include <iomanip> -#include <math.h> + +#include <cstdio> +#include <iomanip> +#include <math.h> #include <cstring> - -using namespace Aws; -using namespace Aws::Client; -using namespace Aws::Auth; -using namespace Aws::Http; -using namespace Aws::Utils; -using namespace Aws::Utils::Logging; - -static const char* EQ = "="; -static const char* AWS_HMAC_SHA256 = "AWS4-HMAC-SHA256"; + +using namespace Aws; +using namespace Aws::Client; +using namespace Aws::Auth; +using namespace Aws::Http; +using namespace Aws::Utils; +using namespace Aws::Utils::Logging; + +static const char* EQ = "="; +static const char* AWS_HMAC_SHA256 = "AWS4-HMAC-SHA256"; static const char* EVENT_STREAM_CONTENT_SHA256 = "STREAMING-AWS4-HMAC-SHA256-EVENTS"; static const char* EVENT_STREAM_PAYLOAD = "AWS4-HMAC-SHA256-PAYLOAD"; -static const char* AWS4_REQUEST = "aws4_request"; -static const char* SIGNED_HEADERS = "SignedHeaders"; -static const char* CREDENTIAL = "Credential"; -static const char* NEWLINE = "\n"; -static const char* X_AMZ_SIGNED_HEADERS = "X-Amz-SignedHeaders"; -static const char* X_AMZ_ALGORITHM = "X-Amz-Algorithm"; -static const char* X_AMZ_CREDENTIAL = "X-Amz-Credential"; -static const char* UNSIGNED_PAYLOAD = "UNSIGNED-PAYLOAD"; -static const char* X_AMZ_SIGNATURE = "X-Amz-Signature"; +static const char* AWS4_REQUEST = "aws4_request"; +static const char* SIGNED_HEADERS = "SignedHeaders"; +static const char* CREDENTIAL = "Credential"; +static const char* NEWLINE = "\n"; +static const char* X_AMZ_SIGNED_HEADERS = "X-Amz-SignedHeaders"; +static const char* X_AMZ_ALGORITHM = "X-Amz-Algorithm"; +static const char* X_AMZ_CREDENTIAL = "X-Amz-Credential"; +static const char* UNSIGNED_PAYLOAD = "UNSIGNED-PAYLOAD"; +static const char* X_AMZ_SIGNATURE = "X-Amz-Signature"; static const char* X_AMZN_TRACE_ID = "x-amzn-trace-id"; static const char* X_AMZ_CONTENT_SHA256 = "x-amz-content-sha256"; static const char* USER_AGENT = "user-agent"; -static const char* SIGNING_KEY = "AWS4"; -static const char* SIMPLE_DATE_FORMAT_STR = "%Y%m%d"; -static const char* EMPTY_STRING_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; - +static const char* SIGNING_KEY = "AWS4"; +static const char* SIMPLE_DATE_FORMAT_STR = "%Y%m%d"; +static const char* EMPTY_STRING_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; + static const char v4LogTag[] = "AWSAuthV4Signer"; static const char v4StreamingLogTag[] = "AWSAuthEventStreamV4Signer"; - -namespace Aws -{ - namespace Auth - { + +namespace Aws +{ + namespace Auth + { const char SIGNATURE[] = "Signature"; - const char SIGV4_SIGNER[] = "SignatureV4"; + const char SIGV4_SIGNER[] = "SignatureV4"; const char EVENTSTREAM_SIGV4_SIGNER[] = "EventStreamSignatureV4"; const char EVENTSTREAM_SIGNATURE_HEADER[] = ":chunk-signature"; const char EVENTSTREAM_DATE_HEADER[] = ":date"; - const char NULL_SIGNER[] = "NullSigner"; - } -} - -static Aws::String CanonicalizeRequestSigningString(HttpRequest& request, bool urlEscapePath) -{ - request.CanonicalizeRequest(); - Aws::StringStream signingStringStream; - signingStringStream << HttpMethodMapper::GetNameForHttpMethod(request.GetMethod()); - - URI uriCpy = request.GetUri(); - // Many AWS services do not decode the URL before calculating SignatureV4 on their end. - // This results in the signature getting calculated with a double encoded URL. - // That means we have to double encode it here for the signature to match on the service side. - if(urlEscapePath) - { - // RFC3986 is how we encode the URL before sending it on the wire. - auto rfc3986EncodedPath = URI::URLEncodePathRFC3986(uriCpy.GetPath()); - uriCpy.SetPath(rfc3986EncodedPath); - // However, SignatureV4 uses this URL encoding scheme - signingStringStream << NEWLINE << uriCpy.GetURLEncodedPath() << NEWLINE; - } - else - { - // For the services that DO decode the URL first; we don't need to double encode it. - uriCpy.SetPath(uriCpy.GetURLEncodedPath()); - signingStringStream << NEWLINE << uriCpy.GetPath() << NEWLINE; - } - + const char NULL_SIGNER[] = "NullSigner"; + } +} + +static Aws::String CanonicalizeRequestSigningString(HttpRequest& request, bool urlEscapePath) +{ + request.CanonicalizeRequest(); + Aws::StringStream signingStringStream; + signingStringStream << HttpMethodMapper::GetNameForHttpMethod(request.GetMethod()); + + URI uriCpy = request.GetUri(); + // Many AWS services do not decode the URL before calculating SignatureV4 on their end. + // This results in the signature getting calculated with a double encoded URL. + // That means we have to double encode it here for the signature to match on the service side. + if(urlEscapePath) + { + // RFC3986 is how we encode the URL before sending it on the wire. + auto rfc3986EncodedPath = URI::URLEncodePathRFC3986(uriCpy.GetPath()); + uriCpy.SetPath(rfc3986EncodedPath); + // However, SignatureV4 uses this URL encoding scheme + signingStringStream << NEWLINE << uriCpy.GetURLEncodedPath() << NEWLINE; + } + else + { + // For the services that DO decode the URL first; we don't need to double encode it. + uriCpy.SetPath(uriCpy.GetURLEncodedPath()); + signingStringStream << NEWLINE << uriCpy.GetPath() << NEWLINE; + } + if (request.GetQueryString().find('=') != std::string::npos) - { - signingStringStream << request.GetQueryString().substr(1) << NEWLINE; - } - else if (request.GetQueryString().size() > 1) - { - signingStringStream << request.GetQueryString().substr(1) << "=" << NEWLINE; - } - else - { - signingStringStream << NEWLINE; - } - - return signingStringStream.str(); -} - -static Http::HeaderValueCollection CanonicalizeHeaders(Http::HeaderValueCollection&& headers) -{ - Http::HeaderValueCollection canonicalHeaders; - for (const auto& header : headers) - { - auto trimmedHeaderName = StringUtils::Trim(header.first.c_str()); - auto trimmedHeaderValue = StringUtils::Trim(header.second.c_str()); - - //multiline gets converted to line1,line2,etc... - auto headerMultiLine = StringUtils::SplitOnLine(trimmedHeaderValue); - Aws::String headerValue = headerMultiLine.size() == 0 ? "" : headerMultiLine[0]; - - if (headerMultiLine.size() > 1) - { - for(size_t i = 1; i < headerMultiLine.size(); ++i) - { - headerValue += ","; - headerValue += StringUtils::Trim(headerMultiLine[i].c_str()); - } - } - - //duplicate spaces need to be converted to one. - Aws::String::iterator new_end = - std::unique(headerValue.begin(), headerValue.end(), - [=](char lhs, char rhs) { return (lhs == rhs) && (lhs == ' '); } - ); - headerValue.erase(new_end, headerValue.end()); - + { + signingStringStream << request.GetQueryString().substr(1) << NEWLINE; + } + else if (request.GetQueryString().size() > 1) + { + signingStringStream << request.GetQueryString().substr(1) << "=" << NEWLINE; + } + else + { + signingStringStream << NEWLINE; + } + + return signingStringStream.str(); +} + +static Http::HeaderValueCollection CanonicalizeHeaders(Http::HeaderValueCollection&& headers) +{ + Http::HeaderValueCollection canonicalHeaders; + for (const auto& header : headers) + { + auto trimmedHeaderName = StringUtils::Trim(header.first.c_str()); + auto trimmedHeaderValue = StringUtils::Trim(header.second.c_str()); + + //multiline gets converted to line1,line2,etc... + auto headerMultiLine = StringUtils::SplitOnLine(trimmedHeaderValue); + Aws::String headerValue = headerMultiLine.size() == 0 ? "" : headerMultiLine[0]; + + if (headerMultiLine.size() > 1) + { + for(size_t i = 1; i < headerMultiLine.size(); ++i) + { + headerValue += ","; + headerValue += StringUtils::Trim(headerMultiLine[i].c_str()); + } + } + + //duplicate spaces need to be converted to one. + Aws::String::iterator new_end = + std::unique(headerValue.begin(), headerValue.end(), + [=](char lhs, char rhs) { return (lhs == rhs) && (lhs == ' '); } + ); + headerValue.erase(new_end, headerValue.end()); + canonicalHeaders[trimmedHeaderName] = headerValue; - } - - return canonicalHeaders; -} - -AWSAuthV4Signer::AWSAuthV4Signer(const std::shared_ptr<Auth::AWSCredentialsProvider>& credentialsProvider, - const char* serviceName, const Aws::String& region, PayloadSigningPolicy signingPolicy, bool urlEscapePath) : - m_includeSha256HashHeader(true), - m_credentialsProvider(credentialsProvider), - m_serviceName(serviceName), - m_region(region), - m_hash(Aws::MakeUnique<Aws::Utils::Crypto::Sha256>(v4LogTag)), - m_HMAC(Aws::MakeUnique<Aws::Utils::Crypto::Sha256HMAC>(v4LogTag)), + } + + return canonicalHeaders; +} + +AWSAuthV4Signer::AWSAuthV4Signer(const std::shared_ptr<Auth::AWSCredentialsProvider>& credentialsProvider, + const char* serviceName, const Aws::String& region, PayloadSigningPolicy signingPolicy, bool urlEscapePath) : + m_includeSha256HashHeader(true), + m_credentialsProvider(credentialsProvider), + m_serviceName(serviceName), + m_region(region), + m_hash(Aws::MakeUnique<Aws::Utils::Crypto::Sha256>(v4LogTag)), + m_HMAC(Aws::MakeUnique<Aws::Utils::Crypto::Sha256HMAC>(v4LogTag)), m_unsignedHeaders({USER_AGENT, X_AMZN_TRACE_ID}), - m_payloadSigningPolicy(signingPolicy), - m_urlEscapePath(urlEscapePath) -{ - //go ahead and warm up the signing cache. + m_payloadSigningPolicy(signingPolicy), + m_urlEscapePath(urlEscapePath) +{ + //go ahead and warm up the signing cache. ComputeHash(credentialsProvider->GetAWSCredentials().GetAWSSecretKey(), DateTime::CalculateGmtTimestampAsString(SIMPLE_DATE_FORMAT_STR), region, m_serviceName); -} - -AWSAuthV4Signer::~AWSAuthV4Signer() -{ +} + +AWSAuthV4Signer::~AWSAuthV4Signer() +{ // empty destructor in .cpp file to keep from needing the implementation of (AWSCredentialsProvider, Sha256, Sha256HMAC) in the header file -} - - -bool AWSAuthV4Signer::ShouldSignHeader(const Aws::String& header) const -{ - return m_unsignedHeaders.find(Aws::Utils::StringUtils::ToLower(header.c_str())) == m_unsignedHeaders.cend(); -} - +} + + +bool AWSAuthV4Signer::ShouldSignHeader(const Aws::String& header) const +{ + return m_unsignedHeaders.find(Aws::Utils::StringUtils::ToLower(header.c_str())) == m_unsignedHeaders.cend(); +} + bool AWSAuthV4Signer::SignRequest(Aws::Http::HttpRequest& request, const char* region, const char* serviceName, bool signBody) const -{ - AWSCredentials credentials = m_credentialsProvider->GetAWSCredentials(); - - //don't sign anonymous requests - if (credentials.GetAWSAccessKeyId().empty() || credentials.GetAWSSecretKey().empty()) - { - return true; - } - - if (!credentials.GetSessionToken().empty()) - { - request.SetAwsSessionToken(credentials.GetSessionToken()); - } - - Aws::String payloadHash(UNSIGNED_PAYLOAD); - switch(m_payloadSigningPolicy) - { - case PayloadSigningPolicy::Always: - signBody = true; - break; - case PayloadSigningPolicy::Never: - signBody = false; - break; - case PayloadSigningPolicy::RequestDependent: - // respect the request setting - default: - break; - } - - if(signBody || request.GetUri().GetScheme() != Http::Scheme::HTTPS) - { +{ + AWSCredentials credentials = m_credentialsProvider->GetAWSCredentials(); + + //don't sign anonymous requests + if (credentials.GetAWSAccessKeyId().empty() || credentials.GetAWSSecretKey().empty()) + { + return true; + } + + if (!credentials.GetSessionToken().empty()) + { + request.SetAwsSessionToken(credentials.GetSessionToken()); + } + + Aws::String payloadHash(UNSIGNED_PAYLOAD); + switch(m_payloadSigningPolicy) + { + case PayloadSigningPolicy::Always: + signBody = true; + break; + case PayloadSigningPolicy::Never: + signBody = false; + break; + case PayloadSigningPolicy::RequestDependent: + // respect the request setting + default: + break; + } + + if(signBody || request.GetUri().GetScheme() != Http::Scheme::HTTPS) + { payloadHash = ComputePayloadHash(request); - if (payloadHash.empty()) - { - return false; - } - } - else - { - AWS_LOGSTREAM_DEBUG(v4LogTag, "Note: Http payloads are not being signed. signPayloads=" << signBody - << " http scheme=" << Http::SchemeMapper::ToString(request.GetUri().GetScheme())); - } - - if(m_includeSha256HashHeader) - { + if (payloadHash.empty()) + { + return false; + } + } + else + { + AWS_LOGSTREAM_DEBUG(v4LogTag, "Note: Http payloads are not being signed. signPayloads=" << signBody + << " http scheme=" << Http::SchemeMapper::ToString(request.GetUri().GetScheme())); + } + + if(m_includeSha256HashHeader) + { request.SetHeaderValue(X_AMZ_CONTENT_SHA256, payloadHash); - } - - //calculate date header to use in internal signature (this also goes into date header). - DateTime now = GetSigningTimestamp(); + } + + //calculate date header to use in internal signature (this also goes into date header). + DateTime now = GetSigningTimestamp(); Aws::String dateHeaderValue = now.ToGmtString(DateFormat::ISO_8601_BASIC); - request.SetHeaderValue(AWS_DATE_HEADER, dateHeaderValue); - - Aws::StringStream headersStream; - Aws::StringStream signedHeadersStream; - - for (const auto& header : CanonicalizeHeaders(request.GetHeaders())) - { - if(ShouldSignHeader(header.first)) - { - headersStream << header.first.c_str() << ":" << header.second.c_str() << NEWLINE; - signedHeadersStream << header.first.c_str() << ";"; - } - } - - Aws::String canonicalHeadersString = headersStream.str(); - AWS_LOGSTREAM_DEBUG(v4LogTag, "Canonical Header String: " << canonicalHeadersString); - - //calculate signed headers parameter - Aws::String signedHeadersValue = signedHeadersStream.str(); - //remove that last semi-colon - if (!signedHeadersValue.empty()) - { + request.SetHeaderValue(AWS_DATE_HEADER, dateHeaderValue); + + Aws::StringStream headersStream; + Aws::StringStream signedHeadersStream; + + for (const auto& header : CanonicalizeHeaders(request.GetHeaders())) + { + if(ShouldSignHeader(header.first)) + { + headersStream << header.first.c_str() << ":" << header.second.c_str() << NEWLINE; + signedHeadersStream << header.first.c_str() << ";"; + } + } + + Aws::String canonicalHeadersString = headersStream.str(); + AWS_LOGSTREAM_DEBUG(v4LogTag, "Canonical Header String: " << canonicalHeadersString); + + //calculate signed headers parameter + Aws::String signedHeadersValue = signedHeadersStream.str(); + //remove that last semi-colon + if (!signedHeadersValue.empty()) + { signedHeadersValue.pop_back(); - } - - AWS_LOGSTREAM_DEBUG(v4LogTag, "Signed Headers value:" << signedHeadersValue); - - //generate generalized canonicalized request string. - Aws::String canonicalRequestString = CanonicalizeRequestSigningString(request, m_urlEscapePath); - - //append v4 stuff to the canonical request string. - canonicalRequestString.append(canonicalHeadersString); - canonicalRequestString.append(NEWLINE); - canonicalRequestString.append(signedHeadersValue); - canonicalRequestString.append(NEWLINE); - canonicalRequestString.append(payloadHash); - - AWS_LOGSTREAM_DEBUG(v4LogTag, "Canonical Request String: " << canonicalRequestString); - - //now compute sha256 on that request string - auto hashResult = m_hash->Calculate(canonicalRequestString); - if (!hashResult.IsSuccess()) - { - AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to hash (sha256) request string"); - AWS_LOGSTREAM_DEBUG(v4LogTag, "The request string is: \"" << canonicalRequestString << "\""); - return false; - } - - auto sha256Digest = hashResult.GetResult(); + } + + AWS_LOGSTREAM_DEBUG(v4LogTag, "Signed Headers value:" << signedHeadersValue); + + //generate generalized canonicalized request string. + Aws::String canonicalRequestString = CanonicalizeRequestSigningString(request, m_urlEscapePath); + + //append v4 stuff to the canonical request string. + canonicalRequestString.append(canonicalHeadersString); + canonicalRequestString.append(NEWLINE); + canonicalRequestString.append(signedHeadersValue); + canonicalRequestString.append(NEWLINE); + canonicalRequestString.append(payloadHash); + + AWS_LOGSTREAM_DEBUG(v4LogTag, "Canonical Request String: " << canonicalRequestString); + + //now compute sha256 on that request string + auto hashResult = m_hash->Calculate(canonicalRequestString); + if (!hashResult.IsSuccess()) + { + AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to hash (sha256) request string"); + AWS_LOGSTREAM_DEBUG(v4LogTag, "The request string is: \"" << canonicalRequestString << "\""); + return false; + } + + auto sha256Digest = hashResult.GetResult(); Aws::String canonicalRequestHash = HashingUtils::HexEncode(sha256Digest); - Aws::String simpleDate = now.ToGmtString(SIMPLE_DATE_FORMAT_STR); - + Aws::String simpleDate = now.ToGmtString(SIMPLE_DATE_FORMAT_STR); + Aws::String signingRegion = region ? region : m_region; Aws::String signingServiceName = serviceName ? serviceName : m_serviceName; Aws::String stringToSign = GenerateStringToSign(dateHeaderValue, simpleDate, canonicalRequestHash, signingRegion, signingServiceName); auto finalSignature = GenerateSignature(credentials, stringToSign, simpleDate, signingRegion, signingServiceName); - - Aws::StringStream ss; - ss << AWS_HMAC_SHA256 << " " << CREDENTIAL << EQ << credentials.GetAWSAccessKeyId() << "/" << simpleDate + + Aws::StringStream ss; + ss << AWS_HMAC_SHA256 << " " << CREDENTIAL << EQ << credentials.GetAWSAccessKeyId() << "/" << simpleDate << "/" << signingRegion << "/" << signingServiceName << "/" << AWS4_REQUEST << ", " << SIGNED_HEADERS << EQ - << signedHeadersValue << ", " << SIGNATURE << EQ << finalSignature; - - auto awsAuthString = ss.str(); - AWS_LOGSTREAM_DEBUG(v4LogTag, "Signing request with: " << awsAuthString); - request.SetAwsAuthorization(awsAuthString); - request.SetSigningAccessKey(credentials.GetAWSAccessKeyId()); + << signedHeadersValue << ", " << SIGNATURE << EQ << finalSignature; + + auto awsAuthString = ss.str(); + AWS_LOGSTREAM_DEBUG(v4LogTag, "Signing request with: " << awsAuthString); + request.SetAwsAuthorization(awsAuthString); + request.SetSigningAccessKey(credentials.GetAWSAccessKeyId()); request.SetSigningRegion(signingRegion); - return true; -} - -bool AWSAuthV4Signer::PresignRequest(Aws::Http::HttpRequest& request, long long expirationTimeInSeconds) const -{ - return PresignRequest(request, m_region.c_str(), expirationTimeInSeconds); -} - -bool AWSAuthV4Signer::PresignRequest(Aws::Http::HttpRequest& request, const char* region, long long expirationInSeconds) const -{ - return PresignRequest(request, region, m_serviceName.c_str(), expirationInSeconds); -} - -bool AWSAuthV4Signer::PresignRequest(Aws::Http::HttpRequest& request, const char* region, const char* serviceName, long long expirationTimeInSeconds) const -{ - AWSCredentials credentials = m_credentialsProvider->GetAWSCredentials(); - - //don't sign anonymous requests - if (credentials.GetAWSAccessKeyId().empty() || credentials.GetAWSSecretKey().empty()) - { - return true; - } - - Aws::StringStream intConversionStream; - intConversionStream << expirationTimeInSeconds; - request.AddQueryStringParameter(Http::X_AMZ_EXPIRES_HEADER, intConversionStream.str()); - - if (!credentials.GetSessionToken().empty()) - { - request.AddQueryStringParameter(Http::AWS_SECURITY_TOKEN, credentials.GetSessionToken()); - } - - //calculate date header to use in internal signature (this also goes into date header). - DateTime now = GetSigningTimestamp(); + return true; +} + +bool AWSAuthV4Signer::PresignRequest(Aws::Http::HttpRequest& request, long long expirationTimeInSeconds) const +{ + return PresignRequest(request, m_region.c_str(), expirationTimeInSeconds); +} + +bool AWSAuthV4Signer::PresignRequest(Aws::Http::HttpRequest& request, const char* region, long long expirationInSeconds) const +{ + return PresignRequest(request, region, m_serviceName.c_str(), expirationInSeconds); +} + +bool AWSAuthV4Signer::PresignRequest(Aws::Http::HttpRequest& request, const char* region, const char* serviceName, long long expirationTimeInSeconds) const +{ + AWSCredentials credentials = m_credentialsProvider->GetAWSCredentials(); + + //don't sign anonymous requests + if (credentials.GetAWSAccessKeyId().empty() || credentials.GetAWSSecretKey().empty()) + { + return true; + } + + Aws::StringStream intConversionStream; + intConversionStream << expirationTimeInSeconds; + request.AddQueryStringParameter(Http::X_AMZ_EXPIRES_HEADER, intConversionStream.str()); + + if (!credentials.GetSessionToken().empty()) + { + request.AddQueryStringParameter(Http::AWS_SECURITY_TOKEN, credentials.GetSessionToken()); + } + + //calculate date header to use in internal signature (this also goes into date header). + DateTime now = GetSigningTimestamp(); Aws::String dateQueryValue = now.ToGmtString(DateFormat::ISO_8601_BASIC); - request.AddQueryStringParameter(Http::AWS_DATE_HEADER, dateQueryValue); - - Aws::StringStream headersStream; - Aws::StringStream signedHeadersStream; - for (const auto& header : CanonicalizeHeaders(request.GetHeaders())) - { - if(ShouldSignHeader(header.first)) - { - headersStream << header.first.c_str() << ":" << header.second.c_str() << NEWLINE; - signedHeadersStream << header.first.c_str() << ";"; - } - } - - Aws::String canonicalHeadersString = headersStream.str(); - AWS_LOGSTREAM_DEBUG(v4LogTag, "Canonical Header String: " << canonicalHeadersString); - - //calculate signed headers parameter - Aws::String signedHeadersValue(signedHeadersStream.str()); - //remove that last semi-colon - if (!signedHeadersValue.empty()) - { - signedHeadersValue.pop_back(); - } - - request.AddQueryStringParameter(X_AMZ_SIGNED_HEADERS, signedHeadersValue); - AWS_LOGSTREAM_DEBUG(v4LogTag, "Signed Headers value: " << signedHeadersValue); - - Aws::StringStream ss; + request.AddQueryStringParameter(Http::AWS_DATE_HEADER, dateQueryValue); + + Aws::StringStream headersStream; + Aws::StringStream signedHeadersStream; + for (const auto& header : CanonicalizeHeaders(request.GetHeaders())) + { + if(ShouldSignHeader(header.first)) + { + headersStream << header.first.c_str() << ":" << header.second.c_str() << NEWLINE; + signedHeadersStream << header.first.c_str() << ";"; + } + } + + Aws::String canonicalHeadersString = headersStream.str(); + AWS_LOGSTREAM_DEBUG(v4LogTag, "Canonical Header String: " << canonicalHeadersString); + + //calculate signed headers parameter + Aws::String signedHeadersValue(signedHeadersStream.str()); + //remove that last semi-colon + if (!signedHeadersValue.empty()) + { + signedHeadersValue.pop_back(); + } + + request.AddQueryStringParameter(X_AMZ_SIGNED_HEADERS, signedHeadersValue); + AWS_LOGSTREAM_DEBUG(v4LogTag, "Signed Headers value: " << signedHeadersValue); + + Aws::StringStream ss; Aws::String signingRegion = region ? region : m_region; Aws::String signingServiceName = serviceName ? serviceName : m_serviceName; - Aws::String simpleDate = now.ToGmtString(SIMPLE_DATE_FORMAT_STR); - ss << credentials.GetAWSAccessKeyId() << "/" << simpleDate + Aws::String simpleDate = now.ToGmtString(SIMPLE_DATE_FORMAT_STR); + ss << credentials.GetAWSAccessKeyId() << "/" << simpleDate << "/" << signingRegion << "/" << signingServiceName << "/" << AWS4_REQUEST; - - request.AddQueryStringParameter(X_AMZ_ALGORITHM, AWS_HMAC_SHA256); - request.AddQueryStringParameter(X_AMZ_CREDENTIAL, ss.str()); - ss.str(""); - - request.SetSigningAccessKey(credentials.GetAWSAccessKeyId()); + + request.AddQueryStringParameter(X_AMZ_ALGORITHM, AWS_HMAC_SHA256); + request.AddQueryStringParameter(X_AMZ_CREDENTIAL, ss.str()); + ss.str(""); + + request.SetSigningAccessKey(credentials.GetAWSAccessKeyId()); request.SetSigningRegion(signingRegion); - - //generate generalized canonicalized request string. - Aws::String canonicalRequestString = CanonicalizeRequestSigningString(request, m_urlEscapePath); - - //append v4 stuff to the canonical request string. - canonicalRequestString.append(canonicalHeadersString); - canonicalRequestString.append(NEWLINE); - canonicalRequestString.append(signedHeadersValue); - canonicalRequestString.append(NEWLINE); + + //generate generalized canonicalized request string. + Aws::String canonicalRequestString = CanonicalizeRequestSigningString(request, m_urlEscapePath); + + //append v4 stuff to the canonical request string. + canonicalRequestString.append(canonicalHeadersString); + canonicalRequestString.append(NEWLINE); + canonicalRequestString.append(signedHeadersValue); + canonicalRequestString.append(NEWLINE); if (ServiceRequireUnsignedPayload(signingServiceName)) { - canonicalRequestString.append(UNSIGNED_PAYLOAD); + canonicalRequestString.append(UNSIGNED_PAYLOAD); } else { - canonicalRequestString.append(EMPTY_STRING_SHA256); - } - AWS_LOGSTREAM_DEBUG(v4LogTag, "Canonical Request String: " << canonicalRequestString); - - //now compute sha256 on that request string - auto hashResult = m_hash->Calculate(canonicalRequestString); - if (!hashResult.IsSuccess()) - { - AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to hash (sha256) request string"); - AWS_LOGSTREAM_DEBUG(v4LogTag, "The request string is: \"" << canonicalRequestString << "\""); - return false; - } - - auto sha256Digest = hashResult.GetResult(); + canonicalRequestString.append(EMPTY_STRING_SHA256); + } + AWS_LOGSTREAM_DEBUG(v4LogTag, "Canonical Request String: " << canonicalRequestString); + + //now compute sha256 on that request string + auto hashResult = m_hash->Calculate(canonicalRequestString); + if (!hashResult.IsSuccess()) + { + AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to hash (sha256) request string"); + AWS_LOGSTREAM_DEBUG(v4LogTag, "The request string is: \"" << canonicalRequestString << "\""); + return false; + } + + auto sha256Digest = hashResult.GetResult(); auto canonicalRequestHash = HashingUtils::HexEncode(sha256Digest); - + auto stringToSign = GenerateStringToSign(dateQueryValue, simpleDate, canonicalRequestHash, signingRegion, signingServiceName); auto finalSigningHash = GenerateSignature(credentials, stringToSign, simpleDate, signingRegion, signingServiceName); - if (finalSigningHash.empty()) - { - return false; - } - + if (finalSigningHash.empty()) + { + return false; + } + //add that the signature to the query string - request.AddQueryStringParameter(X_AMZ_SIGNATURE, finalSigningHash); - - return true; -} - -bool AWSAuthV4Signer::ServiceRequireUnsignedPayload(const Aws::String& serviceName) const -{ - // S3 uses a magic string (instead of the empty string) for its body hash for presigned URLs as outlined here: - // https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html - // this is true for PUT, POST, GET, DELETE and HEAD operations. - // However, other services (for example RDS) implement the specification as outlined here: - // https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html - // which states that body-less requests should use the empty-string SHA256 hash. - return "s3" == serviceName; -} - -Aws::String AWSAuthV4Signer::GenerateSignature(const AWSCredentials& credentials, const Aws::String& stringToSign, - const Aws::String& simpleDate, const Aws::String& region, const Aws::String& serviceName) const -{ - auto key = ComputeHash(credentials.GetAWSSecretKey(), simpleDate, region, serviceName); - return GenerateSignature(stringToSign, key); -} - -Aws::String AWSAuthV4Signer::GenerateSignature(const Aws::String& stringToSign, const ByteBuffer& key) const -{ - AWS_LOGSTREAM_DEBUG(v4LogTag, "Final String to sign: " << stringToSign); - - Aws::StringStream ss; - - auto hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)stringToSign.c_str(), stringToSign.length()), key); - if (!hashResult.IsSuccess()) - { - AWS_LOGSTREAM_ERROR(v4LogTag, "Unable to hmac (sha256) final string"); - AWS_LOGSTREAM_DEBUG(v4LogTag, "The final string is: \"" << stringToSign << "\""); + request.AddQueryStringParameter(X_AMZ_SIGNATURE, finalSigningHash); + + return true; +} + +bool AWSAuthV4Signer::ServiceRequireUnsignedPayload(const Aws::String& serviceName) const +{ + // S3 uses a magic string (instead of the empty string) for its body hash for presigned URLs as outlined here: + // https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html + // this is true for PUT, POST, GET, DELETE and HEAD operations. + // However, other services (for example RDS) implement the specification as outlined here: + // https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html + // which states that body-less requests should use the empty-string SHA256 hash. + return "s3" == serviceName; +} + +Aws::String AWSAuthV4Signer::GenerateSignature(const AWSCredentials& credentials, const Aws::String& stringToSign, + const Aws::String& simpleDate, const Aws::String& region, const Aws::String& serviceName) const +{ + auto key = ComputeHash(credentials.GetAWSSecretKey(), simpleDate, region, serviceName); + return GenerateSignature(stringToSign, key); +} + +Aws::String AWSAuthV4Signer::GenerateSignature(const Aws::String& stringToSign, const ByteBuffer& key) const +{ + AWS_LOGSTREAM_DEBUG(v4LogTag, "Final String to sign: " << stringToSign); + + Aws::StringStream ss; + + auto hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)stringToSign.c_str(), stringToSign.length()), key); + if (!hashResult.IsSuccess()) + { + AWS_LOGSTREAM_ERROR(v4LogTag, "Unable to hmac (sha256) final string"); + AWS_LOGSTREAM_DEBUG(v4LogTag, "The final string is: \"" << stringToSign << "\""); return {}; - } - - //now we finally sign our request string with our hex encoded derived hash. - auto finalSigningDigest = hashResult.GetResult(); - - auto finalSigningHash = HashingUtils::HexEncode(finalSigningDigest); - AWS_LOGSTREAM_DEBUG(v4LogTag, "Final computed signing hash: " << finalSigningHash); - - return finalSigningHash; -} - -Aws::String AWSAuthV4Signer::ComputePayloadHash(Aws::Http::HttpRequest& request) const -{ - if (!request.GetContentBody()) - { - AWS_LOGSTREAM_DEBUG(v4LogTag, "Using cached empty string sha256 " << EMPTY_STRING_SHA256 << " because payload is empty."); - return EMPTY_STRING_SHA256; - } - - //compute hash on payload if it exists. - auto hashResult = m_hash->Calculate(*request.GetContentBody()); - - if(request.GetContentBody()) - { - request.GetContentBody()->clear(); - request.GetContentBody()->seekg(0); - } - - if (!hashResult.IsSuccess()) - { - AWS_LOGSTREAM_ERROR(v4LogTag, "Unable to hash (sha256) request body"); + } + + //now we finally sign our request string with our hex encoded derived hash. + auto finalSigningDigest = hashResult.GetResult(); + + auto finalSigningHash = HashingUtils::HexEncode(finalSigningDigest); + AWS_LOGSTREAM_DEBUG(v4LogTag, "Final computed signing hash: " << finalSigningHash); + + return finalSigningHash; +} + +Aws::String AWSAuthV4Signer::ComputePayloadHash(Aws::Http::HttpRequest& request) const +{ + if (!request.GetContentBody()) + { + AWS_LOGSTREAM_DEBUG(v4LogTag, "Using cached empty string sha256 " << EMPTY_STRING_SHA256 << " because payload is empty."); + return EMPTY_STRING_SHA256; + } + + //compute hash on payload if it exists. + auto hashResult = m_hash->Calculate(*request.GetContentBody()); + + if(request.GetContentBody()) + { + request.GetContentBody()->clear(); + request.GetContentBody()->seekg(0); + } + + if (!hashResult.IsSuccess()) + { + AWS_LOGSTREAM_ERROR(v4LogTag, "Unable to hash (sha256) request body"); return {}; - } - - auto sha256Digest = hashResult.GetResult(); - - Aws::String payloadHash(HashingUtils::HexEncode(sha256Digest)); - AWS_LOGSTREAM_DEBUG(v4LogTag, "Calculated sha256 " << payloadHash << " for payload."); - return payloadHash; -} - -Aws::String AWSAuthV4Signer::GenerateStringToSign(const Aws::String& dateValue, const Aws::String& simpleDate, - const Aws::String& canonicalRequestHash, const Aws::String& region, const Aws::String& serviceName) const -{ - //generate the actual string we will use in signing the final request. - Aws::StringStream ss; - - ss << AWS_HMAC_SHA256 << NEWLINE << dateValue << NEWLINE << simpleDate << "/" << region << "/" - << serviceName << "/" << AWS4_REQUEST << NEWLINE << canonicalRequestHash; - - return ss.str(); -} - -Aws::Utils::ByteBuffer AWSAuthV4Signer::ComputeHash(const Aws::String& secretKey, - const Aws::String& simpleDate, const Aws::String& region, const Aws::String& serviceName) const -{ - Aws::String signingKey(SIGNING_KEY); - signingKey.append(secretKey); - auto hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)simpleDate.c_str(), simpleDate.length()), - ByteBuffer((unsigned char*)signingKey.c_str(), signingKey.length())); - - if (!hashResult.IsSuccess()) - { - AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to HMAC (SHA256) date string \"" << simpleDate << "\""); - return {}; - } - - auto kDate = hashResult.GetResult(); - hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)region.c_str(), region.length()), kDate); - if (!hashResult.IsSuccess()) - { - AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to HMAC (SHA256) region string \"" << region << "\""); - return {}; - } - - auto kRegion = hashResult.GetResult(); - hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)serviceName.c_str(), serviceName.length()), kRegion); - if (!hashResult.IsSuccess()) - { - AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to HMAC (SHA256) service string \"" << m_serviceName << "\""); - return {}; - } - - auto kService = hashResult.GetResult(); - hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)AWS4_REQUEST, strlen(AWS4_REQUEST)), kService); - if (!hashResult.IsSuccess()) - { - AWS_LOGSTREAM_ERROR(v4LogTag, "Unable to HMAC (SHA256) request string"); - AWS_LOGSTREAM_DEBUG(v4LogTag, "The request string is: \"" << AWS4_REQUEST << "\""); - return {}; - } - return hashResult.GetResult(); -} + } + + auto sha256Digest = hashResult.GetResult(); + + Aws::String payloadHash(HashingUtils::HexEncode(sha256Digest)); + AWS_LOGSTREAM_DEBUG(v4LogTag, "Calculated sha256 " << payloadHash << " for payload."); + return payloadHash; +} + +Aws::String AWSAuthV4Signer::GenerateStringToSign(const Aws::String& dateValue, const Aws::String& simpleDate, + const Aws::String& canonicalRequestHash, const Aws::String& region, const Aws::String& serviceName) const +{ + //generate the actual string we will use in signing the final request. + Aws::StringStream ss; + + ss << AWS_HMAC_SHA256 << NEWLINE << dateValue << NEWLINE << simpleDate << "/" << region << "/" + << serviceName << "/" << AWS4_REQUEST << NEWLINE << canonicalRequestHash; + + return ss.str(); +} + +Aws::Utils::ByteBuffer AWSAuthV4Signer::ComputeHash(const Aws::String& secretKey, + const Aws::String& simpleDate, const Aws::String& region, const Aws::String& serviceName) const +{ + Aws::String signingKey(SIGNING_KEY); + signingKey.append(secretKey); + auto hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)simpleDate.c_str(), simpleDate.length()), + ByteBuffer((unsigned char*)signingKey.c_str(), signingKey.length())); + + if (!hashResult.IsSuccess()) + { + AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to HMAC (SHA256) date string \"" << simpleDate << "\""); + return {}; + } + + auto kDate = hashResult.GetResult(); + hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)region.c_str(), region.length()), kDate); + if (!hashResult.IsSuccess()) + { + AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to HMAC (SHA256) region string \"" << region << "\""); + return {}; + } + + auto kRegion = hashResult.GetResult(); + hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)serviceName.c_str(), serviceName.length()), kRegion); + if (!hashResult.IsSuccess()) + { + AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to HMAC (SHA256) service string \"" << m_serviceName << "\""); + return {}; + } + + auto kService = hashResult.GetResult(); + hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)AWS4_REQUEST, strlen(AWS4_REQUEST)), kService); + if (!hashResult.IsSuccess()) + { + AWS_LOGSTREAM_ERROR(v4LogTag, "Unable to HMAC (SHA256) request string"); + AWS_LOGSTREAM_DEBUG(v4LogTag, "The request string is: \"" << AWS4_REQUEST << "\""); + return {}; + } + return hashResult.GetResult(); +} AWSAuthEventStreamV4Signer::AWSAuthEventStreamV4Signer(const std::shared_ptr<Auth::AWSCredentialsProvider>& credentialsProvider, const char* serviceName, const Aws::String& region) : |