494 if (secretKey.empty()) {
507 auto canonicalQueryString = CanonicalizeQueryString(url);
511 if (std::find_if(headers.begin(), headers.end(),
512 [](
const auto &pair) { return pair.first ==
"Host"; }) == headers.end()) {
515 err_msg =
"Unable to extract hostname from URL: " + url;
518 headers.emplace_back(
"Host", host);
522 auto iter = std::find_if(headers.begin(), headers.end(),
523 [](
const auto &pair) { return !strcasecmp(pair.first.c_str(),
"X-Amz-Date"); });
524 std::string date_time;
525 char date_char[] =
"YYYYMMDD";
526 if (iter == headers.end()) {
529 struct tm brokenDownTime;
530 gmtime_r(&now, &brokenDownTime);
532 date_time =
"YYYYMMDDThhmmssZ";
533 strftime(date_time.data(), date_time.size(),
"%Y%m%dT%H%M%SZ", &brokenDownTime);
534 headers.emplace_back(
"X-Amz-Date", date_time);
535 strftime(date_char,
sizeof(date_char),
"%Y%m%d", &brokenDownTime);
537 date_time = iter->second;
538 auto loc = date_time.find(
'T', 0);
540 err_msg =
"Invalid value for X-Amz-Date";
543 memcpy(date_char, date_time.c_str(), 8);
549 std::string payload_hash =
"UNSIGNED-PAYLOAD";
550 iter = std::find_if(headers.begin(), headers.end(),
551 [](
const auto &pair) { return !strcasecmp(pair.first.c_str(),
"X-Amz-Content-Sha256"); });
552 if (iter == headers.end()) {
553 headers.emplace_back(
"X-Amz-Content-Sha256", payload_hash);
555 payload_hash = iter->second;
561 std::vector<std::pair<std::string, std::string>> transformed_headers;
562 transformed_headers.reserve(headers.size());
563 for (
const auto &info : headers) {
564 std::string header = info.first;
565 std::transform(header.begin(), header.end(), header.begin(), &tolower);
567 std::string value = info.second;
571 auto value_trimmed = std::string(
TrimView(value));
576 bool inSpaces =
false;
577 while (right < value_trimmed.length()) {
579 if (value_trimmed[right] ==
' ') {
587 if (value_trimmed[right] ==
' ') {
591 value_trimmed.erase(left, right - left - 1);
597 transformed_headers.emplace_back(header, value);
599 std::sort(transformed_headers.begin(), transformed_headers.end(),
600 [](
const auto &a,
const auto &b) { return a.first < b.first; });
604 std::string signedHeaders, canonicalHeaders;
605 for (
const auto &info : transformed_headers) {
606 canonicalHeaders += info.first +
":" + info.second +
"\n";
607 signedHeaders += info.first +
";";
609 signedHeaders.erase(signedHeaders.end() - 1);
612 auto canonicalRequest =
613 verb +
"\n" + canonicalURI +
"\n" + canonicalQueryString +
"\n" +
614 canonicalHeaders +
"\n" + signedHeaders +
"\n" + payload_hash;
621 std::string canonicalRequestHash;
622 std::vector<unsigned char> messageDigest;
623 messageDigest.resize(EVP_MAX_MD_SIZE);
624 if (!ComputeSHA256(canonicalRequest, messageDigest)) {
625 err_msg =
"Unable to hash canonical request.";
628 MessageDigestAsHex(messageDigest, canonicalRequestHash);
631 auto credentialScope = std::string(date_char) +
"/" + m_region +
"/" + m_service +
"/aws4_request";
632 auto stringToSign = std::string(
"AWS4-HMAC-SHA256\n") + date_time +
"\n" + credentialScope +
"\n" + canonicalRequestHash;
640 auto saKey = std::string(
"AWS4") + secretKey;
641 unsigned int mdLength = 0;
642 const unsigned char *hmac =
643 HMAC(EVP_sha256(), saKey.c_str(), saKey.length(), (
unsigned char *)date_char,
644 sizeof(date_char) - 1, messageDigest.data(), &mdLength);
646 err_msg =
"Unable to calculate HMAC for date.";
650 unsigned int md2Length = 0;
651 unsigned char messageDigest2[EVP_MAX_MD_SIZE];
652 hmac = HMAC(EVP_sha256(), messageDigest.data(), mdLength,
653 reinterpret_cast<unsigned char *
>(m_region.data()), m_region.size(), messageDigest2,
656 err_msg =
"Unable to calculate HMAC for region.";
660 hmac = HMAC(EVP_sha256(), messageDigest2, md2Length,
661 reinterpret_cast<unsigned char *
>(m_service.data()), m_service.size(), messageDigest.data(),
664 err_msg =
"Unable to calculate HMAC for service.";
668 const char request_char[] =
"aws4_request";
669 hmac = HMAC(EVP_sha256(), messageDigest.data(), messageDigest.size(),
reinterpret_cast<const unsigned char *
>(request_char),
670 sizeof(request_char) - 1, messageDigest2, &md2Length);
672 err_msg =
"Unable to calculate HMAC for request.";
676 hmac = HMAC(EVP_sha256(), messageDigest2, md2Length,
677 reinterpret_cast<unsigned char *
>(stringToSign.data()),
678 stringToSign.size(), messageDigest.data(), &mdLength);
680 err_msg =
"Unable to calculate HMAC for request string.";
684 std::string signature;
685 MessageDigestAsHex(messageDigest, signature);
688 std::string(
"AWS4-HMAC-SHA256 Credential=") + keyId +
"/" + credentialScope +
689 ",SignedHeaders=" + signedHeaders +
",Signature=" + signature;
static std::string PathEncode(const std::string_view url)
static std::tuple< std::string, std::string, bool > GetCredentialsForBucket(const std::string &bucket, std::string &err_msg)
static std::string_view TrimView(const std::string_view str)
static std::string GetBucketFromHttpsUrl(const std::string &url)