Skip to content

Commit 98e9c3f

Browse files
Merge pull request #35 from awslabs/SigningImprovments
Changed Sigv4 Signer to cache redundant calculations. Removed user-ag…
2 parents ea62f09 + d100b7d commit 98e9c3f

File tree

4 files changed

+103
-59
lines changed

4 files changed

+103
-59
lines changed
Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
/*
2-
* Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3-
*
4-
* Licensed under the Apache License, Version 2.0 (the "License").
5-
* You may not use this file except in compliance with the License.
6-
* A copy of the License is located at
7-
*
8-
* http://aws.amazon.com/apache2.0
9-
*
10-
* or in the "license" file accompanying this file. This file is distributed
11-
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12-
* express or implied. See the License for the specific language governing
13-
* permissions and limitations under the License.
14-
*/
15-
16-
#define AWS_SDK_VERSION_STRING "1.0.22"
1+
/*
2+
* Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
#define AWS_SDK_VERSION_STRING "1.0.22"

aws-cpp-sdk-core/include/aws/core/auth/AWSAuthSigner.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <aws/core/Region.h>
2121
#include <aws/core/utils/memory/AWSMemory.h>
2222
#include <aws/core/utils/DateTime.h>
23+
#include <aws/core/utils/Array.h>
2324

2425
#include <memory>
2526
#include <atomic>
@@ -127,12 +128,20 @@ namespace Aws
127128
Aws::String GenerateSignature(const Aws::Auth::AWSCredentials& credentials, const Aws::String& stringToSign, const Aws::String& simpleDate) const;
128129
Aws::String ComputePayloadHash(Aws::Http::HttpRequest&) const;
129130
Aws::String GenerateStringToSign(const Aws::String& dateValue, const Aws::String& simpleDate, const Aws::String& canonicalRequestHash) const;
131+
const Aws::Utils::ByteBuffer& ComputeLongLivedHash(const Aws::String& secretKey, const Aws::String& simpleDate) const;
130132

131133
std::shared_ptr<Auth::AWSCredentialsProvider> m_credentialsProvider;
132134
Aws::String m_serviceName;
133135
Aws::String m_region;
134136
Aws::UniquePtr<Aws::Utils::Crypto::Sha256> m_hash;
135137
Aws::UniquePtr<Aws::Utils::Crypto::Sha256HMAC> m_HMAC;
138+
//these next four fields are ONLY for caching purposes and do not change
139+
//the logical state of the signer. They are marked mutable so the
140+
//interface can remain const.
141+
mutable Aws::Utils::ByteBuffer m_partialSignature;
142+
mutable Aws::String m_currentDateStr;
143+
mutable Aws::String m_currentSecretKey;
144+
mutable std::mutex m_partialSignatureLock;
136145
bool m_signPayloads;
137146
bool m_urlEscapePath;
138147
};

aws-cpp-sdk-core/source/auth/AWSAuthSigner.cpp

Lines changed: 74 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ static const char* X_AMZ_SIGNATURE = "X-Amz-Signature";
5555
static const char* SIGNING_KEY = "AWS4";
5656
static const char* LONG_DATE_FORMAT_STR = "%Y%m%dT%H%M%SZ";
5757
static const char* SIMPLE_DATE_FORMAT_STR = "%Y%m%d";
58+
static const char* EMPTY_STRING_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
5859

5960
static const char* v4LogTag = "AWSAuthV4Signer";
6061

@@ -131,6 +132,8 @@ AWSAuthV4Signer::AWSAuthV4Signer(const std::shared_ptr<Auth::AWSCredentialsProvi
131132
m_signPayloads(signPayloads),
132133
m_urlEscapePath(urlEscapePath)
133134
{
135+
//go ahead and warm up the signing cache.
136+
ComputeLongLivedHash(credentialsProvider->GetAWSCredentials().GetAWSSecretKey(), DateTime::CalculateGmtTimestampAsString(SIMPLE_DATE_FORMAT_STR));
134137
}
135138

136139
AWSAuthV4Signer::~AWSAuthV4Signer()
@@ -322,45 +325,9 @@ Aws::String AWSAuthV4Signer::GenerateSignature(const AWSCredentials& credentials
322325

323326
Aws::StringStream ss;
324327

325-
//now we do the complicated part of deriving a signing key.
326-
Aws::String signingKey(SIGNING_KEY);
327-
signingKey.append(credentials.GetAWSSecretKey());
328-
329-
//we use digest only for the derivation process.
330-
auto hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)simpleDate.c_str(), simpleDate.length()),
331-
ByteBuffer((unsigned char*)signingKey.c_str(), signingKey.length()));
332-
if (!hashResult.IsSuccess())
333-
{
334-
AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to hmac (sha256) date string \"" << simpleDate << "\"");
335-
return "";
336-
}
337-
338-
auto kDate = hashResult.GetResult();
339-
hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)m_region.c_str(), m_region.length()), kDate);
340-
if (!hashResult.IsSuccess())
341-
{
342-
AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to hmac (sha256) region string \"" << m_region << "\"");
343-
return "";
344-
}
345-
346-
auto kRegion = hashResult.GetResult();
347-
hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)m_serviceName.c_str(), m_serviceName.length()), kRegion);
348-
if (!hashResult.IsSuccess())
349-
{
350-
AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to hmac (sha256) service string \"" << m_serviceName << "\"");
351-
return "";
352-
}
353-
354-
auto kService = hashResult.GetResult();
355-
hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)AWS4_REQUEST, strlen(AWS4_REQUEST)), kService);
356-
if (!hashResult.IsSuccess())
357-
{
358-
AWS_LOGSTREAM_ERROR(v4LogTag, "Unable to hmac (sha256) request string \"" << AWS4_REQUEST << "\"");
359-
return "";
360-
}
361-
362-
auto kSigning = hashResult.GetResult();
363-
hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)stringToSign.c_str(), stringToSign.length()), kSigning);
328+
auto& partialSignature = ComputeLongLivedHash(credentials.GetAWSSecretKey(), simpleDate);
329+
330+
auto hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)stringToSign.c_str(), stringToSign.length()), partialSignature);
364331
if (!hashResult.IsSuccess())
365332
{
366333
AWS_LOGSTREAM_ERROR(v4LogTag, "Unable to hmac (sha256) final string \"" << stringToSign << "\"");
@@ -378,9 +345,14 @@ Aws::String AWSAuthV4Signer::GenerateSignature(const AWSCredentials& credentials
378345

379346
Aws::String AWSAuthV4Signer::ComputePayloadHash(Aws::Http::HttpRequest& request) const
380347
{
348+
if (!request.GetContentBody())
349+
{
350+
AWS_LOGSTREAM_DEBUG(v4LogTag, "Using cached empty string sha256 " << EMPTY_STRING_SHA256 << " because payload is empty.");
351+
return EMPTY_STRING_SHA256;
352+
}
353+
381354
//compute hash on payload if it exists.
382-
auto hashResult = request.GetContentBody() ? m_hash->Calculate(*request.GetContentBody())
383-
: m_hash->Calculate("");
355+
auto hashResult = m_hash->Calculate(*request.GetContentBody());
384356

385357
if(request.GetContentBody())
386358
{
@@ -411,3 +383,64 @@ Aws::String AWSAuthV4Signer::GenerateStringToSign(const Aws::String& dateValue,
411383

412384
return ss.str();
413385
}
386+
387+
const Aws::Utils::Array<unsigned char>& AWSAuthV4Signer::ComputeLongLivedHash(const Aws::String& secretKey, const Aws::String& simpleDate) const
388+
{
389+
//only compute this once and use it until either the credentials change, or the date changes.
390+
if (m_currentDateStr != simpleDate || m_currentSecretKey != secretKey)
391+
{
392+
std::lock_guard<std::mutex> locker(m_partialSignatureLock);
393+
if (m_currentDateStr != simpleDate || m_currentSecretKey != secretKey)
394+
{
395+
m_currentSecretKey = secretKey;
396+
m_currentDateStr = simpleDate;
397+
398+
//now we do the complicated part of deriving a signing key.
399+
Aws::String signingKey(SIGNING_KEY);
400+
401+
signingKey.append(m_currentSecretKey);
402+
403+
//we use digest only for the derivation process.
404+
auto hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)simpleDate.c_str(), simpleDate.length()),
405+
ByteBuffer((unsigned char*)signingKey.c_str(), signingKey.length()));
406+
407+
if (!hashResult.IsSuccess())
408+
{
409+
AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to hmac (sha256) date string \"" << simpleDate << "\"");
410+
m_partialSignature = ByteBuffer();
411+
return m_partialSignature;
412+
}
413+
414+
auto kDate = hashResult.GetResult();
415+
hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)m_region.c_str(), m_region.length()), kDate);
416+
if (!hashResult.IsSuccess())
417+
{
418+
AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to hmac (sha256) region string \"" << m_region << "\"");
419+
m_partialSignature = ByteBuffer();
420+
return m_partialSignature;
421+
}
422+
423+
auto kRegion = hashResult.GetResult();
424+
hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)m_serviceName.c_str(), m_serviceName.length()), kRegion);
425+
if (!hashResult.IsSuccess())
426+
{
427+
AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to hmac (sha256) service string \"" << m_serviceName << "\"");
428+
m_partialSignature = ByteBuffer();
429+
return m_partialSignature;
430+
}
431+
432+
auto kService = hashResult.GetResult();
433+
hashResult = m_HMAC->Calculate(ByteBuffer((unsigned char*)AWS4_REQUEST, strlen(AWS4_REQUEST)), kService);
434+
if (!hashResult.IsSuccess())
435+
{
436+
AWS_LOGSTREAM_ERROR(v4LogTag, "Unable to hmac (sha256) request string \"" << AWS4_REQUEST << "\"");
437+
m_partialSignature = ByteBuffer();
438+
return m_partialSignature;
439+
}
440+
441+
m_partialSignature = hashResult.GetResult();
442+
}
443+
}
444+
445+
return m_partialSignature;
446+
}

aws-cpp-sdk-core/source/client/AWSClient.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,14 +257,16 @@ HttpResponseOutcome AWSClient::AttemptOneRequest(const Aws::String& uri,
257257
HttpResponseOutcome AWSClient::AttemptOneRequest(const Aws::String& uri, HttpMethod method) const
258258
{
259259
std::shared_ptr<HttpRequest> httpRequest(CreateHttpRequest(uri, method, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod));
260-
AddCommonHeaders(*httpRequest);
261-
260+
262261
if (!m_signer->SignRequest(*httpRequest))
263262
{
264263
AWS_LOG_ERROR(AWS_CLIENT_LOG_TAG, "Request signing failed. Returning error.");
265264
return HttpResponseOutcome(); // TODO: make a real error when error revamp reaches branch (SIGNING_ERROR)
266265
}
267266

267+
//user agent and headers like that shouldn't be signed for the sake of compatibility with proxies which MAY mutate that header.
268+
AddCommonHeaders(*httpRequest);
269+
268270
AWS_LOG_DEBUG(AWS_CLIENT_LOG_TAG, "Request Successfully signed");
269271
std::shared_ptr<HttpResponse> httpResponse(
270272
m_httpClient->MakeRequest(*httpRequest, m_readRateLimiter.get(), m_writeRateLimiter.get()));

0 commit comments

Comments
 (0)