aws_request_signer.cc
Go to the documentation of this file.
1 //
2 // Copyright 2020 gRPC authors.
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 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
17 
19 
20 #include <algorithm>
21 #include <utility>
22 #include <vector>
23 
24 #include <openssl/crypto.h>
25 #include <openssl/evp.h>
26 #include <openssl/hmac.h>
27 #include <openssl/sha.h>
28 
29 #include "absl/status/statusor.h"
30 #include "absl/strings/ascii.h"
31 #include "absl/strings/escaping.h"
32 #include "absl/strings/str_cat.h"
33 #include "absl/strings/str_format.h"
34 #include "absl/strings/str_join.h"
35 #include "absl/strings/str_split.h"
36 #include "absl/strings/string_view.h"
37 #include "absl/time/clock.h"
38 #include "absl/time/time.h"
39 
40 namespace grpc_core {
41 
42 namespace {
43 
44 const char kAlgorithm[] = "AWS4-HMAC-SHA256";
45 const char kDateFormat[] = "%a, %d %b %E4Y %H:%M:%S %Z";
46 const char kXAmzDateFormat[] = "%Y%m%dT%H%M%SZ";
47 
48 void SHA256(const std::string& str, unsigned char out[SHA256_DIGEST_LENGTH]) {
51  SHA256_Update(&sha256, str.c_str(), str.size());
53 }
54 
55 std::string SHA256Hex(const std::string& str) {
56  unsigned char hash[SHA256_DIGEST_LENGTH];
57  SHA256(str, hash);
58  std::string hash_str(reinterpret_cast<char const*>(hash),
60  return absl::BytesToHexString(hash_str);
61 }
62 
64  unsigned int len;
65  unsigned char digest[EVP_MAX_MD_SIZE];
66  HMAC(EVP_sha256(), key.c_str(), key.length(),
67  reinterpret_cast<const unsigned char*>(msg.c_str()), msg.length(),
68  digest, &len);
69  return std::string(digest, digest + len);
70 }
71 
72 } // namespace
73 
75  std::string access_key_id, std::string secret_access_key, std::string token,
77  std::string request_payload,
78  std::map<std::string, std::string> additional_headers,
80  : access_key_id_(std::move(access_key_id)),
81  secret_access_key_(std::move(secret_access_key)),
82  token_(std::move(token)),
83  method_(std::move(method)),
84  region_(std::move(region)),
85  request_payload_(std::move(request_payload)),
86  additional_headers_(std::move(additional_headers)) {
87  auto amz_date_it = additional_headers_.find("x-amz-date");
88  auto date_it = additional_headers_.find("date");
89  if (amz_date_it != additional_headers_.end() &&
90  date_it != additional_headers_.end()) {
92  "Only one of {date, x-amz-date} can be specified, not both.");
93  return;
94  }
95  if (amz_date_it != additional_headers_.end()) {
96  static_request_date_ = amz_date_it->second;
97  } else if (date_it != additional_headers_.end()) {
98  absl::Time request_date;
99  std::string err_str;
100  if (!absl::ParseTime(kDateFormat, date_it->second, &request_date,
101  &err_str)) {
102  *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(err_str.c_str());
103  return;
104  }
106  absl::FormatTime(kXAmzDateFormat, request_date, absl::UTCTimeZone());
107  }
109  if (!tmp_url.ok()) {
110  *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Invalid Aws request url.");
111  return;
112  }
113  url_ = tmp_url.value();
114 }
115 
116 std::map<std::string, std::string> AwsRequestSigner::GetSignedRequestHeaders() {
117  std::string request_date_full;
118  if (!static_request_date_.empty()) {
119  if (!request_headers_.empty()) {
120  return request_headers_;
121  }
122  request_date_full = static_request_date_;
123  } else {
124  absl::Time request_date = absl::Now();
125  request_date_full =
126  absl::FormatTime(kXAmzDateFormat, request_date, absl::UTCTimeZone());
127  }
128  std::string request_date_short = request_date_full.substr(0, 8);
129  // TASK 1: Create a canonical request for Signature Version 4
130  // https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
131  std::vector<absl::string_view> canonical_request_vector;
132  // 1. HTTPRequestMethod
133  canonical_request_vector.emplace_back(method_);
134  canonical_request_vector.emplace_back("\n");
135  // 2. CanonicalURI
136  canonical_request_vector.emplace_back(
137  url_.path().empty() ? "/" : absl::string_view(url_.path()));
138  canonical_request_vector.emplace_back("\n");
139  // 3. CanonicalQueryString
140  std::vector<std::string> query_vector;
141  for (const URI::QueryParam& query_kv : url_.query_parameter_pairs()) {
142  query_vector.emplace_back(absl::StrCat(query_kv.key, "=", query_kv.value));
143  }
144  std::string query = absl::StrJoin(query_vector, "&");
145  canonical_request_vector.emplace_back(query);
146  canonical_request_vector.emplace_back("\n");
147  // 4. CanonicalHeaders
148  if (request_headers_.empty()) {
149  request_headers_.insert({"host", url_.authority()});
150  if (!token_.empty()) {
151  request_headers_.insert({"x-amz-security-token", token_});
152  }
153  for (const auto& header : additional_headers_) {
154  request_headers_.insert(
155  {absl::AsciiStrToLower(header.first), header.second});
156  }
157  }
158  if (additional_headers_.find("date") == additional_headers_.end()) {
159  request_headers_["x-amz-date"] = request_date_full;
160  }
161  std::vector<absl::string_view> canonical_headers_vector;
162  for (const auto& header : request_headers_) {
163  canonical_headers_vector.emplace_back(header.first);
164  canonical_headers_vector.emplace_back(":");
165  canonical_headers_vector.emplace_back(header.second);
166  canonical_headers_vector.emplace_back("\n");
167  }
168  std::string canonical_headers = absl::StrJoin(canonical_headers_vector, "");
169  canonical_request_vector.emplace_back(canonical_headers);
170  canonical_request_vector.emplace_back("\n");
171  // 5. SignedHeaders
172  std::vector<absl::string_view> signed_headers_vector;
173  for (const auto& header : request_headers_) {
174  signed_headers_vector.emplace_back(header.first);
175  }
176  std::string signed_headers = absl::StrJoin(signed_headers_vector, ";");
177  canonical_request_vector.emplace_back(signed_headers);
178  canonical_request_vector.emplace_back("\n");
179  // 6. RequestPayload
180  std::string hashed_request_payload = SHA256Hex(request_payload_);
181  canonical_request_vector.emplace_back(hashed_request_payload);
182  std::string canonical_request = absl::StrJoin(canonical_request_vector, "");
183  // TASK 2: Create a string to sign for Signature Version 4
184  // https://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
185  std::vector<absl::string_view> string_to_sign_vector;
186  // 1. Algorithm
187  string_to_sign_vector.emplace_back("AWS4-HMAC-SHA256");
188  string_to_sign_vector.emplace_back("\n");
189  // 2. RequestDateTime
190  string_to_sign_vector.emplace_back(request_date_full);
191  string_to_sign_vector.emplace_back("\n");
192  // 3. CredentialScope
193  std::pair<absl::string_view, absl::string_view> host_parts =
195  std::string service_name(host_parts.first);
196  std::string credential_scope = absl::StrFormat(
197  "%s/%s/%s/aws4_request", request_date_short, region_, service_name);
198  string_to_sign_vector.emplace_back(credential_scope);
199  string_to_sign_vector.emplace_back("\n");
200  // 4. HashedCanonicalRequest
201  std::string hashed_canonical_request = SHA256Hex(canonical_request);
202  string_to_sign_vector.emplace_back(hashed_canonical_request);
203  std::string string_to_sign = absl::StrJoin(string_to_sign_vector, "");
204  // TASK 3: Task 3: Calculate the signature for AWS Signature Version 4
205  // https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
206  // 1. Derive your signing key.
207  std::string date = HMAC("AWS4" + secret_access_key_, request_date_short);
208  std::string region = HMAC(date, region_);
209  std::string service = HMAC(region, service_name);
210  std::string signing = HMAC(service, "aws4_request");
211  // 2. Calculate the signature.
212  std::string signature_str = HMAC(signing, string_to_sign);
213  std::string signature = absl::BytesToHexString(signature_str);
214  // TASK 4: Add the signature to the HTTP request
215  // https://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html
216  std::string authorization_header = absl::StrFormat(
217  "%s Credential=%s/%s, SignedHeaders=%s, Signature=%s", kAlgorithm,
218  access_key_id_, credential_scope, signed_headers, signature);
219  request_headers_["Authorization"] = authorization_header;
220  return request_headers_;
221 }
222 
223 } // namespace grpc_core
absl::StrSplit
strings_internal::Splitter< typename strings_internal::SelectDelimiter< Delimiter >::type, AllowEmpty, absl::string_view > StrSplit(strings_internal::ConvertibleToStringView text, Delimiter d)
Definition: abseil-cpp/absl/strings/str_split.h:499
xds_interop_client.str
str
Definition: xds_interop_client.py:487
absl::MaxSplits
strings_internal::MaxSplitsImpl< typename strings_internal::SelectDelimiter< Delimiter >::type > MaxSplits(Delimiter delimiter, int limit)
Definition: abseil-cpp/absl/strings/str_split.h:294
gen_build_yaml.out
dictionary out
Definition: src/benchmark/gen_build_yaml.py:24
const
#define const
Definition: bloaty/third_party/zlib/zconf.h:230
absl::StrCat
std::string StrCat(const AlphaNum &a, const AlphaNum &b)
Definition: abseil-cpp/absl/strings/str_cat.cc:98
absl::StrFormat
ABSL_MUST_USE_RESULT std::string StrFormat(const FormatSpec< Args... > &format, const Args &... args)
Definition: abseil-cpp/absl/strings/str_format.h:338
absl::Time
Definition: third_party/abseil-cpp/absl/time/time.h:642
evp.h
grpc_core
Definition: call_metric_recorder.h:31
absl::string_view
Definition: abseil-cpp/absl/strings/string_view.h:167
testing::internal::string
::std::string string
Definition: bloaty/third_party/protobuf/third_party/googletest/googletest/include/gtest/internal/gtest-port.h:881
error
grpc_error_handle error
Definition: retry_filter.cc:499
grpc_core::AwsRequestSigner::request_headers_
std::map< std::string, std::string > request_headers_
Definition: aws_request_signer.h:67
absl::UTCTimeZone
TimeZone UTCTimeZone()
Definition: third_party/abseil-cpp/absl/time/time.h:1099
grpc_core::AwsRequestSigner::token_
std::string token_
Definition: aws_request_signer.h:59
grpc_core::URI::Parse
static absl::StatusOr< URI > Parse(absl::string_view uri_text)
Definition: uri_parser.cc:209
EVP_sha256
const OPENSSL_EXPORT EVP_MD * EVP_sha256(void)
absl::AsciiStrToLower
void AsciiStrToLower(std::string *s)
Definition: abseil-cpp/absl/strings/ascii.cc:158
aws_request_signer.h
absl::FormatTime
std::string FormatTime(absl::string_view format, absl::Time t, absl::TimeZone tz)
Definition: abseil-cpp/absl/time/format.cc:74
grpc_core::URI::path
const std::string & path() const
Definition: uri_parser.h:70
hash
uint64_t hash
Definition: ring_hash.cc:284
grpc_core::URI::QueryParam
Definition: uri_parser.h:33
setup.url
url
Definition: setup.py:547
query
Definition: ares_private.h:198
absl::BytesToHexString
std::string BytesToHexString(absl::string_view from)
Definition: abseil-cpp/absl/strings/escaping.cc:940
absl::move
constexpr absl::remove_reference_t< T > && move(T &&t) noexcept
Definition: abseil-cpp/absl/utility/utility.h:221
absl::StrJoin
std::string StrJoin(Iterator start, Iterator end, absl::string_view sep, Formatter &&fmt)
Definition: abseil-cpp/absl/strings/str_join.h:239
grpc_core::URI::authority
const std::string & authority() const
Definition: uri_parser.h:69
sha.h
grpc_core::AwsRequestSigner::region_
std::string region_
Definition: aws_request_signer.h:62
absl::ParseTime
bool ParseTime(absl::string_view format, absl::string_view input, absl::Time *time, std::string *err)
Definition: abseil-cpp/absl/time/format.cc:91
header
struct absl::base_internal::@2940::AllocList::Header header
crypto.h
grpc_core::AwsRequestSigner::url_
URI url_
Definition: aws_request_signer.h:61
sha256
static const MD sha256
Definition: digest_test.cc:53
grpc_core::URI::query_parameter_pairs
const std::vector< QueryParam > & query_parameter_pairs() const
Definition: uri_parser.h:81
grpc_core::AwsRequestSigner::AwsRequestSigner
AwsRequestSigner(std::string access_key_id, std::string secret_access_key, std::string token, std::string method, std::string url, std::string region, std::string request_payload, std::map< std::string, std::string > additional_headers, grpc_error_handle *error)
Definition: aws_request_signer.cc:74
msg
std::string msg
Definition: client_interceptors_end2end_test.cc:372
GRPC_ERROR_CREATE_FROM_STATIC_STRING
#define GRPC_ERROR_CREATE_FROM_STATIC_STRING(desc)
Definition: error.h:291
grpc_core::AwsRequestSigner::static_request_date_
std::string static_request_date_
Definition: aws_request_signer.h:66
grpc_core::AwsRequestSigner::method_
std::string method_
Definition: aws_request_signer.h:60
SHA256
#define SHA256
Definition: boringssl_prefix_symbols.h:2154
absl::Now
ABSL_NAMESPACE_BEGIN Time Now()
Definition: abseil-cpp/absl/time/clock.cc:39
absl::StatusOr::ok
ABSL_MUST_USE_RESULT bool ok() const
Definition: abseil-cpp/absl/status/statusor.h:491
sha256_state_st
Definition: sha.h:189
key
const char * key
Definition: hpack_parser_table.cc:164
grpc_core::AwsRequestSigner::access_key_id_
std::string access_key_id_
Definition: aws_request_signer.h:57
grpc_core::AwsRequestSigner::secret_access_key_
std::string secret_access_key_
Definition: aws_request_signer.h:58
SHA256_Init
#define SHA256_Init
Definition: boringssl_prefix_symbols.h:2156
SHA256_DIGEST_LENGTH
#define SHA256_DIGEST_LENGTH
Definition: sha.h:155
std
Definition: grpcpp/impl/codegen/async_unary_call.h:407
EVP_MAX_MD_SIZE
#define EVP_MAX_MD_SIZE
Definition: digest.h:156
HMAC
#define HMAC
Definition: boringssl_prefix_symbols.h:1783
absl::StatusOr::value
const T & value() const &ABSL_ATTRIBUTE_LIFETIME_BOUND
Definition: abseil-cpp/absl/status/statusor.h:687
absl::StatusOr
Definition: abseil-cpp/absl/status/statusor.h:187
len
int len
Definition: abseil-cpp/absl/base/internal/low_level_alloc_test.cc:46
service
__attribute__((deprecated("Please use GRPCProtoMethod."))) @interface ProtoMethod NSString * service
Definition: ProtoMethod.h:25
grpc_error
Definition: error_internal.h:42
method
NSString * method
Definition: ProtoMethod.h:28
grpc_core::AwsRequestSigner::request_payload_
std::string request_payload_
Definition: aws_request_signer.h:63
SHA256_Final
#define SHA256_Final
Definition: boringssl_prefix_symbols.h:2155
grpc_core::AwsRequestSigner::additional_headers_
std::map< std::string, std::string > additional_headers_
Definition: aws_request_signer.h:64
hmac.h
grpc_core::AwsRequestSigner::GetSignedRequestHeaders
std::map< std::string, std::string > GetSignedRequestHeaders()
Definition: aws_request_signer.cc:116
port_platform.h
SHA256_Update
#define SHA256_Update
Definition: boringssl_prefix_symbols.h:2159


grpc
Author(s):
autogenerated on Fri May 16 2025 02:57:45