AWS SDK for C++

AWS SDK for C++ Version 1.11.440

Loading...
Searching...
No Matches
SigV4aSigner.h
1
6#pragma once
7
8#include <smithy/identity/signer/AwsSignerBase.h>
9#include <smithy/identity/identity/AwsCredentialIdentityBase.h>
10#include <aws/core/auth/AWSCredentials.h>
11#include <aws/crt/auth/Credentials.h>
12
13#include <aws/core/http/HttpRequest.h>
14#include <aws/core/auth/signer/AWSAuthSignerHelper.h>
15#include <aws/crt/http/HttpConnection.h>
16#include <aws/crt/http/HttpRequestResponse.h>
17#include <condition_variable>
18#include <mutex>
19
20namespace smithy {
21 static const char* UNSIGNED_PAYLOAD = "UNSIGNED-PAYLOAD";
22 static const char* EMPTY_STRING_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
23 static const char v4AsymmetricLogTag[] = "AWSAuthSymmetricV4Signer";
24 static const char* USER_AGENT = "user-agent";
25 static const char* X_AMZN_TRACE_ID = "x-amzn-trace-id";
26
31 class AwsSigV4aSigner : public AwsSignerBase<AwsCredentialIdentityBase> {
32
33 public:
35 explicit AwsSigV4aSigner(const Aws::String& serviceName, const Aws::String& region)
36 : m_serviceName(serviceName), m_region(region)
37 {
38 }
39
40 SigningFutureOutcome sign(std::shared_ptr<HttpRequest> httpRequest, const AwsCredentialIdentityBase& identity, SigningProperties properties) override
41 {
42
43 auto signPayloadIt = properties.find("SignPayload");
44 bool signPayload = signPayloadIt != properties.end() ? signPayloadIt->second.get<Aws::String>() == "true" : false;
45
46 assert(httpRequest);
47 assert(identity.expiration().has_value());
48
49 auto &request = *httpRequest;
50
51 auto crtCredentials = Aws::MakeShared<Aws::Crt::Auth::Credentials>(v4AsymmetricLogTag,
52 Aws::Crt::ByteCursorFromCString(identity.accessKeyId().c_str()),
53 Aws::Crt::ByteCursorFromCString(identity.secretAccessKey().c_str()),
54 Aws::Crt::ByteCursorFromCString((*identity.sessionToken()).c_str()),
55 (*identity.expiration()).Seconds());
56
57 Aws::Crt::Auth::AwsSigningConfig awsSigningConfig;
58
59 bool success = createAwsSigningConfig(crtCredentials, request, awsSigningConfig, signPayload);
60
61 if(!success)
62 {
63 AWS_LOGSTREAM_ERROR(v4AsymmetricLogTag, "Failed to get Auth configuration");
64
65 return SigningError(Aws::Client::CoreErrors::MEMORY_ALLOCATION, "", "Failed to get Auth configuration", false);
66 }
67
68 std::shared_ptr<Aws::Crt::Http::HttpRequest> crtHttpRequest = request.ToCrtHttpRequest();
69
70 auto sigv4HttpRequestSigner = Aws::MakeShared<Aws::Crt::Auth::Sigv4HttpRequestSigner>(v4AsymmetricLogTag);
71 //This is an async call, so we need to wait till we have received an outcome
72 Aws::String errorMessage;
73 bool processed = false;
74 //producer function
75 sigv4HttpRequestSigner->SignRequest(crtHttpRequest, awsSigningConfig,
76 [&request, &success, &errorMessage, &processed, this](const std::shared_ptr<Aws::Crt::Http::HttpRequest>& signedCrtHttpRequest, int errorCode) {
77 std::unique_lock<std::mutex> lock(m_mutex);
78 m_cv.wait(lock, [&]{ return !processed; });
79 success = (errorCode == AWS_ERROR_SUCCESS);
80 if (success)
81 {
82 if (m_signatureType == Aws::Crt::Auth::SignatureType::HttpRequestViaHeaders)
83 {
84 for (size_t i = 0; i < signedCrtHttpRequest->GetHeaderCount(); i++)
85 {
86 Aws::Crt::Optional<Aws::Crt::Http::HttpHeader> httpHeader = signedCrtHttpRequest->GetHeader(i);
87 request.SetHeaderValue(Aws::String(reinterpret_cast<const char*>(httpHeader->name.ptr), httpHeader->name.len),
88 Aws::String(reinterpret_cast<const char*>(httpHeader->value.ptr), httpHeader->value.len));
89 }
90 }
91 else if (m_signatureType == Aws::Crt::Auth::SignatureType::HttpRequestViaQueryParams)
92 {
93 Aws::Http::URI newPath(reinterpret_cast<const char*>(signedCrtHttpRequest->GetPath()->ptr));
94 request.GetUri().SetQueryString(newPath.GetQueryString());
95 }
96 else
97 {
98 errorMessage = "No action to take when signature type is neither \"HttpRequestViaHeaders\" nor \"HttpRequestViaQueryParams\"";
99 AWS_LOGSTREAM_ERROR(v4AsymmetricLogTag, errorMessage);
100 success = false;
101 }
102 }
103 else
104 {
105 Aws::OStringStream errStream;
106 errStream << "Encountered internal error during signing process with AWS signature version 4 (Asymmetric):" << aws_error_str(errorCode);
107 errorMessage = errStream.str();
108 AWS_LOGSTREAM_ERROR(v4AsymmetricLogTag, errorMessage);
109 }
110
111 processed = true;
112 m_cv.notify_all();
113 }
114 );
115
116 //consumer
117 {
118 std::unique_lock<std::mutex> lock(m_mutex);
119 m_cv.wait(lock, [&]{ return processed; });
120
121 }
122
123 return success? SigningFutureOutcome(std::move(httpRequest)) : SigningError(Aws::Client::CoreErrors::MEMORY_ALLOCATION, "", "Failed to sign the request with sigv4", false);
124 }
125
126
127
128 virtual ~AwsSigV4aSigner() {};
129 protected:
130
132 std::shared_ptr<Aws::Crt::Auth::Credentials>& crtCredentials,
133 const Aws::Http::HttpRequest& request,
134 Aws::Crt::Auth::AwsSigningConfig& awsSigningConfig,
135 bool signBody) const
136 {
137 awsSigningConfig.SetSigningAlgorithm(static_cast<Aws::Crt::Auth::SigningAlgorithm>(Aws::Auth::AWSSigningAlgorithm::ASYMMETRIC_SIGV4));
138 awsSigningConfig.SetSignatureType(m_signatureType);
139 awsSigningConfig.SetRegion(m_region.c_str());
140 awsSigningConfig.SetService(m_region.c_str());
141 awsSigningConfig.SetSigningTimepoint(GetSigningTimestamp().UnderlyingTimestamp());
142 awsSigningConfig.SetUseDoubleUriEncode(m_urlEscape);
143 awsSigningConfig.SetShouldNormalizeUriPath(true);
144 awsSigningConfig.SetOmitSessionToken(false);
145 awsSigningConfig.SetShouldSignHeaderUserData(reinterpret_cast<void*>(const_cast<Aws::Set<Aws::String>*>(&m_unsignedHeaders)));
146 awsSigningConfig.SetShouldSignHeaderCallback([](const Aws::Crt::ByteCursor *name, void *user_data) {
147 Aws::Set<Aws::String>* unsignedHeaders = static_cast<Aws::Set<Aws::String>*>(user_data);
148 Aws::String headerKey(reinterpret_cast<const char*>(name->ptr), name->len);
149 return unsignedHeaders->find(Aws::Utils::StringUtils::ToLower(headerKey.c_str())) == unsignedHeaders->cend();
150 });
151 if (m_signatureType == Aws::Crt::Auth::SignatureType::HttpRequestViaHeaders)
152 {
153 Aws::String payloadHash(UNSIGNED_PAYLOAD);
154 if(signBody || request.GetUri().GetScheme() != Aws::Http::Scheme::HTTPS)
155 {
156 if (!request.GetContentBody())
157 {
158 AWS_LOGSTREAM_DEBUG(v4AsymmetricLogTag, "Using cached empty string sha256 " << EMPTY_STRING_SHA256 << " because payload is empty.");
159 payloadHash = EMPTY_STRING_SHA256;
160 }
161 else
162 {
163 // The hash will be calculated from the payload during signing.
164 payloadHash = {};
165 }
166 }
167 else
168 {
169 AWS_LOGSTREAM_DEBUG(v4AsymmetricLogTag, "Note: Http payloads are not being signed. signPayloads=" << signBody
170 << " http scheme=" << Aws::Http::SchemeMapper::ToString(request.GetUri().GetScheme()));
171 }
172 awsSigningConfig.SetSignedBodyValue(payloadHash.c_str());
173 awsSigningConfig.SetSignedBodyHeader(m_includeSha256HashHeader ? Aws::Crt::Auth::SignedBodyHeaderType::XAmzContentSha256 : Aws::Crt::Auth::SignedBodyHeaderType::None);
174 }
175 else if (m_signatureType == Aws::Crt::Auth::SignatureType::HttpRequestViaQueryParams)
176 {
178 {
179 awsSigningConfig.SetSignedBodyValue(UNSIGNED_PAYLOAD);
180 }
181 else
182 {
183 awsSigningConfig.SetSignedBodyValue(EMPTY_STRING_SHA256);
184 }
185 }
186 else
187 {
188 AWS_LOGSTREAM_ERROR(v4AsymmetricLogTag, "The signature type should be either \"HttpRequestViaHeaders\" or \"HttpRequestViaQueryParams\"");
189 return false;
190 }
191 awsSigningConfig.SetExpirationInSeconds(static_cast<uint64_t>(m_expirationTimeInSeconds));
192 awsSigningConfig.SetCredentials(crtCredentials);
193 return true;
194 }
195
196
197 bool ServiceRequireUnsignedPayload(const Aws::String& serviceName) const
198 {
199 // S3 uses a magic string (instead of the empty string) for its body hash for presigned URLs as outlined here:
200 // https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
201 // this is true for PUT, POST, GET, DELETE and HEAD operations.
202 // However, other services (for example RDS) implement the specification as outlined here:
203 // https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
204 // which states that body-less requests should use the empty-string SHA256 hash.
205 return "s3" == serviceName || "s3-object-lambda" == serviceName;
206 }
207
210 //params that can be exposed later
213 const bool m_urlEscape{true};
215 const Aws::Crt::Auth::SignatureType m_signatureType{Aws::Crt::Auth::SignatureType::HttpRequestViaQueryParams};
216 std::condition_variable m_cv;
217 std::mutex m_mutex;
218 };
219}
virtual const std::shared_ptr< Aws::IOStream > & GetContentBody() const =0
const Aws::String & GetQueryString() const
Definition URI.h:160
Scheme GetScheme() const
Definition URI.h:66
static Aws::String ToLower(const char *source)
virtual Aws::String accessKeyId() const =0
virtual Aws::Crt::Optional< AwsIdentity::DateTime > expiration() const override=0
virtual Aws::String secretAccessKey() const =0
virtual Aws::Crt::Optional< Aws::String > sessionToken() const =0
std::condition_variable m_cv
const Aws::Crt::Auth::SignatureType m_signatureType
bool ServiceRequireUnsignedPayload(const Aws::String &serviceName) const
AwsSigV4aSigner(const Aws::String &serviceName, const Aws::String &region)
const Aws::Set< Aws::String > m_unsignedHeaders
long long m_expirationTimeInSeconds
bool createAwsSigningConfig(std::shared_ptr< Aws::Crt::Auth::Credentials > &crtCredentials, const Aws::Http::HttpRequest &request, Aws::Crt::Auth::AwsSigningConfig &awsSigningConfig, bool signBody) const
const bool m_includeSha256HashHeader
SigningFutureOutcome sign(std::shared_ptr< HttpRequest > httpRequest, const AwsCredentialIdentityBase &identity, SigningProperties properties) override
Aws::Client::AWSError< Aws::Client::CoreErrors > SigningError
Aws::Utils::FutureOutcome< std::shared_ptr< HttpRequest >, SigningError > SigningFutureOutcome
Aws::UnorderedMap< Aws::String, Aws::Crt::Variant< Aws::String, bool > > SigningProperties
virtual Aws::Utils::DateTime GetSigningTimestamp() const
AWS_CORE_API const char * ToString(Scheme scheme)
std::basic_string< char, std::char_traits< char >, Aws::Allocator< char > > String
Definition AWSString.h:97
std::set< T, std::less< T >, Aws::Allocator< T > > Set
Definition AWSSet.h:18
std::basic_ostringstream< char, std::char_traits< char >, Aws::Allocator< char > > OStringStream
static const char v4AsymmetricLogTag[]
static const char * UNSIGNED_PAYLOAD
static const char * EMPTY_STRING_SHA256
static const char * X_AMZN_TRACE_ID
static const char * USER_AGENT