diff options
author | dakovalkov <dakovalkov@yandex-team.com> | 2023-12-03 13:33:55 +0300 |
---|---|---|
committer | dakovalkov <dakovalkov@yandex-team.com> | 2023-12-03 14:04:39 +0300 |
commit | 2a718325637e5302334b6d0a6430f63168f8dbb3 (patch) | |
tree | 64be81080b7df9ec1d86d053a0c394ae53fcf1fe /contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth | |
parent | e0d94a470142d95c3007e9c5d80380994940664a (diff) | |
download | ydb-2a718325637e5302334b6d0a6430f63168f8dbb3.tar.gz |
Update contrib/libs/aws-sdk-cpp to 1.11.37
Diffstat (limited to 'contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth')
15 files changed, 1508 insertions, 840 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 deleted file mode 100644 index 0baa00058f..0000000000 --- a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/AWSAuthSigner.cpp +++ /dev/null @@ -1,806 +0,0 @@ -/** - * 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/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 <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"; -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* 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 v4LogTag[] = "AWSAuthV4Signer"; -static const char v4StreamingLogTag[] = "AWSAuthEventStreamV4Signer"; - -namespace Aws -{ - namespace Auth - { - const char SIGNATURE[] = "Signature"; - 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; - } - - 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()); - - 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)), - m_unsignedHeaders({USER_AGENT, X_AMZN_TRACE_ID}), - 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() -{ - // 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::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) - { - 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) - { - request.SetHeaderValue(X_AMZ_CONTENT_SHA256, payloadHash); - } - - //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()) - { - 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::String canonicalRequestHash = HashingUtils::HexEncode(sha256Digest); - 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 - << "/" << 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()); - 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(); - 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; - 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 - << "/" << 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.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); - if (ServiceRequireUnsignedPayload(signingServiceName)) - { - 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(); - 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; - } - - //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 || "s3-object-lambda" == 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"); - 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(); -} - -AWSAuthEventStreamV4Signer::AWSAuthEventStreamV4Signer(const std::shared_ptr<Auth::AWSCredentialsProvider>& - credentialsProvider, const char* serviceName, const Aws::String& region) : - m_serviceName(serviceName), - m_region(region), - m_credentialsProvider(credentialsProvider) -{ - - m_unsignedHeaders.emplace_back(X_AMZN_TRACE_ID); - m_unsignedHeaders.emplace_back(USER_AGENT_HEADER); -} - -bool AWSAuthEventStreamV4Signer::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()); - } - - request.SetHeaderValue(X_AMZ_CONTENT_SHA256, EVENT_STREAM_CONTENT_SHA256); - - //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(v4StreamingLogTag, "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(v4StreamingLogTag, "Signed Headers value:" << signedHeadersValue); - - //generate generalized canonicalized request string. - Aws::String canonicalRequestString = CanonicalizeRequestSigningString(request, true/* m_urlEscapePath */); - - //append v4 stuff to the canonical request string. - canonicalRequestString.append(canonicalHeadersString); - canonicalRequestString.append(NEWLINE); - canonicalRequestString.append(signedHeadersValue); - canonicalRequestString.append(NEWLINE); - canonicalRequestString.append(EVENT_STREAM_CONTENT_SHA256); - - AWS_LOGSTREAM_DEBUG(v4StreamingLogTag, "Canonical Request String: " << canonicalRequestString); - - //now compute sha256 on that request string - auto hashResult = m_hash.Calculate(canonicalRequestString); - if (!hashResult.IsSuccess()) - { - AWS_LOGSTREAM_ERROR(v4StreamingLogTag, "Failed to hash (sha256) request string"); - AWS_LOGSTREAM_DEBUG(v4StreamingLogTag, "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 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 - << "/" << signingRegion << "/" << signingServiceName << "/" << AWS4_REQUEST << ", " << SIGNED_HEADERS << EQ - << signedHeadersValue << ", " << SIGNATURE << EQ << HashingUtils::HexEncode(finalSignature); - - auto awsAuthString = ss.str(); - AWS_LOGSTREAM_DEBUG(v4StreamingLogTag, "Signing request with: " << awsAuthString); - request.SetAwsAuthorization(awsAuthString); - request.SetSigningAccessKey(credentials.GetAWSAccessKeyId()); - request.SetSigningRegion(signingRegion); - return true; -} - -// this works regardless if the current machine is Big/Little Endian -static void WriteBigEndian(Aws::String& str, uint64_t n) -{ - int shift = 56; - while(shift >= 0) - { - str.push_back((n >> shift) & 0xFF); - shift -= 8; - } -} - -bool AWSAuthEventStreamV4Signer::SignEventMessage(Event::Message& message, Aws::String& priorSignature) const -{ - using Event::EventHeaderValue; - - Aws::StringStream stringToSign; - stringToSign << EVENT_STREAM_PAYLOAD << NEWLINE; - const DateTime now = GetSigningTimestamp(); - const auto simpleDate = now.ToGmtString(SIMPLE_DATE_FORMAT_STR); - stringToSign << now.ToGmtString(DateFormat::ISO_8601_BASIC) << NEWLINE - << simpleDate << "/" << m_region << "/" - << m_serviceName << "/aws4_request" << NEWLINE << priorSignature << NEWLINE; - - - Aws::String nonSignatureHeaders; - nonSignatureHeaders.push_back(char(sizeof(EVENTSTREAM_DATE_HEADER) - 1)); // length of the string - nonSignatureHeaders += EVENTSTREAM_DATE_HEADER; - nonSignatureHeaders.push_back(static_cast<char>(EventHeaderValue::EventHeaderType::TIMESTAMP)); // type of the value - WriteBigEndian(nonSignatureHeaders, static_cast<uint64_t>(now.Millis())); // the value of the timestamp in big-endian - - auto hashOutcome = m_hash.Calculate(nonSignatureHeaders); - if (!hashOutcome.IsSuccess()) - { - AWS_LOGSTREAM_ERROR(v4StreamingLogTag, "Failed to hash (sha256) non-signature headers."); - return false; - } - - const auto nonSignatureHeadersHash = hashOutcome.GetResult(); - stringToSign << HashingUtils::HexEncode(nonSignatureHeadersHash) << NEWLINE; - - if (message.GetEventPayload().empty()) - { - AWS_LOGSTREAM_WARN(v4StreamingLogTag, "Attempting to sign an empty message (no payload and no headers). " - "It is unlikely that this is the intended behavior."); - } - else - { - // use a preallocatedStreamBuf to avoid making a copy. - // The Hashing API requires either Aws::String or IStream as input. - // TODO: the hashing API should be accept 'unsigned char*' as input. - Utils::Stream::PreallocatedStreamBuf streamBuf(message.GetEventPayload().data(), message.GetEventPayload().size()); - Aws::IOStream payload(&streamBuf); - hashOutcome = m_hash.Calculate(payload); - - if (!hashOutcome.IsSuccess()) - { - AWS_LOGSTREAM_ERROR(v4StreamingLogTag, "Failed to hash (sha256) non-signature headers."); - return false; - } - const auto payloadHash = hashOutcome.GetResult(); - stringToSign << HashingUtils::HexEncode(payloadHash); - AWS_LOGSTREAM_DEBUG(v4StreamingLogTag, "Payload hash - " << HashingUtils::HexEncode(payloadHash)); - } - - Utils::ByteBuffer finalSignatureDigest = GenerateSignature(m_credentialsProvider->GetAWSCredentials(), stringToSign.str(), simpleDate, m_region, m_serviceName); - const auto finalSignature = HashingUtils::HexEncode(finalSignatureDigest); - AWS_LOGSTREAM_DEBUG(v4StreamingLogTag, "Final computed signing hash: " << finalSignature); - priorSignature = finalSignature; - - message.InsertEventHeader(EVENTSTREAM_DATE_HEADER, EventHeaderValue(now.Millis(), EventHeaderValue::EventHeaderType::TIMESTAMP)); - message.InsertEventHeader(EVENTSTREAM_SIGNATURE_HEADER, std::move(finalSignatureDigest)); - - AWS_LOGSTREAM_INFO(v4StreamingLogTag, "Event chunk final signature - " << finalSignature); - return true; -} - -bool AWSAuthEventStreamV4Signer::ShouldSignHeader(const Aws::String& header) const -{ - return std::find(m_unsignedHeaders.cbegin(), m_unsignedHeaders.cend(), Aws::Utils::StringUtils::ToLower(header.c_str())) == m_unsignedHeaders.cend(); -} - -Utils::ByteBuffer AWSAuthEventStreamV4Signer::GenerateSignature(const AWSCredentials& credentials, const Aws::String& stringToSign, - const Aws::String& simpleDate, const Aws::String& region, const Aws::String& serviceName) const -{ - Utils::Threading::ReaderLockGuard guard(m_derivedKeyLock); - const auto& secretKey = credentials.GetAWSSecretKey(); - if (secretKey != m_currentSecretKey || simpleDate != m_currentDateStr) - { - guard.UpgradeToWriterLock(); - // double-checked lock to prevent updating twice - if (m_currentDateStr != simpleDate || m_currentSecretKey != secretKey) - { - m_currentSecretKey = secretKey; - m_currentDateStr = simpleDate; - m_derivedKey = ComputeHash(m_currentSecretKey, m_currentDateStr, region, serviceName); - } - - } - return GenerateSignature(stringToSign, m_derivedKey); -} - -Utils::ByteBuffer AWSAuthEventStreamV4Signer::GenerateSignature(const Aws::String& stringToSign, const ByteBuffer& key) const -{ - AWS_LOGSTREAM_DEBUG(v4StreamingLogTag, "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(v4StreamingLogTag, "Unable to hmac (sha256) final string"); - AWS_LOGSTREAM_DEBUG(v4StreamingLogTag, "The final string is: \"" << stringToSign << "\""); - return {}; - } - - return hashResult.GetResult(); -} - -Aws::String AWSAuthEventStreamV4Signer::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 AWSAuthEventStreamV4Signer::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(v4StreamingLogTag, "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(v4StreamingLogTag, "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(v4StreamingLogTag, "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(v4StreamingLogTag, "Unable to HMAC (SHA256) request string"); - AWS_LOGSTREAM_DEBUG(v4StreamingLogTag, "The request string is: \"" << AWS4_REQUEST << "\""); - return {}; - } - return hashResult.GetResult(); -} diff --git a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/AWSCredentialsProvider.cpp b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/AWSCredentialsProvider.cpp index bf20ede35e..084e4bca6e 100644 --- a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/AWSCredentialsProvider.cpp +++ b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/AWSCredentialsProvider.cpp @@ -48,7 +48,7 @@ static const char DEFAULT_CREDENTIALS_FILE[] = "credentials"; extern const char DEFAULT_CONFIG_FILE[] = "config"; -static const int EXPIRATION_GRACE_PERIOD = 5 * 1000; +static const int AWS_CREDENTIAL_PROVIDER_EXPIRATION_GRACE_PERIOD = 5 * 1000; void AWSCredentialsProvider::Reload() { @@ -183,9 +183,10 @@ AWSCredentials ProfileConfigFileAWSCredentialsProvider::GetAWSCredentials() { RefreshIfExpired(); ReaderLockGuard guard(m_reloadLock); - auto credsFileProfileIter = m_credentialsFileLoader.GetProfiles().find(m_profileToUse); + const Aws::Map<Aws::String, Aws::Config::Profile>& profiles = m_credentialsFileLoader.GetProfiles(); + auto credsFileProfileIter = profiles.find(m_profileToUse); - if(credsFileProfileIter != m_credentialsFileLoader.GetProfiles().end()) + if(credsFileProfileIter != profiles.end()) { return credsFileProfileIter->second.GetCredentials(); } @@ -239,37 +240,71 @@ AWSCredentials InstanceProfileCredentialsProvider::GetAWSCredentials() { RefreshIfExpired(); ReaderLockGuard guard(m_reloadLock); + if (m_ec2MetadataConfigLoader) + { + const Aws::Map<Aws::String, Aws::Config::Profile> &profiles = m_ec2MetadataConfigLoader->GetProfiles(); + auto profileIter = profiles.find(Aws::Config::INSTANCE_PROFILE_KEY); + + if (profileIter != profiles.end()) { + return profileIter->second.GetCredentials(); + } + } + else + { + AWS_LOGSTREAM_ERROR(INSTANCE_LOG_TAG, "EC2 Metadata config loader is a nullptr"); + } + + return AWSCredentials(); +} + +bool InstanceProfileCredentialsProvider::ExpiresSoon() const +{ + ReaderLockGuard guard(m_reloadLock); auto profileIter = m_ec2MetadataConfigLoader->GetProfiles().find(Aws::Config::INSTANCE_PROFILE_KEY); + AWSCredentials credentials; if(profileIter != m_ec2MetadataConfigLoader->GetProfiles().end()) { - return profileIter->second.GetCredentials(); + credentials = profileIter->second.GetCredentials(); } - return AWSCredentials(); + return ((credentials.GetExpiration() - Aws::Utils::DateTime::Now()).count() < AWS_CREDENTIAL_PROVIDER_EXPIRATION_GRACE_PERIOD); } void InstanceProfileCredentialsProvider::Reload() { - AWS_LOGSTREAM_INFO(INSTANCE_LOG_TAG, "Credentials have expired attempting to repull from EC2 Metadata Service."); - m_ec2MetadataConfigLoader->Load(); - AWSCredentialsProvider::Reload(); + AWS_LOGSTREAM_INFO(INSTANCE_LOG_TAG, "Credentials have expired attempting to re-pull from EC2 Metadata Service."); + if (m_ec2MetadataConfigLoader) { + m_ec2MetadataConfigLoader->Load(); + AWSCredentialsProvider::Reload(); + } else { + AWS_LOGSTREAM_ERROR(INSTANCE_LOG_TAG, "EC2 Metadata config loader is a nullptr"); + } } void InstanceProfileCredentialsProvider::RefreshIfExpired() { AWS_LOGSTREAM_DEBUG(INSTANCE_LOG_TAG, "Checking if latest credential pull has expired."); ReaderLockGuard guard(m_reloadLock); - if (!IsTimeToRefresh(m_loadFrequencyMs)) - { - return; - } + auto profileIter = m_ec2MetadataConfigLoader->GetProfiles().find(Aws::Config::INSTANCE_PROFILE_KEY); + AWSCredentials credentials; - guard.UpgradeToWriterLock(); - if (!IsTimeToRefresh(m_loadFrequencyMs)) // double-checked lock to avoid refreshing twice + if(profileIter != m_ec2MetadataConfigLoader->GetProfiles().end()) { - return; + credentials = profileIter->second.GetCredentials(); + + if (!credentials.IsEmpty() && !IsTimeToRefresh(m_loadFrequencyMs) && !ExpiresSoon()) + { + return; + } + + guard.UpgradeToWriterLock(); + if (!credentials.IsEmpty() && !IsTimeToRefresh(m_loadFrequencyMs) && !ExpiresSoon()) // double-checked lock to avoid refreshing twice + { + return; + } } + Reload(); } @@ -306,12 +341,17 @@ AWSCredentials TaskRoleCredentialsProvider::GetAWSCredentials() bool TaskRoleCredentialsProvider::ExpiresSoon() const { - return ((m_credentials.GetExpiration() - Aws::Utils::DateTime::Now()).count() < EXPIRATION_GRACE_PERIOD); + return ((m_credentials.GetExpiration() - Aws::Utils::DateTime::Now()).count() < AWS_CREDENTIAL_PROVIDER_EXPIRATION_GRACE_PERIOD); } void TaskRoleCredentialsProvider::Reload() { - AWS_LOGSTREAM_INFO(TASK_ROLE_LOG_TAG, "Credentials have expired or will expire, attempting to repull from ECS IAM Service."); + AWS_LOGSTREAM_INFO(TASK_ROLE_LOG_TAG, "Credentials have expired or will expire, attempting to re-pull from ECS IAM Service."); + if (!m_ecsCredentialsClient) + { + AWS_LOGSTREAM_ERROR(INSTANCE_LOG_TAG, "ECS Credentials client is a nullptr"); + return; + } auto credentialsStr = m_ecsCredentialsClient->GetECSCredentials(); if (credentialsStr.empty()) return; diff --git a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/AWSCredentialsProviderChain.cpp b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/AWSCredentialsProviderChain.cpp index 8b019a1664..403bd380c4 100644 --- a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/AWSCredentialsProviderChain.cpp +++ b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/AWSCredentialsProviderChain.cpp @@ -77,3 +77,9 @@ DefaultAWSCredentialsProviderChain::DefaultAWSCredentialsProviderChain() : AWSCr AWS_LOGSTREAM_INFO(DefaultCredentialsProviderChainTag, "Added EC2 metadata service credentials provider to the provider chain."); } } + +DefaultAWSCredentialsProviderChain::DefaultAWSCredentialsProviderChain(const DefaultAWSCredentialsProviderChain& chain) { + for (const auto& provider: chain.GetProviders()) { + AddProvider(provider); + } +} diff --git a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/SSOCredentialsProvider.cpp b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/SSOCredentialsProvider.cpp index e8f780762e..9576e9d999 100644 --- a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/SSOCredentialsProvider.cpp +++ b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/SSOCredentialsProvider.cpp @@ -33,7 +33,8 @@ SSOCredentialsProvider::SSOCredentialsProvider() : m_profileToUse(GetConfigProfi AWS_LOGSTREAM_INFO(SSO_CREDENTIALS_PROVIDER_LOG_TAG, "Setting sso credentials provider to read config from " << m_profileToUse); } -SSOCredentialsProvider::SSOCredentialsProvider(const Aws::String& profile) : m_profileToUse(profile) +SSOCredentialsProvider::SSOCredentialsProvider(const Aws::String& profile) : m_profileToUse(profile), + m_bearerTokenProvider(profile) { AWS_LOGSTREAM_INFO(SSO_CREDENTIALS_PROVIDER_LOG_TAG, "Setting sso credentials provider to read config from " << m_profileToUse); } @@ -48,15 +49,24 @@ AWSCredentials SSOCredentialsProvider::GetAWSCredentials() void SSOCredentialsProvider::Reload() { auto profile = Aws::Config::GetCachedConfigProfile(m_profileToUse); - - Aws::String hashedStartUrl = Aws::Utils::HashingUtils::HexEncode(Aws::Utils::HashingUtils::CalculateSHA1(profile.GetSsoStartUrl())); - auto profileDirectory = ProfileConfigFileAWSCredentialsProvider::GetProfileDirectory(); - Aws::StringStream ssToken; - ssToken << profileDirectory; - ssToken << PATH_DELIM << "sso" << PATH_DELIM << "cache" << PATH_DELIM << hashedStartUrl << ".json"; - auto ssoTokenPath = ssToken.str(); - AWS_LOGSTREAM_DEBUG(SSO_CREDENTIALS_PROVIDER_LOG_TAG, "Loading token from: " << ssoTokenPath) - Aws::String accessToken = LoadAccessTokenFile(ssoTokenPath); + const auto accessToken = [&]() -> Aws::String { + // If we have an SSO Session set, use the refreshed token. + if (profile.IsSsoSessionSet()) { + m_ssoRegion = profile.GetSsoSession().GetSsoRegion(); + auto token = m_bearerTokenProvider.GetAWSBearerToken(); + m_expiresAt = token.GetExpiration(); + return token.GetToken(); + } + Aws::String hashedStartUrl = Aws::Utils::HashingUtils::HexEncode(Aws::Utils::HashingUtils::CalculateSHA1(profile.GetSsoStartUrl())); + auto profileDirectory = ProfileConfigFileAWSCredentialsProvider::GetProfileDirectory(); + Aws::StringStream ssToken; + ssToken << profileDirectory; + ssToken << PATH_DELIM << "sso" << PATH_DELIM << "cache" << PATH_DELIM << hashedStartUrl << ".json"; + auto ssoTokenPath = ssToken.str(); + AWS_LOGSTREAM_DEBUG(SSO_CREDENTIALS_PROVIDER_LOG_TAG, "Loading token from: " << ssoTokenPath) + m_ssoRegion = profile.GetSsoRegion(); + return LoadAccessTokenFile(ssoTokenPath); + }(); if (accessToken.empty()) { AWS_LOGSTREAM_TRACE(SSO_CREDENTIALS_PROVIDER_LOG_TAG, "Access token for SSO not available"); return; @@ -72,7 +82,7 @@ void SSOCredentialsProvider::Reload() Aws::Client::ClientConfiguration config; config.scheme = Aws::Http::Scheme::HTTPS; - config.region = profile.GetSsoRegion(); + config.region = m_ssoRegion; AWS_LOGSTREAM_DEBUG(SSO_CREDENTIALS_PROVIDER_LOG_TAG, "Passing config to client for region: " << m_ssoRegion); Aws::Vector<Aws::String> retryableErrors; diff --git a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/STSCredentialsProvider.cpp b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/STSCredentialsProvider.cpp index 3f48c9e0c7..b861e6132b 100644 --- a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/STSCredentialsProvider.cpp +++ b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/STSCredentialsProvider.cpp @@ -30,6 +30,8 @@ using Aws::Utils::Threading::ReaderLockGuard; using Aws::Utils::Threading::WriterLockGuard; static const char STS_ASSUME_ROLE_WEB_IDENTITY_LOG_TAG[] = "STSAssumeRoleWithWebIdentityCredentialsProvider"; +static const int STS_CREDENTIAL_PROVIDER_EXPIRATION_GRACE_PERIOD = 5 * 1000; + STSAssumeRoleWebIdentityCredentialsProvider::STSAssumeRoleWebIdentityCredentialsProvider() : m_initialized(false) { @@ -145,16 +147,21 @@ void STSAssumeRoleWebIdentityCredentialsProvider::Reload() m_credentials = result.creds; } +bool STSAssumeRoleWebIdentityCredentialsProvider::ExpiresSoon() const +{ + return ((m_credentials.GetExpiration() - Aws::Utils::DateTime::Now()).count() < STS_CREDENTIAL_PROVIDER_EXPIRATION_GRACE_PERIOD); +} + void STSAssumeRoleWebIdentityCredentialsProvider::RefreshIfExpired() { ReaderLockGuard guard(m_reloadLock); - if (!m_credentials.IsExpiredOrEmpty()) + if (!m_credentials.IsEmpty() && !ExpiresSoon()) { return; } guard.UpgradeToWriterLock(); - if (!m_credentials.IsExpiredOrEmpty()) // double-checked lock to avoid refreshing twice + if (!m_credentials.IsExpiredOrEmpty() && !ExpiresSoon()) // double-checked lock to avoid refreshing twice { return; } diff --git a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/bearer-token-provider/DefaultBearerTokenProviderChain.cpp b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/bearer-token-provider/DefaultBearerTokenProviderChain.cpp new file mode 100644 index 0000000000..16b301cd67 --- /dev/null +++ b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/bearer-token-provider/DefaultBearerTokenProviderChain.cpp @@ -0,0 +1,35 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/core/auth/bearer-token-provider/DefaultBearerTokenProviderChain.h> +#include <aws/core/auth/AWSBearerToken.h> +#include <aws/core/auth/bearer-token-provider/SSOBearerTokenProvider.h> +#include <aws/core/utils/logging/LogMacros.h> + + +static const char SSO_DEFAULT_BEARER_TOKEN_PROVIDER_CHAIN_LOG_TAG[] = "SSOBearerTokenProvider"; + +Aws::Auth::AWSBearerToken Aws::Auth::DefaultBearerTokenProviderChain::GetAWSBearerToken() +{ + for (auto&& bearerTokenProvider : m_providerChain) + { + if(!bearerTokenProvider) { + AWS_LOGSTREAM_FATAL(SSO_DEFAULT_BEARER_TOKEN_PROVIDER_CHAIN_LOG_TAG, + "Unexpected nullptr in DefaultBearerTokenProviderChain::m_providerChain"); + break; + } + AWSBearerToken bearerToken = bearerTokenProvider->GetAWSBearerToken(); + if(!bearerToken.IsExpiredOrEmpty()) + { + return bearerToken; + } + } + return AWSBearerToken("", Aws::Utils::DateTime(0.0)); +} + +Aws::Auth::DefaultBearerTokenProviderChain::DefaultBearerTokenProviderChain() +{ + AddProvider(Aws::MakeShared<Aws::Auth::SSOBearerTokenProvider>(SSO_DEFAULT_BEARER_TOKEN_PROVIDER_CHAIN_LOG_TAG)); +} diff --git a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/bearer-token-provider/SSOBearerTokenProvider.cpp b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/bearer-token-provider/SSOBearerTokenProvider.cpp new file mode 100644 index 0000000000..b55131e340 --- /dev/null +++ b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/bearer-token-provider/SSOBearerTokenProvider.cpp @@ -0,0 +1,244 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + + +#include <aws/core/auth/bearer-token-provider/SSOBearerTokenProvider.h> +#include <aws/core/auth/AWSCredentialsProvider.h> +#include <aws/core/config/AWSProfileConfigLoader.h> +#include <aws/core/internal/AWSHttpResourceClient.h> +#include <aws/core/platform/Environment.h> +#include <aws/core/platform/FileSystem.h> +#include <aws/core/utils/logging/LogMacros.h> +#include <aws/core/utils/FileSystemUtils.h> +#include <aws/core/client/SpecifiedRetryableErrorsRetryStrategy.h> +#include <aws/core/utils/HashingUtils.h> +#include <aws/core/utils/json/JsonSerializer.h> + +using namespace Aws::Auth; + +using Aws::Utils::Threading::ReaderLockGuard; + + +static const char SSO_BEARER_TOKEN_PROVIDER_LOG_TAG[] = "SSOBearerTokenProvider"; +static const char SSO_GRANT_TYPE[] = "refresh_token"; + +const size_t SSOBearerTokenProvider::REFRESH_WINDOW_BEFORE_EXPIRATION_S = 600; +const size_t SSOBearerTokenProvider::REFRESH_ATTEMPT_INTERVAL_S = 30; + +SSOBearerTokenProvider::SSOBearerTokenProvider() + : m_profileToUse(Aws::Auth::GetConfigProfileName()), + m_lastUpdateAttempt((int64_t) 0) +{ + AWS_LOGSTREAM_INFO(SSO_BEARER_TOKEN_PROVIDER_LOG_TAG, "Setting sso bearerToken provider to read config from " << m_profileToUse); +} + +SSOBearerTokenProvider::SSOBearerTokenProvider(const Aws::String& awsProfile) + : m_profileToUse(awsProfile), + m_lastUpdateAttempt((int64_t) 0) +{ + AWS_LOGSTREAM_INFO(SSO_BEARER_TOKEN_PROVIDER_LOG_TAG, "Setting sso bearerToken provider to read config from " << m_profileToUse); +} + +AWSBearerToken SSOBearerTokenProvider::GetAWSBearerToken() +{ + Aws::Utils::Threading::ReaderLockGuard guard(m_reloadLock); + if(m_token.IsEmpty()) + { + Reload(); + } + if(!m_token.IsEmpty()) + { + const Aws::Utils::DateTime now = Aws::Utils::DateTime::Now(); + if (now >= m_token.GetExpiration() - std::chrono::seconds(REFRESH_WINDOW_BEFORE_EXPIRATION_S) && + m_lastUpdateAttempt + std::chrono::seconds(REFRESH_ATTEMPT_INTERVAL_S) < now) + { + guard.UpgradeToWriterLock(); + RefreshFromSso(); + } + } + + if(m_token.IsExpiredOrEmpty()) + { + /* If a loaded token has expired and has insufficient metadata to perform a refresh the SSO token + provider must raise an exception that the token has expired and cannot be refreshed. + Error logging and returning an empty object instead because of disabled exceptions and poor legacy API design. */ + AWS_LOGSTREAM_ERROR(SSO_BEARER_TOKEN_PROVIDER_LOG_TAG, "SSOBearerTokenProvider is unable to provide a token"); + return Aws::Auth::AWSBearerToken("", Aws::Utils::DateTime(0.0)); + } + return m_token; +} + +void SSOBearerTokenProvider::Reload() +{ + CachedSsoToken cachedSsoToken = LoadAccessTokenFile(); + if(cachedSsoToken.accessToken.empty()) { + AWS_LOGSTREAM_TRACE(SSO_BEARER_TOKEN_PROVIDER_LOG_TAG, "Access token for SSO not available"); + return; + } + const Aws::Utils::DateTime now = Aws::Utils::DateTime::Now(); + if(cachedSsoToken.expiresAt < now) { + AWS_LOGSTREAM_ERROR(SSO_BEARER_TOKEN_PROVIDER_LOG_TAG, "Cached Token is already expired at " << cachedSsoToken.expiresAt.ToGmtString(Aws::Utils::DateFormat::ISO_8601)); + return; + } + + m_token.SetToken(cachedSsoToken.accessToken); + m_token.SetExpiration(cachedSsoToken.expiresAt); +} + +void SSOBearerTokenProvider::RefreshFromSso() +{ + CachedSsoToken cachedSsoToken = LoadAccessTokenFile(); + + if(!m_client) + { + Aws::Client::ClientConfiguration config; + config.scheme = Aws::Http::Scheme::HTTPS; + /* The SSO token provider must not resolve if any SSO configuration values are present directly on the profile + * instead of an `sso-session` section. The SSO token provider must ignore these configuration values if these + * values are present directly on the profile instead of an `sso-session` section. */ + // config.region = m_profile.GetSsoRegion(); // <- intentionally not used per comment above + config.region = cachedSsoToken.region; + m_client = Aws::MakeUnique<Aws::Internal::SSOCredentialsClient>(SSO_BEARER_TOKEN_PROVIDER_LOG_TAG, config); + } + + Aws::Internal::SSOCredentialsClient::SSOCreateTokenRequest ssoCreateTokenRequest; + ssoCreateTokenRequest.clientId = cachedSsoToken.clientId; + ssoCreateTokenRequest.clientSecret = cachedSsoToken.clientSecret; + ssoCreateTokenRequest.grantType = SSO_GRANT_TYPE; + ssoCreateTokenRequest.refreshToken = cachedSsoToken.refreshToken; + + if(!m_client) { + AWS_LOGSTREAM_FATAL(SSO_BEARER_TOKEN_PROVIDER_LOG_TAG, "Unexpected nullptr in SSOBearerTokenProvider::m_client"); + return; + } + Aws::Internal::SSOCredentialsClient::SSOCreateTokenResult result = m_client->CreateToken(ssoCreateTokenRequest); + if(!result.accessToken.empty()) + { + cachedSsoToken.accessToken = result.accessToken; + cachedSsoToken.expiresAt = Aws::Utils::DateTime::Now() + std::chrono::seconds(result.expiresIn); + if(!result.refreshToken.empty()) { + cachedSsoToken.refreshToken = result.refreshToken; + } + if(!result.clientId.empty()) { + cachedSsoToken.clientId = result.clientId; + } + } + + if(WriteAccessTokenFile(cachedSsoToken)) + { + m_token.SetToken(cachedSsoToken.accessToken); + m_token.SetExpiration(cachedSsoToken.expiresAt); + } + +} + +SSOBearerTokenProvider::CachedSsoToken SSOBearerTokenProvider::LoadAccessTokenFile() const +{ + SSOBearerTokenProvider::CachedSsoToken retValue; + + const Aws::Config::Profile& profile = Aws::Config::GetCachedConfigProfile(m_profileToUse); + if(!profile.IsSsoSessionSet()) { + AWS_LOGSTREAM_ERROR(SSO_BEARER_TOKEN_PROVIDER_LOG_TAG, "SSOBearerTokenProvider set to use a profile " << m_profileToUse << " without a sso_session. Unable to load cached token."); + return retValue; + } + + Aws::String hashedStartUrl = Aws::Utils::HashingUtils::HexEncode(Aws::Utils::HashingUtils::CalculateSHA1(profile.GetSsoSession().GetName())); + Aws::String profileDirectory = ProfileConfigFileAWSCredentialsProvider::GetProfileDirectory(); + Aws::StringStream ssToken; + ssToken << profileDirectory; + ssToken << Aws::FileSystem::PATH_DELIM << "sso" << Aws::FileSystem::PATH_DELIM << "cache" << Aws::FileSystem::PATH_DELIM << hashedStartUrl << ".json"; + auto ssoAccessTokenPath = ssToken.str(); + AWS_LOGSTREAM_DEBUG(SSO_BEARER_TOKEN_PROVIDER_LOG_TAG, "Preparing to load token from: " << ssoAccessTokenPath); + + Aws::IFStream inputFile(ssoAccessTokenPath.c_str()); + if(inputFile) + { + AWS_LOGSTREAM_DEBUG(SSO_BEARER_TOKEN_PROVIDER_LOG_TAG, "Reading content from token file: " << ssoAccessTokenPath); + + Aws::Utils::Json::JsonValue tokenDoc(inputFile); + if (!tokenDoc.WasParseSuccessful()) + { + AWS_LOGSTREAM_ERROR(SSO_BEARER_TOKEN_PROVIDER_LOG_TAG, "Failed to parse token file: " << ssoAccessTokenPath); + return retValue; + } + Utils::Json::JsonView tokenView(tokenDoc); + + retValue.accessToken = tokenView.GetString("accessToken"); + retValue.expiresAt = Aws::Utils::DateTime(tokenView.GetString("expiresAt"), Aws::Utils::DateFormat::ISO_8601); + retValue.refreshToken = tokenView.GetString("refreshToken"); + retValue.clientId = tokenView.GetString("clientId"); + retValue.clientSecret = tokenView.GetString("clientSecret"); + retValue.registrationExpiresAt = Aws::Utils::DateTime(tokenView.GetString("registrationExpiresAt"), Aws::Utils::DateFormat::ISO_8601); + retValue.region = tokenView.GetString("region"); + retValue.startUrl = tokenView.GetString("startUrl"); + + return retValue; + } + else + { + AWS_LOGSTREAM_INFO(SSO_BEARER_TOKEN_PROVIDER_LOG_TAG, "Unable to open token file on path: " << ssoAccessTokenPath); + return retValue; + } +} + +bool SSOBearerTokenProvider::WriteAccessTokenFile(const CachedSsoToken& token) const +{ + const Aws::Config::Profile& profile = Aws::Config::GetCachedConfigProfile(m_profileToUse); + if(!profile.IsSsoSessionSet()) { + AWS_LOGSTREAM_ERROR(SSO_BEARER_TOKEN_PROVIDER_LOG_TAG, "SSOBearerTokenProvider set to use a profile " + << m_profileToUse << " without a sso_session. Unable to write a cached token."); + return false; + } + + Aws::String hashedStartUrl = Aws::Utils::HashingUtils::HexEncode(Aws::Utils::HashingUtils::CalculateSHA1(profile.GetSsoSession().GetName())); + Aws::String profileDirectory = ProfileConfigFileAWSCredentialsProvider::GetProfileDirectory(); + Aws::StringStream ssToken; + ssToken << profileDirectory; + ssToken << Aws::FileSystem::PATH_DELIM << "sso" << Aws::FileSystem::PATH_DELIM << "cache" << Aws::FileSystem::PATH_DELIM << hashedStartUrl << ".json"; + auto ssoAccessTokenPath = ssToken.str(); + AWS_LOGSTREAM_DEBUG(SSO_BEARER_TOKEN_PROVIDER_LOG_TAG, "Preparing to write token to: " << ssoAccessTokenPath); + + Aws::OFStream outputFileStream(ssoAccessTokenPath.c_str(), std::ios_base::out | std::ios_base::trunc); + if(outputFileStream && outputFileStream.good()) + { + AWS_LOGSTREAM_DEBUG(SSO_BEARER_TOKEN_PROVIDER_LOG_TAG, "Writing content to token file: " << ssoAccessTokenPath); + + Aws::Utils::Json::JsonValue cachedTokenDoc; + if(!token.accessToken.empty()) { + cachedTokenDoc.WithString("accessToken", token.accessToken); + } + if(token.expiresAt != 0.0) { + cachedTokenDoc.WithString("expiresAt", token.expiresAt.ToGmtString(Aws::Utils::DateFormat::ISO_8601)); + } + if(!token.refreshToken.empty()) { + cachedTokenDoc.WithString("refreshToken", token.refreshToken); + } + if(!token.clientId.empty()) { + cachedTokenDoc.WithString("clientId", token.clientId); + } + if(!token.clientSecret.empty()) { + cachedTokenDoc.WithString("clientSecret", token.clientSecret); + } + if(token.registrationExpiresAt != 0.0) { + cachedTokenDoc.WithString("registrationExpiresAt", token.registrationExpiresAt.ToGmtString(Aws::Utils::DateFormat::ISO_8601)); + } + if(!token.region.empty()) { + cachedTokenDoc.WithString("region", token.region); + } + if(!token.startUrl.empty()) { + cachedTokenDoc.WithString("startUrl", token.startUrl); + } + + const Aws::String& resultingJsonStr = cachedTokenDoc.View().WriteReadable();; + outputFileStream << resultingJsonStr; + + return outputFileStream.good(); + } + else + { + AWS_LOGSTREAM_INFO(SSO_BEARER_TOKEN_PROVIDER_LOG_TAG, "Unable to open token file on path for writing: " << ssoAccessTokenPath); + return false; + } +} diff --git a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer-provider/BearerTokenAuthSignerProvider.cpp b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer-provider/BearerTokenAuthSignerProvider.cpp new file mode 100644 index 0000000000..9bb9c5edae --- /dev/null +++ b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer-provider/BearerTokenAuthSignerProvider.cpp @@ -0,0 +1,46 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/core/auth/signer-provider/BearerTokenAuthSignerProvider.h> + +#include <aws/core/auth/signer/AWSNullSigner.h> + +#include <aws/core/utils/logging/LogMacros.h> +#include <aws/core/auth/AWSCredentialsProvider.h> +#include <aws/core/utils/memory/stl/AWSAllocator.h> + +const char BEARER_TOKEN_AUTH_SIGNER_PROVIDER_ALLOC_TAG[] = "BearerTokenAuthSignerProvider"; + +using namespace Aws::Auth; + +BearerTokenAuthSignerProvider::BearerTokenAuthSignerProvider(const std::shared_ptr<Aws::Auth::AWSBearerTokenProviderBase> bearerTokenProvider) +{ + m_signers.emplace_back(Aws::MakeShared<Aws::Client::AWSAuthBearerSigner>(BEARER_TOKEN_AUTH_SIGNER_PROVIDER_ALLOC_TAG, bearerTokenProvider)); + m_signers.emplace_back(Aws::MakeShared<Aws::Client::AWSNullSigner>(BEARER_TOKEN_AUTH_SIGNER_PROVIDER_ALLOC_TAG)); +} + +std::shared_ptr<Aws::Client::AWSAuthSigner> BearerTokenAuthSignerProvider::GetSigner(const Aws::String& signerName) const +{ + for(const auto& signer : m_signers) + { + if(!signer) { + AWS_LOGSTREAM_FATAL(BEARER_TOKEN_AUTH_SIGNER_PROVIDER_ALLOC_TAG, "Unexpected nullptr in BearerTokenAuthSignerProvider::m_signers"); + break; + } + if(signer->GetName() == signerName) + { + return signer; + } + } + AWS_LOGSTREAM_ERROR(BEARER_TOKEN_AUTH_SIGNER_PROVIDER_ALLOC_TAG, "Request's signer: '" << signerName << "' is not found in the signer's map."); + assert(false); + return nullptr; +} + +void BearerTokenAuthSignerProvider::AddSigner(std::shared_ptr<Aws::Client::AWSAuthSigner>& signer) +{ + assert(signer); + m_signers.emplace_back(signer); +} diff --git a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/AWSAuthSignerProvider.cpp b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer-provider/DefaultAuthSignerProvider.cpp index 31fd6c006b..fb7e0cfa40 100644 --- a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/AWSAuthSignerProvider.cpp +++ b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer-provider/DefaultAuthSignerProvider.cpp @@ -2,10 +2,14 @@ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ + +#include <aws/core/auth/signer-provider/DefaultAuthSignerProvider.h> + +#include <aws/core/auth/signer/AWSAuthEventStreamV4Signer.h> +#include <aws/core/auth/signer/AWSNullSigner.h> + #include <aws/core/utils/logging/LogMacros.h> -#include <aws/core/auth/AWSAuthSignerProvider.h> -#include <aws/core/auth/AWSAuthSigner.h> #include <aws/core/auth/AWSCredentialsProvider.h> #include <aws/core/utils/memory/stl/AWSAllocator.h> @@ -14,9 +18,10 @@ const char CLASS_TAG[] = "AuthSignerProvider"; using namespace Aws::Auth; DefaultAuthSignerProvider::DefaultAuthSignerProvider(const std::shared_ptr<AWSCredentialsProvider>& credentialsProvider, - const Aws::String& serviceName, const Aws::String& region) + const Aws::String& serviceName, const Aws::String& region, Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy signingPolicy, bool urlEscapePath) { - m_signers.emplace_back(Aws::MakeShared<Aws::Client::AWSAuthV4Signer>(CLASS_TAG, credentialsProvider, serviceName.c_str(), region)); + m_signers.emplace_back(Aws::MakeShared<Aws::Client::AWSAuthV4Signer>(CLASS_TAG, credentialsProvider, serviceName.c_str(), region, signingPolicy, urlEscapePath, AWSSigningAlgorithm::SIGV4)); + m_signers.emplace_back(Aws::MakeShared<Aws::Client::AWSAuthV4Signer>(CLASS_TAG, credentialsProvider, serviceName.c_str(), region, signingPolicy, urlEscapePath, AWSSigningAlgorithm::ASYMMETRIC_SIGV4)); m_signers.emplace_back(Aws::MakeShared<Aws::Client::AWSAuthEventStreamV4Signer>(CLASS_TAG, credentialsProvider, serviceName.c_str(), region)); m_signers.emplace_back(Aws::MakeShared<Aws::Client::AWSNullSigner>(CLASS_TAG)); } diff --git a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer/AWSAuthBearerSigner.cpp b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer/AWSAuthBearerSigner.cpp new file mode 100644 index 0000000000..ff14c8a371 --- /dev/null +++ b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer/AWSAuthBearerSigner.cpp @@ -0,0 +1,50 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/core/auth/signer/AWSAuthBearerSigner.h> +#include <aws/core/auth/bearer-token-provider/AWSBearerTokenProviderBase.h> + +#include <aws/core/utils/logging/LogMacros.h> +#include <aws/core/http/HttpRequest.h> + +namespace Aws +{ + namespace Auth + { + const char BEARER_SIGNER[] = "Bearer"; + } + + namespace Client + { + static const char LOGGING_TAG[] = "AWSAuthBearerSigner"; + static const char AUTHORIZATION_HEADER[] = "authorization"; + + bool AWSAuthBearerSigner::SignRequest(Aws::Http::HttpRequest& ioRequest) const + { + if(Aws::Http::Scheme::HTTPS != ioRequest.GetUri().GetScheme()) + { + // Clients MUST always use TLS (https) or equivalent transport security + // when making requests with bearer tokens. + // https://datatracker.ietf.org/doc/html/rfc6750 + AWS_LOGSTREAM_ERROR(LOGGING_TAG, "HTTPS scheme must be used with a bearer token authorization"); + return false; + } + if(!m_bearerTokenProvider) + { + AWS_LOGSTREAM_FATAL(LOGGING_TAG, "Unexpected nullptr AWSAuthBearerSigner::m_bearerTokenProvider"); + return false; + } + const Aws::Auth::AWSBearerToken& token = m_bearerTokenProvider->GetAWSBearerToken(); + if(token.IsExpiredOrEmpty()) + { + AWS_LOGSTREAM_ERROR(LOGGING_TAG, "Invalid bearer token to use: expired or empty"); + return false; + } + + ioRequest.SetHeaderValue(AUTHORIZATION_HEADER, "Bearer " + token.GetToken()); + return true; + } + } +} diff --git a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer/AWSAuthEventStreamV4Signer.cpp b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer/AWSAuthEventStreamV4Signer.cpp new file mode 100644 index 0000000000..195e83a751 --- /dev/null +++ b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer/AWSAuthEventStreamV4Signer.cpp @@ -0,0 +1,320 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/core/auth/signer/AWSAuthEventStreamV4Signer.h> +#include <aws/core/auth/signer/AWSAuthSignerCommon.h> +#include <aws/core/auth/signer/AWSAuthSignerHelper.h> + +#include <aws/core/auth/AWSCredentialsProvider.h> +#include <aws/core/http/HttpRequest.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/Sha256HMAC.h> +#include <aws/core/utils/stream/PreallocatedStreamBuf.h> +#include <aws/core/utils/event/EventMessage.h> +#include <aws/core/utils/event/EventHeader.h> + +#include <aws/crt/auth/Credentials.h> +#include <aws/crt/http/HttpRequestResponse.h> + +#include <iomanip> +#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* EVENT_STREAM_CONTENT_SHA256 = "STREAMING-AWS4-HMAC-SHA256-EVENTS"; +static const char* EVENT_STREAM_PAYLOAD = "AWS4-HMAC-SHA256-PAYLOAD"; +static const char* v4StreamingLogTag = "AWSAuthEventStreamV4Signer"; + +namespace Aws +{ + namespace Auth + { + const char EVENTSTREAM_SIGV4_SIGNER[] = "EventStreamSignatureV4"; + const char EVENTSTREAM_SIGNATURE_HEADER[] = ":chunk-signature"; + const char EVENTSTREAM_DATE_HEADER[] = ":date"; + } +} + +AWSAuthEventStreamV4Signer::AWSAuthEventStreamV4Signer(const std::shared_ptr<Auth::AWSCredentialsProvider>& + credentialsProvider, const char* serviceName, const Aws::String& region) : + m_serviceName(serviceName), + m_region(region), + m_credentialsProvider(credentialsProvider) +{ + + m_unsignedHeaders.emplace_back(Aws::Auth::AWSAuthHelper::X_AMZN_TRACE_ID); + m_unsignedHeaders.emplace_back(USER_AGENT_HEADER); +} + +bool AWSAuthEventStreamV4Signer::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()); + } + + request.SetHeaderValue(Aws::Auth::AWSAuthHelper::X_AMZ_CONTENT_SHA256, EVENT_STREAM_CONTENT_SHA256); + + //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 : Aws::Auth::AWSAuthHelper::CanonicalizeHeaders(request.GetHeaders())) + { + if(ShouldSignHeader(header.first)) + { + headersStream << header.first.c_str() << ":" << header.second.c_str() << Aws::Auth::AWSAuthHelper::NEWLINE; + signedHeadersStream << header.first.c_str() << ";"; + } + } + + Aws::String canonicalHeadersString = headersStream.str(); + AWS_LOGSTREAM_DEBUG(v4StreamingLogTag, "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(v4StreamingLogTag, "Signed Headers value:" << signedHeadersValue); + + //generate generalized canonicalized request string. + Aws::String canonicalRequestString = Aws::Auth::AWSAuthHelper::CanonicalizeRequestSigningString(request, true/* m_urlEscapePath */); + + //append v4 stuff to the canonical request string. + canonicalRequestString.append(canonicalHeadersString); + canonicalRequestString.append(Aws::Auth::AWSAuthHelper::NEWLINE); + canonicalRequestString.append(signedHeadersValue); + canonicalRequestString.append(Aws::Auth::AWSAuthHelper::NEWLINE); + canonicalRequestString.append(EVENT_STREAM_CONTENT_SHA256); + + AWS_LOGSTREAM_DEBUG(v4StreamingLogTag, "Canonical Request String: " << canonicalRequestString); + + //now compute sha256 on that request string + auto hashResult = m_hash.Calculate(canonicalRequestString); + if (!hashResult.IsSuccess()) + { + AWS_LOGSTREAM_ERROR(v4StreamingLogTag, "Failed to hash (sha256) request string"); + AWS_LOGSTREAM_DEBUG(v4StreamingLogTag, "The request string is: \"" << canonicalRequestString << "\""); + return false; + } + + auto sha256Digest = hashResult.GetResult(); + Aws::String canonicalRequestHash = HashingUtils::HexEncode(sha256Digest); + Aws::String simpleDate = now.ToGmtString(Aws::Auth::AWSAuthHelper::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::Auth::AWSAuthHelper::AWS_HMAC_SHA256 << " " << Aws::Auth::AWSAuthHelper::CREDENTIAL << Aws::Auth::AWSAuthHelper::EQ << credentials.GetAWSAccessKeyId() << "/" << simpleDate + << "/" << signingRegion << "/" << signingServiceName << "/" << Aws::Auth::AWSAuthHelper::AWS4_REQUEST << ", " << Aws::Auth::AWSAuthHelper::SIGNED_HEADERS << Aws::Auth::AWSAuthHelper::EQ + << signedHeadersValue << ", " << SIGNATURE << Aws::Auth::AWSAuthHelper::EQ << HashingUtils::HexEncode(finalSignature); + + auto awsAuthString = ss.str(); + AWS_LOGSTREAM_DEBUG(v4StreamingLogTag, "Signing request with: " << awsAuthString); + request.SetAwsAuthorization(awsAuthString); + request.SetSigningAccessKey(credentials.GetAWSAccessKeyId()); + request.SetSigningRegion(signingRegion); + return true; +} + +// this works regardless if the current machine is Big/Little Endian +static void WriteBigEndian(Aws::String& str, uint64_t n) +{ + int shift = 56; + while(shift >= 0) + { + str.push_back((n >> shift) & 0xFF); + shift -= 8; + } +} + +bool AWSAuthEventStreamV4Signer::SignEventMessage(Event::Message& message, Aws::String& priorSignature) const +{ + using Event::EventHeaderValue; + + Aws::StringStream stringToSign; + stringToSign << EVENT_STREAM_PAYLOAD << Aws::Auth::AWSAuthHelper::NEWLINE; + const DateTime now = GetSigningTimestamp(); + const auto simpleDate = now.ToGmtString(Aws::Auth::AWSAuthHelper::SIMPLE_DATE_FORMAT_STR); + stringToSign << now.ToGmtString(DateFormat::ISO_8601_BASIC) << Aws::Auth::AWSAuthHelper::NEWLINE + << simpleDate << "/" << m_region << "/" + << m_serviceName << "/aws4_request" << Aws::Auth::AWSAuthHelper::NEWLINE << priorSignature << Aws::Auth::AWSAuthHelper::NEWLINE; + + + Aws::String nonSignatureHeaders; + nonSignatureHeaders.push_back(char(sizeof(EVENTSTREAM_DATE_HEADER) - 1)); // length of the string + nonSignatureHeaders += EVENTSTREAM_DATE_HEADER; + nonSignatureHeaders.push_back(static_cast<char>(EventHeaderValue::EventHeaderType::TIMESTAMP)); // type of the value + WriteBigEndian(nonSignatureHeaders, static_cast<uint64_t>(now.Millis())); // the value of the timestamp in big-endian + + auto hashOutcome = m_hash.Calculate(nonSignatureHeaders); + if (!hashOutcome.IsSuccess()) + { + AWS_LOGSTREAM_ERROR(v4StreamingLogTag, "Failed to hash (sha256) non-signature headers."); + return false; + } + + const auto nonSignatureHeadersHash = hashOutcome.GetResult(); + stringToSign << HashingUtils::HexEncode(nonSignatureHeadersHash) << Aws::Auth::AWSAuthHelper::NEWLINE; + + if (message.GetEventPayload().empty()) + { + AWS_LOGSTREAM_WARN(v4StreamingLogTag, "Attempting to sign an empty message (no payload and no headers). " + "It is unlikely that this is the intended behavior."); + } + else + { + // use a preallocatedStreamBuf to avoid making a copy. + // The Hashing API requires either Aws::String or IStream as input. + // TODO: the hashing API should be accept 'unsigned char*' as input. + Utils::Stream::PreallocatedStreamBuf streamBuf(message.GetEventPayload().data(), message.GetEventPayload().size()); + Aws::IOStream payload(&streamBuf); + hashOutcome = m_hash.Calculate(payload); + + if (!hashOutcome.IsSuccess()) + { + AWS_LOGSTREAM_ERROR(v4StreamingLogTag, "Failed to hash (sha256) non-signature headers."); + return false; + } + const auto payloadHash = hashOutcome.GetResult(); + stringToSign << HashingUtils::HexEncode(payloadHash); + AWS_LOGSTREAM_DEBUG(v4StreamingLogTag, "Payload hash - " << HashingUtils::HexEncode(payloadHash)); + } + + Aws::Utils::ByteBuffer finalSignatureDigest = GenerateSignature(m_credentialsProvider->GetAWSCredentials(), stringToSign.str(), simpleDate, m_region, m_serviceName); + const auto finalSignature = HashingUtils::HexEncode(finalSignatureDigest); + AWS_LOGSTREAM_DEBUG(v4StreamingLogTag, "Final computed signing hash: " << finalSignature); + priorSignature = finalSignature; + + message.InsertEventHeader(EVENTSTREAM_DATE_HEADER, EventHeaderValue(now.Millis(), EventHeaderValue::EventHeaderType::TIMESTAMP)); + message.InsertEventHeader(EVENTSTREAM_SIGNATURE_HEADER, std::move(finalSignatureDigest)); + + AWS_LOGSTREAM_INFO(v4StreamingLogTag, "Event chunk final signature - " << finalSignature); + return true; +} + +bool AWSAuthEventStreamV4Signer::ShouldSignHeader(const Aws::String& header) const +{ + return std::find(m_unsignedHeaders.cbegin(), m_unsignedHeaders.cend(), Aws::Utils::StringUtils::ToLower(header.c_str())) == m_unsignedHeaders.cend(); +} + +Aws::Utils::ByteBuffer AWSAuthEventStreamV4Signer::GenerateSignature(const AWSCredentials& credentials, const Aws::String& stringToSign, + const Aws::String& simpleDate, const Aws::String& region, const Aws::String& serviceName) const +{ + Utils::Threading::ReaderLockGuard guard(m_derivedKeyLock); + const auto& secretKey = credentials.GetAWSSecretKey(); + if (secretKey != m_currentSecretKey || simpleDate != m_currentDateStr) + { + guard.UpgradeToWriterLock(); + // double-checked lock to prevent updating twice + if (m_currentDateStr != simpleDate || m_currentSecretKey != secretKey) + { + m_currentSecretKey = secretKey; + m_currentDateStr = simpleDate; + m_derivedKey = ComputeHash(m_currentSecretKey, m_currentDateStr, region, serviceName); + } + + } + return GenerateSignature(stringToSign, m_derivedKey); +} + +Aws::Utils::ByteBuffer AWSAuthEventStreamV4Signer::GenerateSignature(const Aws::String& stringToSign, const ByteBuffer& key) const +{ + AWS_LOGSTREAM_DEBUG(v4StreamingLogTag, "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(v4StreamingLogTag, "Unable to hmac (sha256) final string"); + AWS_LOGSTREAM_DEBUG(v4StreamingLogTag, "The final string is: \"" << stringToSign << "\""); + return {}; + } + + return hashResult.GetResult(); +} + +Aws::String AWSAuthEventStreamV4Signer::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::Auth::AWSAuthHelper::AWS_HMAC_SHA256 << Aws::Auth::AWSAuthHelper::NEWLINE << dateValue << Aws::Auth::AWSAuthHelper::NEWLINE << simpleDate << "/" << region << "/" + << serviceName << "/" << Aws::Auth::AWSAuthHelper::AWS4_REQUEST << Aws::Auth::AWSAuthHelper::NEWLINE << canonicalRequestHash; + + return ss.str(); +} + +Aws::Utils::ByteBuffer AWSAuthEventStreamV4Signer::ComputeHash(const Aws::String& secretKey, + const Aws::String& simpleDate, const Aws::String& region, const Aws::String& serviceName) const +{ + Aws::String signingKey(Aws::Auth::AWSAuthHelper::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(v4StreamingLogTag, "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(v4StreamingLogTag, "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(v4StreamingLogTag, "Failed to HMAC (SHA256) service string \"" << m_serviceName << "\""); + return {}; + } + + auto kService = hashResult.GetResult(); + hashResult = m_HMAC.Calculate(ByteBuffer((unsigned char*)Aws::Auth::AWSAuthHelper::AWS4_REQUEST, strlen(Aws::Auth::AWSAuthHelper::AWS4_REQUEST)), kService); + if (!hashResult.IsSuccess()) + { + AWS_LOGSTREAM_ERROR(v4StreamingLogTag, "Unable to HMAC (SHA256) request string"); + AWS_LOGSTREAM_DEBUG(v4StreamingLogTag, "The request string is: \"" << Aws::Auth::AWSAuthHelper::AWS4_REQUEST << "\""); + return {}; + } + return hashResult.GetResult(); +} diff --git a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer/AWSAuthSignerCommon.cpp b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer/AWSAuthSignerCommon.cpp new file mode 100644 index 0000000000..d26f41e6b3 --- /dev/null +++ b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer/AWSAuthSignerCommon.cpp @@ -0,0 +1,14 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/core/auth/signer/AWSAuthSignerCommon.h> + +namespace Aws +{ +namespace Auth +{ +const char SIGNATURE[] = "Signature"; +} // namespace Auth +} // namespace Aws
\ No newline at end of file diff --git a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer/AWSAuthSignerHelper.cpp b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer/AWSAuthSignerHelper.cpp new file mode 100644 index 0000000000..5f7005d1da --- /dev/null +++ b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer/AWSAuthSignerHelper.cpp @@ -0,0 +1,103 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/core/auth/signer/AWSAuthSignerHelper.h> +#include <aws/core/http/HttpTypes.h> + +#include <aws/core/http/HttpRequest.h> +#include <aws/core/http/URI.h> +#include <aws/core/utils/StringUtils.h> + +namespace Aws +{ +namespace Auth +{ + +const char* AWSAuthHelper::EQ = "="; +const char* AWSAuthHelper::AWS_HMAC_SHA256 = "AWS4-HMAC-SHA256"; +const char* AWSAuthHelper::AWS4_REQUEST = "aws4_request"; +const char* AWSAuthHelper::SIGNED_HEADERS = "SignedHeaders"; +const char* AWSAuthHelper::CREDENTIAL = "Credential"; +const char* AWSAuthHelper::NEWLINE = "\n"; +const char* AWSAuthHelper::X_AMZN_TRACE_ID = "x-amzn-trace-id"; +const char* AWSAuthHelper::X_AMZ_CONTENT_SHA256 = "x-amz-content-sha256"; +const char* AWSAuthHelper::SIGNING_KEY = "AWS4"; +const char* AWSAuthHelper::SIMPLE_DATE_FORMAT_STR = "%Y%m%d"; + +Aws::String Aws::Auth::AWSAuthHelper::CanonicalizeRequestSigningString(Aws::Http::HttpRequest& request, bool urlEscapePath) +{ + request.CanonicalizeRequest(); + Aws::StringStream signingStringStream; + signingStringStream << Aws::Http::HttpMethodMapper::GetNameForHttpMethod(request.GetMethod()); + + Aws::Http::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. + uriCpy.SetPath(uriCpy.GetURLEncodedPathRFC3986()); + // However, SignatureV4 uses this URL encoding scheme + signingStringStream << AWSAuthHelper::NEWLINE << uriCpy.GetURLEncodedPath() << AWSAuthHelper::NEWLINE; + } + else + { + // For the services that DO decode the URL first; we don't need to double encode it. + signingStringStream << AWSAuthHelper::NEWLINE << uriCpy.GetURLEncodedPath() << AWSAuthHelper::NEWLINE; + } + + if (request.GetQueryString().find('=') != std::string::npos) + { + signingStringStream << request.GetQueryString().substr(1) << AWSAuthHelper::NEWLINE; + } + else if (request.GetQueryString().size() > 1) + { + signingStringStream << request.GetQueryString().substr(1) << "=" << AWSAuthHelper::NEWLINE; + } + else + { + signingStringStream << AWSAuthHelper::NEWLINE; + } + + return signingStringStream.str(); +} + +Aws::Http::HeaderValueCollection Aws::Auth::AWSAuthHelper::CanonicalizeHeaders(Aws::Http::HeaderValueCollection&& headers) +{ + Aws::Http::HeaderValueCollection canonicalHeaders; + for (const auto& header : headers) + { + auto trimmedHeaderName = Aws::Utils::StringUtils::Trim(header.first.c_str()); + auto trimmedHeaderValue = Aws::Utils::StringUtils::Trim(header.second.c_str()); + + //multiline gets converted to line1,line2,etc... + auto headerMultiLine = Aws::Utils::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 += Aws::Utils::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; +} + +} // namespace Auth +} // namespace Aws
\ No newline at end of file diff --git a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer/AWSAuthV4Signer.cpp b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer/AWSAuthV4Signer.cpp new file mode 100644 index 0000000000..f8bfdbf867 --- /dev/null +++ b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer/AWSAuthV4Signer.cpp @@ -0,0 +1,580 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/core/auth/signer/AWSAuthV4Signer.h> +#include <aws/core/auth/signer/AWSAuthSignerCommon.h> +#include <aws/core/auth/signer/AWSAuthSignerHelper.h> + +#include <aws/core/auth/AWSCredentialsProvider.h> +#include <aws/core/http/HttpRequest.h> +#include <aws/core/http/URI.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/crt/auth/Credentials.h> +#include <aws/crt/http/HttpRequestResponse.h> + +#include <iomanip> +#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* 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* STREAMING_UNSIGNED_PAYLOAD_TRAILER = "STREAMING-UNSIGNED-PAYLOAD-TRAILER"; +static const char* X_AMZ_SIGNATURE = "X-Amz-Signature"; +static const char* USER_AGENT = "user-agent"; +static const char* EMPTY_STRING_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; + +static const char v4LogTag[] = "AWSAuthV4Signer"; +static const char v4AsymmetricLogTag[] = "AWSAuthSymmetricV4Signer"; + +namespace Aws +{ + namespace Auth + { + const char SIGV4_SIGNER[] = "SignatureV4"; + const char ASYMMETRIC_SIGV4_SIGNER[] = "AsymmetricSignatureV4"; + } +} + +AWSAuthV4Signer::AWSAuthV4Signer(const std::shared_ptr<Auth::AWSCredentialsProvider>& credentialsProvider, const char* serviceName, + const Aws::String& region, PayloadSigningPolicy signingPolicy, bool urlEscapePath, AWSSigningAlgorithm signingAlgorithm) : + m_includeSha256HashHeader(true), + m_signingAlgorithm(signingAlgorithm), + 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, Aws::Auth::AWSAuthHelper::X_AMZN_TRACE_ID}), + m_payloadSigningPolicy(signingPolicy), + m_urlEscapePath(urlEscapePath) +{ + //go ahead and warm up the signing cache. + ComputeHash(credentialsProvider->GetAWSCredentials().GetAWSSecretKey(), DateTime::CalculateGmtTimestampAsString(Aws::Auth::AWSAuthHelper::SIMPLE_DATE_FORMAT_STR), region, m_serviceName); +} + +AWSAuthV4Signer::~AWSAuthV4Signer() +{ + // empty destructor in .cpp file to keep from needing the implementation of (AWSCredentialsProvider, Sha256, Sha256HMAC) in the header file +} + +bool AWSAuthV4Signer::SignRequestWithSigV4a(Aws::Http::HttpRequest& request, const char* region, const char* serviceName, + bool signBody, long long expirationTimeInSeconds, Aws::Crt::Auth::SignatureType signatureType) const +{ + AWSCredentials credentials = m_credentialsProvider->GetAWSCredentials(); + auto crtCredentials = Aws::MakeShared<Aws::Crt::Auth::Credentials>(v4AsymmetricLogTag, + Aws::Crt::ByteCursorFromCString(credentials.GetAWSAccessKeyId().c_str()), + Aws::Crt::ByteCursorFromCString(credentials.GetAWSSecretKey().c_str()), + Aws::Crt::ByteCursorFromCString(credentials.GetSessionToken().c_str()), + credentials.GetExpiration().Seconds()); + + Aws::Crt::Auth::AwsSigningConfig awsSigningConfig; + awsSigningConfig.SetSigningAlgorithm(static_cast<Aws::Crt::Auth::SigningAlgorithm>(AWSSigningAlgorithm::ASYMMETRIC_SIGV4)); + awsSigningConfig.SetSignatureType(signatureType); + awsSigningConfig.SetRegion(region); + awsSigningConfig.SetService(serviceName); + awsSigningConfig.SetSigningTimepoint(GetSigningTimestamp().UnderlyingTimestamp()); + awsSigningConfig.SetUseDoubleUriEncode(m_urlEscapePath); + awsSigningConfig.SetShouldNormalizeUriPath(true); + awsSigningConfig.SetOmitSessionToken(false); + awsSigningConfig.SetShouldSignHeaderUserData(reinterpret_cast<void*>(const_cast<Aws::Set<Aws::String>*>(&m_unsignedHeaders))); + awsSigningConfig.SetShouldSignHeaderCallback([](const Aws::Crt::ByteCursor *name, void *user_data) { + Aws::Set<Aws::String>* unsignedHeaders = static_cast<Aws::Set<Aws::String>*>(user_data); + Aws::String headerKey(reinterpret_cast<const char*>(name->ptr), name->len); + return unsignedHeaders->find(Aws::Utils::StringUtils::ToLower(headerKey.c_str())) == unsignedHeaders->cend(); + }); + if (signatureType == Aws::Crt::Auth::SignatureType::HttpRequestViaHeaders) + { + Aws::String payloadHash(UNSIGNED_PAYLOAD); + if(signBody || request.GetUri().GetScheme() != Http::Scheme::HTTPS) + { + if (!request.GetContentBody()) + { + AWS_LOGSTREAM_DEBUG(v4AsymmetricLogTag, "Using cached empty string sha256 " << EMPTY_STRING_SHA256 << " because payload is empty."); + payloadHash = EMPTY_STRING_SHA256; + } + else + { + // The hash will be calculated from the payload during signing. + payloadHash = {}; + } + } + else + { + AWS_LOGSTREAM_DEBUG(v4AsymmetricLogTag, "Note: Http payloads are not being signed. signPayloads=" << signBody + << " http scheme=" << Http::SchemeMapper::ToString(request.GetUri().GetScheme())); + } + awsSigningConfig.SetSignedBodyValue(payloadHash.c_str()); + awsSigningConfig.SetSignedBodyHeader(m_includeSha256HashHeader ? Aws::Crt::Auth::SignedBodyHeaderType::XAmzContentSha256 : Aws::Crt::Auth::SignedBodyHeaderType::None); + } + else if (signatureType == Aws::Crt::Auth::SignatureType::HttpRequestViaQueryParams) + { + if (ServiceRequireUnsignedPayload(serviceName)) + { + awsSigningConfig.SetSignedBodyValue(UNSIGNED_PAYLOAD); + } + else + { + awsSigningConfig.SetSignedBodyValue(EMPTY_STRING_SHA256); + } + } + else + { + AWS_LOGSTREAM_ERROR(v4AsymmetricLogTag, "The signature type should be either \"HttpRequestViaHeaders\" or \"HttpRequestViaQueryParams\""); + return false; + } + awsSigningConfig.SetExpirationInSeconds(static_cast<uint64_t>(expirationTimeInSeconds)); + awsSigningConfig.SetCredentials(crtCredentials); + + std::shared_ptr<Aws::Crt::Http::HttpRequest> crtHttpRequest = request.ToCrtHttpRequest(); + + auto sigv4HttpRequestSigner = Aws::MakeShared<Aws::Crt::Auth::Sigv4HttpRequestSigner>(v4AsymmetricLogTag); + bool success = true; + sigv4HttpRequestSigner->SignRequest(crtHttpRequest, awsSigningConfig, + [&request, &success, signatureType](const std::shared_ptr<Aws::Crt::Http::HttpRequest>& signedCrtHttpRequest, int errorCode) { + success = (errorCode == AWS_ERROR_SUCCESS); + if (success) + { + if (signatureType == Aws::Crt::Auth::SignatureType::HttpRequestViaHeaders) + { + for (size_t i = 0; i < signedCrtHttpRequest->GetHeaderCount(); i++) + { + Aws::Crt::Optional<Aws::Crt::Http::HttpHeader> httpHeader = signedCrtHttpRequest->GetHeader(i); + request.SetHeaderValue(Aws::String(reinterpret_cast<const char*>(httpHeader->name.ptr), httpHeader->name.len), + Aws::String(reinterpret_cast<const char*>(httpHeader->value.ptr), httpHeader->value.len)); + } + } + else if (signatureType == Aws::Crt::Auth::SignatureType::HttpRequestViaQueryParams) + { + Aws::Http::URI newPath(reinterpret_cast<const char*>(signedCrtHttpRequest->GetPath()->ptr)); + request.GetUri().SetQueryString(newPath.GetQueryString()); + } + else + { + AWS_LOGSTREAM_ERROR(v4AsymmetricLogTag, "No action to take when signature type is neither \"HttpRequestViaHeaders\" nor \"HttpRequestViaQueryParams\""); + success = false; + } + } + else + { + AWS_LOGSTREAM_ERROR(v4AsymmetricLogTag, "Encountered internal error during signing process with AWS signature version 4 (Asymmetric):" << aws_error_str(errorCode)); + } + } + ); + return success; +} + +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 +{ + Aws::String signingRegion = region ? region : m_region; + Aws::String signingServiceName = serviceName ? serviceName : m_serviceName; + AWSCredentials credentials = m_credentialsProvider->GetAWSCredentials(); + + //don't sign anonymous requests + if (credentials.GetAWSAccessKeyId().empty() || credentials.GetAWSSecretKey().empty()) + { + return true; + } + + request.SetSigningAccessKey(credentials.GetAWSAccessKeyId()); + request.SetSigningRegion(signingRegion); + + 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 (m_signingAlgorithm == AWSSigningAlgorithm::ASYMMETRIC_SIGV4) + { + // Replace m_serviceName with signingServiceName after rebasing on S3 outposts. + return SignRequestWithSigV4a(request, signingRegion.c_str(), m_serviceName.c_str(), signBody, + 0 /* expirationTimeInSeconds doesn't matter for HttpRequestViaHeaders */, Aws::Crt::Auth::SignatureType::HttpRequestViaHeaders); + } + + if (!credentials.GetSessionToken().empty()) + { + request.SetAwsSessionToken(credentials.GetSessionToken()); + } + + if(signBody || request.GetUri().GetScheme() != Http::Scheme::HTTPS) + { + payloadHash = ComputePayloadHash(request); + if (payloadHash.empty()) + { + return false; + } + if (request.GetRequestHash().second != nullptr) + { + Aws::String checksumHeaderKey = Aws::String("x-amz-checksum-") + request.GetRequestHash().first; + Aws::String checksumHeaderValue = HashingUtils::Base64Encode(request.GetRequestHash().second->Calculate(*(request.GetContentBody())).GetResult()); + request.SetHeaderValue(checksumHeaderKey, checksumHeaderValue); + request.SetRequestHash("", nullptr); + } + } + else + { + AWS_LOGSTREAM_DEBUG(v4LogTag, "Note: Http payloads are not being signed. signPayloads=" << signBody + << " http scheme=" << Http::SchemeMapper::ToString(request.GetUri().GetScheme())); + if (request.GetRequestHash().second != nullptr) + { + payloadHash = STREAMING_UNSIGNED_PAYLOAD_TRAILER; + Aws::String trailerHeaderValue = Aws::String("x-amz-checksum-") + request.GetRequestHash().first; + request.SetHeaderValue(Http::AWS_TRAILER_HEADER, trailerHeaderValue); + request.SetTransferEncoding(CHUNKED_VALUE); + request.SetHeaderValue(Http::CONTENT_ENCODING_HEADER, Http::AWS_CHUNKED_VALUE); + request.SetHeaderValue(Http::DECODED_CONTENT_LENGTH_HEADER, request.GetHeaderValue(Http::CONTENT_LENGTH_HEADER)); + request.DeleteHeader(Http::CONTENT_LENGTH_HEADER); + } + } + + if(m_includeSha256HashHeader) + { + request.SetHeaderValue(Aws::Auth::AWSAuthHelper::X_AMZ_CONTENT_SHA256, payloadHash); + } + + //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 : Aws::Auth::AWSAuthHelper::CanonicalizeHeaders(request.GetHeaders())) + { + if(ShouldSignHeader(header.first)) + { + headersStream << header.first.c_str() << ":" << header.second.c_str() << Aws::Auth::AWSAuthHelper::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 = Aws::Auth::AWSAuthHelper::CanonicalizeRequestSigningString(request, m_urlEscapePath); + + //append v4 stuff to the canonical request string. + canonicalRequestString.append(canonicalHeadersString); + canonicalRequestString.append(Aws::Auth::AWSAuthHelper::NEWLINE); + canonicalRequestString.append(signedHeadersValue); + canonicalRequestString.append(Aws::Auth::AWSAuthHelper::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(Aws::Auth::AWSAuthHelper::SIMPLE_DATE_FORMAT_STR); + + Aws::String stringToSign = GenerateStringToSign(dateHeaderValue, simpleDate, canonicalRequestHash, signingRegion, signingServiceName); + auto finalSignature = GenerateSignature(credentials, stringToSign, simpleDate, signingRegion, signingServiceName); + + Aws::StringStream ss; + ss << Aws::Auth::AWSAuthHelper::AWS_HMAC_SHA256 << " " << Aws::Auth::AWSAuthHelper::CREDENTIAL << Aws::Auth::AWSAuthHelper::EQ << credentials.GetAWSAccessKeyId() << "/" << simpleDate + << "/" << signingRegion << "/" << signingServiceName << "/" << Aws::Auth::AWSAuthHelper::AWS4_REQUEST << ", " << Aws::Auth::AWSAuthHelper::SIGNED_HEADERS << Aws::Auth::AWSAuthHelper::EQ + << signedHeadersValue << ", " << SIGNATURE << Aws::Auth::AWSAuthHelper::EQ << finalSignature; + + auto awsAuthString = ss.str(); + AWS_LOGSTREAM_DEBUG(v4LogTag, "Signing request with: " << awsAuthString); + request.SetAwsAuthorization(awsAuthString); + 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 +{ + Aws::String signingRegion = region ? region : m_region; + Aws::String signingServiceName = serviceName ? serviceName : m_serviceName; + AWSCredentials credentials = m_credentialsProvider->GetAWSCredentials(); + + //don't sign anonymous requests + if (credentials.GetAWSAccessKeyId().empty() || credentials.GetAWSSecretKey().empty()) + { + return true; + } + + if (m_signingAlgorithm == AWSSigningAlgorithm::ASYMMETRIC_SIGV4) + { + return SignRequestWithSigV4a(request, signingRegion.c_str(), signingServiceName.c_str(), false /* signBody doesn't matter for HttpRequestViaHeaders */, + expirationTimeInSeconds, Aws::Crt::Auth::SignatureType::HttpRequestViaQueryParams); + } + + 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 : Aws::Auth::AWSAuthHelper::CanonicalizeHeaders(request.GetHeaders())) + { + if(ShouldSignHeader(header.first)) + { + headersStream << header.first.c_str() << ":" << header.second.c_str() << Aws::Auth::AWSAuthHelper::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 simpleDate = now.ToGmtString(Aws::Auth::AWSAuthHelper::SIMPLE_DATE_FORMAT_STR); + ss << credentials.GetAWSAccessKeyId() << "/" << simpleDate + << "/" << signingRegion << "/" << signingServiceName << "/" << Aws::Auth::AWSAuthHelper::AWS4_REQUEST; + + request.AddQueryStringParameter(X_AMZ_ALGORITHM, Aws::Auth::AWSAuthHelper::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 = Aws::Auth::AWSAuthHelper::CanonicalizeRequestSigningString(request, m_urlEscapePath); + + //append v4 stuff to the canonical request string. + canonicalRequestString.append(canonicalHeadersString); + canonicalRequestString.append(Aws::Auth::AWSAuthHelper::NEWLINE); + canonicalRequestString.append(signedHeadersValue); + canonicalRequestString.append(Aws::Auth::AWSAuthHelper::NEWLINE); + if (ServiceRequireUnsignedPayload(signingServiceName)) + { + 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(); + 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; + } + + //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 || "s3-object-lambda" == 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"); + 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::Auth::AWSAuthHelper::AWS_HMAC_SHA256 << Aws::Auth::AWSAuthHelper::NEWLINE << dateValue << Aws::Auth::AWSAuthHelper::NEWLINE << simpleDate << "/" << region << "/" + << serviceName << "/" << Aws::Auth::AWSAuthHelper::AWS4_REQUEST << Aws::Auth::AWSAuthHelper::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(Aws::Auth::AWSAuthHelper::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*)Aws::Auth::AWSAuthHelper::AWS4_REQUEST, strlen(Aws::Auth::AWSAuthHelper::AWS4_REQUEST)), kService); + if (!hashResult.IsSuccess()) + { + AWS_LOGSTREAM_ERROR(v4LogTag, "Unable to HMAC (SHA256) request string"); + AWS_LOGSTREAM_DEBUG(v4LogTag, "The request string is: \"" << Aws::Auth::AWSAuthHelper::AWS4_REQUEST << "\""); + return {}; + } + return hashResult.GetResult(); +} diff --git a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer/AWSNullSigner.cpp b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer/AWSNullSigner.cpp new file mode 100644 index 0000000000..d94cb421f2 --- /dev/null +++ b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/auth/signer/AWSNullSigner.cpp @@ -0,0 +1,14 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/core/auth/signer/AWSNullSigner.h> + +namespace Aws +{ + namespace Auth + { + const char NULL_SIGNER[] = "NullSigner"; + } +} |