4 #include "util/aws/aws.h" 6 #include <openssl/hmac.h> 7 #include <openssl/sha.h> 8 #include <openssl/evp.h> 10 #include "absl/strings/escaping.h" 11 #include "absl/strings/str_cat.h" 12 #include "absl/strings/str_join.h" 13 #include "absl/strings/str_split.h" 14 #include "base/logging.h" 19 using namespace boost;
20 namespace h2 = beast::http;
24 void HMAC(absl::string_view key, absl::string_view msg,
char dest[SHA256_DIGEST_LENGTH]) {
25 HMAC_CTX* hmac = HMAC_CTX_new();
27 CHECK_EQ(1, HMAC_CTX_reset(hmac));
28 CHECK_EQ(1, HMAC_Init_ex(hmac, reinterpret_cast<const uint8_t*>(key.data()), key.size(),
31 CHECK_EQ(1, HMAC_Update(hmac, reinterpret_cast<const uint8_t*>(msg.data()), msg.size()));
33 uint8_t* ptr = reinterpret_cast<uint8_t*>(dest);
35 CHECK_EQ(1, HMAC_Final(hmac, ptr, &len));
40 string GetSignatureKey(absl::string_view key, absl::string_view datestamp, absl::string_view region,
41 absl::string_view service) {
43 absl::string_view sign_key{sign,
sizeof(sign)};
44 HMAC(absl::StrCat(
"AWS4", key), datestamp, sign);
45 HMAC(sign_key, region, sign);
46 HMAC(sign_key, service, sign);
47 HMAC(sign_key,
"aws4_request", sign);
49 return string(sign_key);
52 void Hexify(
const char* str,
size_t len,
char* dest) {
53 static constexpr
char kHex[] =
"0123456789abcdef";
55 for (
unsigned i = 0; i < len; ++i) {
57 *dest++ = kHex[(c >> 4) & 0xF];
58 *dest++ = kHex[c & 0xF];
64 using bb_str_view = ::boost::beast::string_view;
66 inline absl::string_view absl_sv(
const bb_str_view s) {
67 return absl::string_view{s.data(), s.size()};
70 constexpr
char kAlgo[] =
"AWS4-HMAC-SHA256";
76 void Sha256String(absl::string_view str,
char out[65]) {
77 unsigned char hash[SHA256_DIGEST_LENGTH];
80 EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
83 CHECK_EQ(1, EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL));
85 CHECK_EQ(1, EVP_DigestUpdate(mdctx, str.data(), str.size()));
88 CHECK_EQ(1, EVP_DigestFinal_ex(mdctx, hash, &len));
89 CHECK_EQ(SHA256_DIGEST_LENGTH, len);
91 EVP_MD_CTX_free(mdctx);
96 SHA256_Update(&sha256, str.data(), str.size());
97 SHA256_Final(hash, &sha256);
100 Hexify(reinterpret_cast<const char*>(hash), SHA256_DIGEST_LENGTH, out);
103 void Sha256String(const ::boost::beast::multi_buffer& mb,
char out[65]) {
104 unsigned char hash[SHA256_DIGEST_LENGTH];
106 SHA256_Init(&sha256);
108 for (
const auto& e: mb.cdata()) {
109 SHA256_Update(&sha256, e.data(), e.size());
111 SHA256_Final(hash, &sha256);
112 Hexify(reinterpret_cast<const char*>(hash), SHA256_DIGEST_LENGTH, out);
117 const char AWS::kHashEmpty[] =
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
119 ::boost::asio::ssl::context AWS::CheckedSslContext() {
120 system::error_code ec;
121 asio::ssl::context cntx{asio::ssl::context::tlsv12_client};
122 cntx.set_options(boost::asio::ssl::context::default_workarounds |
123 boost::asio::ssl::context::no_compression | boost::asio::ssl::context::no_sslv2 |
124 boost::asio::ssl::context::no_sslv3 | boost::asio::ssl::context::no_tlsv1 |
125 boost::asio::ssl::context::no_tlsv1_1);
126 cntx.set_verify_mode(asio::ssl::verify_peer, ec);
128 LOG(FATAL) <<
"Could not set verify mode " << ec;
131 cntx.load_verify_file(
"/etc/ssl/certs/ca-certificates.crt", ec);
133 LOG(FATAL) <<
"Could not load certificates file" << ec;
140 const char* access_key = getenv(
"AWS_ACCESS_KEY_ID");
141 const char* secret_key = getenv(
"AWS_SECRET_ACCESS_KEY");
144 return Status(
"Can not find AWS_ACCESS_KEY_ID");
147 return Status(
"Can not find AWS_ACCESS_KEY_ID");
149 secret_ = secret_key;
150 access_key_ = access_key;
152 time_t now = time(
nullptr);
155 CHECK(&tm_s == gmtime_r(&now, &tm_s));
157 CHECK_GT(strftime(date_str_, arraysize(date_str_),
"%Y%m%d", &tm_s), 0);
158 sign_key_ = GetSignatureKey(secret_, date_str_, region_id_, service_);
160 credential_scope_ = absl::StrCat(date_str_,
"/", region_id_,
"/", service_,
"/",
"aws4_request");
167 void AWS::Sign(absl::string_view domain, absl::string_view sha256,
168 ::boost::beast::http::header<true, ::boost::beast::http::fields>* header)
const {
169 header->set(h2::field::host, domain);
172 time_t now = time(
nullptr);
175 CHECK(&tm_s == gmtime_r(&now, &tm_s));
179 CHECK_GT(strftime(amz_date, arraysize(amz_date),
"%Y%m%dT%H%M00Z", &tm_s), 0);
180 VLOG(1) <<
"Time now: " << now;
182 header->set(
"x-amz-date", amz_date);
183 header->set(
"x-amz-content-sha256", sha256);
185 string canonical_headers = absl::StrCat(
"host",
":", domain,
"\n");
186 absl::StrAppend(&canonical_headers,
"x-amz-content-sha256",
":", sha256,
"\n");
187 absl::StrAppend(&canonical_headers,
"x-amz-date",
":", amz_date,
"\n");
190 AuthHeader(absl_sv(header->method_string()), canonical_headers, absl_sv(header->target()),
193 header->set(h2::field::authorization, auth_header);
196 string AWS::AuthHeader(absl::string_view method, absl::string_view headers,
197 absl::string_view target, absl::string_view content_sha256,
198 absl::string_view amz_date)
const {
199 size_t pos = target.find(
'?');
200 absl::string_view url = target.substr(0, pos);
201 absl::string_view query_string;
202 string canonical_querystring;
204 if (pos != string::npos) {
205 query_string = target.substr(pos + 1);
208 vector<absl::string_view> params = absl::StrSplit(query_string,
"&", absl::SkipWhitespace{});
209 sort(params.begin(), params.end());
210 canonical_querystring = absl::StrJoin(params,
"&");
213 string canonical_request = absl::StrCat(method,
"\n", url,
"\n", canonical_querystring,
"\n");
214 string signed_headers =
"host;x-amz-content-sha256;x-amz-date";
215 absl::StrAppend(&canonical_request, headers,
"\n", signed_headers,
"\n", content_sha256);
216 VLOG(1) <<
"CanonicalRequest:\n" << canonical_request <<
"\n-------------------\n";
219 detail::Sha256String(canonical_request, hexdigest);
221 string string_to_sign =
222 absl::StrCat(kAlgo,
"\n", amz_date,
"\n", credential_scope_,
"\n", hexdigest);
224 char signature[SHA256_DIGEST_LENGTH];
225 HMAC(sign_key_, string_to_sign, signature);
226 Hexify(signature, SHA256_DIGEST_LENGTH, hexdigest);
228 string authorization_header =
229 absl::StrCat(kAlgo,
" Credential=", access_key_,
"/", credential_scope_,
230 ",SignedHeaders=", signed_headers,
",Signature=", hexdigest);
232 return authorization_header;