492 if (secretKey.empty()) {
505 auto canonicalQueryString = CanonicalizeQueryString(url);
509 if (std::find_if(headers.begin(), headers.end(),
510 [](
const auto &pair) { return pair.first ==
"Host"; }) == headers.end()) {
513 err_msg =
"Unable to extract hostname from URL: " + url;
516 headers.emplace_back(
"Host", host);
520 auto iter = std::find_if(headers.begin(), headers.end(),
521 [](
const auto &pair) { return !strcasecmp(pair.first.c_str(),
"X-Amz-Date"); });
522 std::string date_time;
523 char date_char[] =
"YYYYMMDD";
524 if (iter == headers.end()) {
527 struct tm brokenDownTime;
528 gmtime_r(&now, &brokenDownTime);
530 date_time =
"YYYYMMDDThhmmssZ";
531 strftime(date_time.data(), date_time.size(),
"%Y%m%dT%H%M%SZ", &brokenDownTime);
532 headers.emplace_back(
"X-Amz-Date", date_time);
533 strftime(date_char,
sizeof(date_char),
"%Y%m%d", &brokenDownTime);
535 date_time = iter->second;
536 auto loc = date_time.find(
'T', 0);
538 err_msg =
"Invalid value for X-Amz-Date";
541 memcpy(date_char, date_time.c_str(), 8);
547 std::string payload_hash =
"UNSIGNED-PAYLOAD";
548 iter = std::find_if(headers.begin(), headers.end(),
549 [](
const auto &pair) { return !strcasecmp(pair.first.c_str(),
"X-Amz-Content-Sha256"); });
550 if (iter == headers.end()) {
551 headers.emplace_back(
"X-Amz-Content-Sha256", payload_hash);
553 payload_hash = iter->second;
559 std::vector<std::pair<std::string, std::string>> transformed_headers;
560 transformed_headers.reserve(headers.size());
561 for (
const auto &info : headers) {
562 std::string header = info.first;
563 std::transform(header.begin(), header.end(), header.begin(), &tolower);
565 std::string value = info.second;
569 auto value_trimmed = std::string(
TrimView(value));
574 bool inSpaces =
false;
575 while (right < value_trimmed.length()) {
577 if (value_trimmed[right] ==
' ') {
585 if (value_trimmed[right] ==
' ') {
589 value_trimmed.erase(left, right - left - 1);
595 transformed_headers.emplace_back(header, value);
597 std::sort(transformed_headers.begin(), transformed_headers.end(),
598 [](
const auto &a,
const auto &b) { return a.first < b.first; });
602 std::string signedHeaders, canonicalHeaders;
603 for (
const auto &info : transformed_headers) {
604 canonicalHeaders += info.first +
":" + info.second +
"\n";
605 signedHeaders += info.first +
";";
607 signedHeaders.erase(signedHeaders.end() - 1);
610 auto canonicalRequest =
611 verb +
"\n" + canonicalURI +
"\n" + canonicalQueryString +
"\n" +
612 canonicalHeaders +
"\n" + signedHeaders +
"\n" + payload_hash;
619 std::string canonicalRequestHash;
620 std::vector<unsigned char> messageDigest;
621 messageDigest.resize(EVP_MAX_MD_SIZE);
622 if (!ComputeSHA256(canonicalRequest, messageDigest)) {
623 err_msg =
"Unable to hash canonical request.";
626 MessageDigestAsHex(messageDigest, canonicalRequestHash);
629 auto credentialScope = std::string(date_char) +
"/" + m_region +
"/" + m_service +
"/aws4_request";
630 auto stringToSign = std::string(
"AWS4-HMAC-SHA256\n") + date_time +
"\n" + credentialScope +
"\n" + canonicalRequestHash;
638 auto saKey = std::string(
"AWS4") + secretKey;
639 unsigned int mdLength = 0;
640 const unsigned char *hmac =
641 HMAC(EVP_sha256(), saKey.c_str(), saKey.length(), (
unsigned char *)date_char,
642 sizeof(date_char) - 1, messageDigest.data(), &mdLength);
644 err_msg =
"Unable to calculate HMAC for date.";
648 unsigned int md2Length = 0;
649 unsigned char messageDigest2[EVP_MAX_MD_SIZE];
650 hmac = HMAC(EVP_sha256(), messageDigest.data(), mdLength,
651 reinterpret_cast<unsigned char *
>(m_region.data()), m_region.size(), messageDigest2,
654 err_msg =
"Unable to calculate HMAC for region.";
658 hmac = HMAC(EVP_sha256(), messageDigest2, md2Length,
659 reinterpret_cast<unsigned char *
>(m_service.data()), m_service.size(), messageDigest.data(),
662 err_msg =
"Unable to calculate HMAC for service.";
666 const char request_char[] =
"aws4_request";
667 hmac = HMAC(EVP_sha256(), messageDigest.data(), messageDigest.size(),
reinterpret_cast<const unsigned char *
>(request_char),
668 sizeof(request_char) - 1, messageDigest2, &md2Length);
670 err_msg =
"Unable to calculate HMAC for request.";
674 hmac = HMAC(EVP_sha256(), messageDigest2, md2Length,
675 reinterpret_cast<unsigned char *
>(stringToSign.data()),
676 stringToSign.size(), messageDigest.data(), &mdLength);
678 err_msg =
"Unable to calculate HMAC for request string.";
682 std::string signature;
683 MessageDigestAsHex(messageDigest, signature);
686 std::string(
"AWS4-HMAC-SHA256 Credential=") + keyId +
"/" + credentialScope +
687 ",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)