/** * Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. * * @author baidu aip */ #ifndef __AIP_UTILS_H__ #define __AIP_UTILS_H__ #include #include #include #include #include #include #include const int __BCE_VERSION__ = 1; const int __BCE_EXPIRE__ = 1800; namespace aip { template std::basic_istream& getall(std::basic_istream& input, std::basic_string& str) { std::ostringstream oss; oss << input.rdbuf(); str.assign(oss.str()); return input; } inline int get_file_content(const char *filename, std::string* out) { std::ifstream in(filename, std::ios::in | std::ios::binary); if (in) { getall(in, *out); return 0; } else { return -1; } } inline std::string to_upper(std::string src) { std::transform(src.begin(), src.end(), src.begin(), toupper); return src; } inline std::string to_lower(std::string src) { std::transform(src.begin(), src.end(), src.begin(), tolower); return src; } inline std::string to_hex(unsigned char c, bool lower = false) { const std::string hex = "0123456789ABCDEF"; std::stringstream ss; ss << hex[c >> 4] << hex[c & 0xf]; return lower ? to_lower(ss.str()) : ss.str(); } inline time_t now() { return time(NULL); } std::string utc_time(time_t timestamp) { struct tm result_tm; char buffer[32]; #ifdef _WIN32 gmtime_s(&result_tm, ×tamp); #else gmtime_r(×tamp, &result_tm); #endif size_t size = strftime(buffer, 32, "%Y-%m-%dT%H:%M:%SZ", &result_tm); return std::string(buffer, size); } void url_parse( const std::string & url, std::map & params) { int pos = (int)url.find("?"); if (pos != -1) { int key_start = pos + 1, key_len = 0, val_start = 0; for (int i = key_start; i <= (int)url.size(); ++i) { switch (url[i]) { case '=': key_len = i - key_start; val_start = i + 1; break; case '\0': case '&': if (key_len != 0) { params[url.substr(key_start, key_len)] = url.substr(val_start, i - val_start); key_start = i + 1; key_len = 0; } break; default: break; } } } } std::string url_encode(const std::string & input, bool encode_slash=true) { std::stringstream ss; const char *str = input.c_str(); for (uint32_t i = 0; i < input.size(); i++) { unsigned char c = str[i]; if (isalnum(c) || c == '_' || c == '-' || c == '~' || c == '.' || (!encode_slash && c == '/')) { ss << c; } else { ss << "%" << to_hex(c); } } return ss.str(); } std::string canonicalize_params(std::map & params) { std::vector v; v.reserve(params.size()); for (auto & it : params) { v.push_back(url_encode(it.first) + "=" + url_encode(it.second)); } std::sort(v.begin(), v.end()); std::string result; for (auto & it : v) { result.append((result.empty() ? "" : "&") + it); } return result; } std::string canonicalize_headers(std::map & headers) { std::vector v; v.reserve(headers.size()); for (auto & it : headers) { v.push_back(url_encode(to_lower(it.first)) + ":" + url_encode(it.second)); } std::sort(v.begin(), v.end()); std::string result; for (auto & it : v) { result.append((result.empty() ? "" : "\n") + it); } return result; } std::string get_headers_keys(std::map & headers) { std::vector v; v.reserve(headers.size()); for (auto & it : headers) { v.push_back(to_lower(it.first)); } std::string result; for (auto & it : v) { result.append((result.empty() ? "" : ";") + it); } return result; } std::string get_host(const std::string & url) { int pos = (int)url.find("://") + 3; return url.substr( pos, url.find('/', pos) - pos ); } std::string get_path(const std::string & url) { int path_start = (int)url.find('/', url.find("://") + 3); int path_end = (int)url.find('?'); path_end = path_end == -1 ? (int)url.size() : path_end; return url.substr(path_start, path_end - path_start); } std::string hmac_sha256( const std::string & src, const std::string & sk) { const EVP_MD *evp_md = EVP_sha256(); unsigned char md[EVP_MAX_MD_SIZE]; unsigned int md_len = 0; if (HMAC(evp_md, reinterpret_cast(sk.data()), (int)sk.size(), reinterpret_cast(src.data()), src.size(), md, &md_len) == NULL) { return ""; } std::stringstream ss; for (int i = 0; i < (int)md_len; ++i) { ss << to_hex(md[i], true); } return ss.str(); } void sign( std::string method, std::string & url, std::map & params, std::map & headers, std::string & ak, std::string & sk) { url_parse(url, params); headers["Host"] = get_host(url); std::string timestamp = utc_time(now()); headers["x-bce-date"] = timestamp; std::stringstream ss; ss << "bce-auth-v" << __BCE_VERSION__ << "/" << ak << "/" << timestamp << "/" << __BCE_EXPIRE__; std::string val = ss.str(); std::string sign_key = hmac_sha256(val, sk); ss.str(""); ss << to_upper(method) << '\n' << url_encode(get_path(url), false) << '\n' << canonicalize_params(params) << '\n' << canonicalize_headers(headers); std::string signature = hmac_sha256(ss.str(), sign_key); ss.str(""); ss << "bce-auth-v" << __BCE_VERSION__ << "/" << ak << "/" << timestamp << "/" << __BCE_EXPIRE__ << "/" << get_headers_keys(headers) << "/" << signature; headers["authorization"] = ss.str(); } } #endif