46 #include <openssl/sha.h>
47 #include <openssl/hmac.h>
49 #include "BESInternalError.h"
51 #include "url_parser.h"
56 const int SHA256_DIGEST_STRING_LENGTH = (SHA256_DIGEST_LENGTH << 1);
58 std::string join(
const std::vector<std::string>& ss,
const std::string delim) {
59 std::stringstream sstream;
60 const auto l = ss.size() - 1;
61 std::vector<int>::size_type i;
62 for (i = 0; i < l; i++) {
63 sstream << ss.at(i) << delim;
71 void sha256(
const std::string str,
unsigned char outputBuffer[SHA256_DIGEST_LENGTH]) {
72 char *c_string =
new char [str.length()+1];
73 std::strcpy(c_string, str.c_str());
77 SHA256_Update(&sha256, c_string, strlen(c_string));
78 unsigned char hash[SHA256_DIGEST_LENGTH];
79 SHA256_Final(hash, &sha256);
81 for (
int i=0;i<SHA256_DIGEST_LENGTH;i++) {
82 outputBuffer[i] = hash[i];
87 std::string sha256_base16(
const std::string str) {
89 unsigned char hashOut[SHA256_DIGEST_LENGTH];
93 SHA256_Update(&sha256, (
const unsigned char *)str.c_str(), str.length());
94 SHA256_Final(hashOut, &sha256);
96 AWSV4::sha256(str,hashOut);
99 char outputBuffer[SHA256_DIGEST_STRING_LENGTH + 1];
100 for (
int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
101 snprintf(outputBuffer + (i * 2), 3,
"%02x", hashOut[i]);
103 outputBuffer[SHA256_DIGEST_STRING_LENGTH] = 0;
104 return std::string{outputBuffer};
108 static std::string trim(
const std::string& str,
const std::string& whitespace =
" \t")
110 const auto strBegin = str.find_first_not_of(whitespace);
111 if (strBegin == std::string::npos)
114 const auto strEnd = str.find_last_not_of(whitespace);
115 const auto strRange = strEnd - strBegin + 1;
117 return str.substr(strBegin, strRange);
128 std::map<std::string,std::string> canonicalize_headers(
const std::vector<std::string>& headers) {
129 std::map<std::string,std::string> header_key2val;
130 for(
auto h = headers.begin(), end = headers.end(); h != end; ++h ) {
136 auto i = h->find(
':');
137 if (i == std::string::npos) {
138 header_key2val.clear();
139 return header_key2val;
142 std::string key = trim(h->substr(0, i));
143 const std::string val = trim(h->substr(i+1));
144 if (key.empty() || val.empty()) {
145 header_key2val.clear();
146 return header_key2val;
149 std::transform(key.begin(), key.end(), key.begin(),::tolower);
150 header_key2val[key] = val;
152 return header_key2val;
156 const std::string map_headers_string(
const std::map<std::string,std::string>& header_key2val) {
157 const std::string pair_delim{
":"};
159 for (
auto kv = header_key2val.begin(), end = header_key2val.end(); kv != end; ++kv) {
160 h.append(kv->first + pair_delim + kv->second + ENDL);
166 const std::string map_signed_headers(
const std::map<std::string,std::string>& header_key2val) {
167 const std::string signed_headers_delim{
";"};
168 std::vector<std::string> ks;
170 for (
auto kv = header_key2val.begin(), end = header_key2val.end(); kv != end; ++kv) {
171 ks.push_back(kv->first);
173 return join(ks,signed_headers_delim);
176 const std::string canonicalize_request(
const std::string& http_request_method,
177 const std::string& canonical_uri,
178 const std::string& canonical_query_string,
179 const std::string& canonical_headers,
180 const std::string& signed_headers,
181 const std::string& shar256_of_payload) {
182 return http_request_method + ENDL +
183 canonical_uri + ENDL +
184 canonical_query_string + ENDL +
185 canonical_headers + ENDL +
186 signed_headers + ENDL +
194 const std::string string_to_sign(
const std::string& algorithm,
195 const std::time_t& request_date,
196 const std::string& credential_scope,
197 const std::string& hashed_canonical_request) {
198 return algorithm + ENDL +
199 ISO8601_date(request_date) + ENDL +
200 credential_scope + ENDL +
201 hashed_canonical_request;
204 const std::string credential_scope(
const std::time_t& request_date,
205 const std::string region,
206 const std::string service) {
207 const std::string s{
"/"};
208 return utc_yyyymmdd(request_date) + s + region + s + service + s + AWS4_REQUEST;
212 const std::string ISO8601_date(
const std::time_t& t) {
213 char buf[
sizeof "20111008T070709Z"];
214 std::strftime(buf,
sizeof buf,
"%Y%m%dT%H%M%SZ", std::gmtime(&t));
215 return std::string{buf};
219 const std::string utc_yyyymmdd(
const std::time_t& t) {
220 char buf[
sizeof "20111008"];
221 std::strftime(buf,
sizeof buf,
"%Y%m%d", std::gmtime(&t));
222 return std::string{buf};
226 const std::string hmac_to_string(
const unsigned char *hmac) {
228 char buf[SHA256_DIGEST_STRING_LENGTH + 1];
229 for (
int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
231 snprintf(buf + (i * 2), 3,
"%02x", hmac[i]);
233 buf[SHA256_DIGEST_STRING_LENGTH] = 0;
234 return std::string{buf};
249 const std::string calculate_signature(
const std::time_t& request_date,
250 const std::string secret,
251 const std::string region,
252 const std::string service,
253 const std::string string_to_sign,
254 const bool verbose) {
257 unsigned char md[EVP_MAX_MD_SIZE+1];
260 const std::string k1 = AWS4 + secret;
261 const std::string yyyymmdd = utc_yyyymmdd(request_date);
262 unsigned char* kDate = HMAC(EVP_sha256(), (
const void *)k1.c_str(), k1.length(),
263 (
const unsigned char *)yyyymmdd.c_str(), yyyymmdd.length(), md, &md_len);
265 throw BESInternalError(
"Could not compute AWS V4 requst signature." ,__FILE__, __LINE__);
268 std::cerr <<
"kDate: " << hmac_to_string(kDate) << std::endl;
269 std::cerr <<
"md_len: " << md_len << std::endl;
271 std::cerr <<
"md: " << hmac_to_string(md) << std::endl;
274 unsigned char *kRegion = HMAC(EVP_sha256(), md, (
size_t)md_len,
275 (
const unsigned char*)region.c_str(), region.length(), md, &md_len);
277 throw BESInternalError(
"Could not compute AWS V4 requst signature." ,__FILE__, __LINE__);
280 std::cerr <<
"kRegion: " << hmac_to_string(kRegion) << std::endl;
281 std::cerr <<
"md_len: " << md_len << std::endl;
283 std::cerr <<
"md: " << hmac_to_string(md) << std::endl;
286 unsigned char *kService = HMAC(EVP_sha256(), md, (
size_t)md_len,
287 (
const unsigned char*)service.c_str(), service.length(), md, &md_len);
289 throw BESInternalError(
"Could not compute AWS V4 requst signature." ,__FILE__, __LINE__);
292 std::cerr <<
"kService: " << hmac_to_string(kService) << std::endl;
293 std::cerr <<
"md_len: " << md_len << std::endl;
295 std::cerr <<
"md: " << hmac_to_string(md) << std::endl;
298 unsigned char *kSigning = HMAC(EVP_sha256(), md, (
size_t)md_len,
299 (
const unsigned char*)AWS4_REQUEST.c_str(), AWS4_REQUEST.length(), md, &md_len);
301 throw BESInternalError(
"Could not compute AWS V4 requst signature." ,__FILE__, __LINE__);
304 std::cerr <<
"kSigning " << hmac_to_string(kSigning) << std::endl;
305 std::cerr <<
"md_len: " << md_len << std::endl;
307 std::cerr <<
"md: " << hmac_to_string(md) << std::endl;
310 unsigned char *kSig = HMAC(EVP_sha256(), md, (
size_t)md_len,
311 (
const unsigned char*)string_to_sign.c_str(), string_to_sign.length(), md, &md_len);
313 throw BESInternalError(
"Could not compute AWS V4 requst signature." ,__FILE__, __LINE__);
316 auto sig = hmac_to_string(md);
333 const std::string compute_awsv4_signature(
334 const std::string &uri_str,
335 const std::time_t &request_date,
336 const std::string &public_key,
337 const std::string &secret_key,
338 const std::string ®ion,
339 const std::string &service,
340 const bool &verbose) {
342 url_parser uri(uri_str);
345 const auto canonical_uri = uri.path();
347 const auto canonical_query = uri.query();
351 const std::string sha256_empty_payload = {
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"};
359 std::vector<std::string> headers{
"host: ",
"x-amz-date: "};
360 headers[0].append(uri.host());
361 headers[1].append(ISO8601_date(request_date));
363 const auto canonical_headers_map = canonicalize_headers(headers);
364 if (canonical_headers_map.empty()) {
365 throw std::runtime_error(
"Empty header list while building AWS V4 request signature");
367 const auto headers_string = map_headers_string(canonical_headers_map);
368 const auto signed_headers = map_signed_headers(canonical_headers_map);
369 const auto canonical_request = canonicalize_request(AWSV4::GET,
374 sha256_empty_payload);
377 std::cerr <<
"-- Canonical Request\n" << canonical_request <<
"\n--\n" << std::endl;
379 auto hashed_canonical_request = sha256_base16(canonical_request);
380 auto credential_scope = AWSV4::credential_scope(request_date,region,service);
381 auto string_to_sign = AWSV4::string_to_sign(STRING_TO_SIGN_ALGO,
384 hashed_canonical_request);
387 std::cerr <<
"-- String to Sign\n" << string_to_sign <<
"\n----\n" << std::endl;
389 auto signature = calculate_signature(request_date,
396 std::cerr <<
"-- signature\n" << signature <<
"\n----\n" << std::endl;
398 const std::string authorization_header = STRING_TO_SIGN_ALGO +
" Credential=" + public_key +
"/"
399 + credential_scope +
", SignedHeaders=" + signed_headers +
", Signature=" + signature;
402 std::cerr <<
"-- authorization_header\n" << authorization_header <<
"\n----\n" << std::endl;
404 return authorization_header;