bool AWSAuthV4Signer::SignRequestWithCreds()

in src/aws-cpp-sdk-core/source/auth/signer/AWSAuthV4Signer.cpp [189:334]


bool AWSAuthV4Signer::SignRequestWithCreds(Aws::Http::HttpRequest& request, const AWSCredentials& credentials,
                                           const char* region, const char* serviceName, bool signBody) const
{
    Aws::String signingRegion = region ? region : m_region;
    Aws::String signingServiceName = serviceName ? serviceName : m_serviceName;

    //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);

    if (m_signingAlgorithm == AWSSigningAlgorithm::ASYMMETRIC_SIGV4)
    {
        return SignRequestWithSigV4a(request, signingRegion.c_str(), signingServiceName.c_str(), signBody,
            0 /* expirationTimeInSeconds doesn't matter for HttpRequestViaHeaders */, Aws::Crt::Auth::SignatureType::HttpRequestViaHeaders);
    }

    if (!credentials.GetSessionToken().empty())
    {
        request.SetAwsSessionToken(credentials.GetSessionToken());
    }

    // If the request checksum, set the signer to use a unsigned
    // trailing payload. otherwise use it in the header
    if (request.GetRequestHash().second != nullptr && !request.GetRequestHash().first.empty() && request.GetContentBody() != nullptr) {
      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 checksumHeaderValue = Aws::String("x-amz-checksum-") + request.GetRequestHash().first;
        request.DeleteHeader(checksumHeaderValue.c_str());
        request.SetHeaderValue(Http::AWS_TRAILER_HEADER, checksumHeaderValue);
        request.SetTransferEncoding(CHUNKED_VALUE);
        request.HasContentEncoding()
                ? request.SetContentEncoding(Aws::String{Http::AWS_CHUNKED_VALUE} + "," + request.GetContentEncoding())
                : request.SetContentEncoding(Http::AWS_CHUNKED_VALUE);

        if (request.HasHeader(Http::CONTENT_LENGTH_HEADER)) {
          request.SetHeaderValue(Http::DECODED_CONTENT_LENGTH_HEADER, request.GetHeaderValue(Http::CONTENT_LENGTH_HEADER));
          request.DeleteHeader(Http::CONTENT_LENGTH_HEADER);
        }
      }
    } else {
      payloadHash = ComputePayloadHash(request);
      if (payloadHash.empty()) {
        // this indicates a hashing error occurred, which was logged
        return false;
      }

      Aws::String checksumHeaderKey = Aws::String("x-amz-checksum-") + request.GetRequestHash().first;
      const auto headers = request.GetHeaders();
      if (request.GetRequestHash().second != nullptr && !request.HasHeader(checksumHeaderKey.c_str())) {
        Aws::String checksumHeaderValue;
        if (request.GetRequestHash().first == "sha256") {
          // we already calculated the payload hash so just reverse the hex string to
          // a ByteBuffer and Base64Encode it - otherwise we're re-hashing the content
          checksumHeaderValue = HashingUtils::Base64Encode(HashingUtils::HexDecode(payloadHash));
        } else {
          // if it is one of the other hashes, we must be careful if there is no content body
          const auto& body = request.GetContentBody();
          checksumHeaderValue = (body) ? HashingUtils::Base64Encode(request.GetRequestHash().second->Calculate(*body).GetResult())
                                       : HashingUtils::Base64Encode(request.GetRequestHash().second->Calculate({}).GetResult());
        }
        request.SetHeaderValue(checksumHeaderKey, checksumHeaderValue);
        request.SetRequestHash("", nullptr);
      }
    }

    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 sha256Digest = HashingUtils::CalculateSHA256(canonicalRequestString);
    if (sha256Digest.GetLength() == 0)
    {
        AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to hash (sha256) request string");
        AWS_LOGSTREAM_DEBUG(v4LogTag, "The request string is: \"" << canonicalRequestString << "\"");
        return false;
    }

    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;
}