#include "oauth.h" #include "cocoatweet/util/util.h" #include #include #include #include #include #include #include #include #include extern "C" { #include #include #include #include } #ifndef NDEBUG #include #endif namespace CocoaTweet::Authentication { OAuth1::OAuth1() { method_ = AuthenticationMethod::OAUTH10A; } OAuth1::OAuth1(const Key _key) { key_ = _key; method_ = AuthenticationMethod::OAUTH10A; } std::map OAuth1::signature( const std::map& _param, const std::string& _method, const std::string& _url) { std::vector tmp; for (const auto& [key, value] : _param) { tmp.push_back(key + "=" + value); } std::string query = CocoaTweet::Util::join(tmp, "&"); auto significateKey = key().consumerSecret() + "&" + key().accessTokenSecret(); auto significateBase = _method + "&" + CocoaTweet::Util::urlEncode(_url) + "&" + CocoaTweet::Util::urlEncode(query); auto k64Sha1 = hmacSha1(significateKey, significateBase); auto ret = std::map{{"oauth_signature", k64Sha1}}; return ret; } const std::string OAuth1::calculateAuthHeader(std::map _bodyParam, const std::string& _method, const std::string& _url) { auto authParam = oauthParam(); auto sigingParam = authParam; if (!_bodyParam.empty()) { for (const auto [k, v] : _bodyParam) { sigingParam.insert_or_assign(k, v); } } auto sign = signature(sigingParam, _method, _url); authParam.merge(sign); // ヘッダの構築 std::string oauthHeader = "authorization: OAuth "; { std::vector tmp; for (const auto& [key, value] : authParam) { tmp.push_back(key + "=" + CocoaTweet::Util::urlEncode(value)); } oauthHeader += CocoaTweet::Util::join(tmp, ","); } return oauthHeader; } const std::string OAuth1::nonce() const { std::random_device engine; std::string nonceTable = "abcdefghijklmnopqrstuvwxyz0123456789"; std::uniform_int_distribution dist(0, nonceTable.length() - 1); std::string nonce; for (auto i = 0; i < 32; ++i) { nonce += nonceTable[dist(engine)]; } return nonce; } const std::string OAuth1::timestamp() const { return std::to_string(time(nullptr)); } const std::string OAuth1::method() const { return "HMAC-SHA1"; } const std::string OAuth1::version() const { return "1.0"; } std::map OAuth1::oauthParam() const { auto tmp = std::map{{"oauth_nonce", nonce()}, {"oauth_signature_method", method()}, {"oauth_timestamp", timestamp()}, {"oauth_version", version()}}; tmp.merge(key().noSecret()); return tmp; } const std::string OAuth1::base64(const std::string& _raw) { auto base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; std::stringstream ss; for (auto r : _raw) { ss << std::bitset<8>(r); } if (_raw.length() % 3 == 1) { ss << "0000"; } else if (_raw.length() % 3 == 2) { ss << "00"; } auto bin = ss.str(); std::string base64 = ""; for (auto i = 0; i < bin.length() / 6; i++) { base64 += base64Table[std::stoi(bin.substr(i * 6, 6), nullptr, 2)]; } if (base64.length() % 4 == 3) { base64 += "="; } else if (base64.length() % 4 == 2) { base64 += "=="; } else if (base64.length() % 4 == 1) { base64 += "==="; } return base64; } std::string OAuth1::hmacSha1(std::string _key, std::string _data) { unsigned char result[255]; unsigned int length = 255; HMAC(EVP_sha1(), reinterpret_cast(_key.c_str()), _key.length(), reinterpret_cast(_data.c_str()), _data.length(), result, &length); auto sha1 = std::string(reinterpret_cast(result), length); // base64 encodeもやっちゃえ日産 BIO* encoder = BIO_new(BIO_f_base64()); BIO* bmem = BIO_new(BIO_s_mem()); encoder = BIO_push(encoder, bmem); BIO_write(encoder, sha1.c_str(), sha1.length()); BIO_flush(encoder); BUF_MEM* bptr; BIO_get_mem_ptr(encoder, &bptr); char* k64 = (char*)std::malloc(bptr->length); std::memcpy(k64, bptr->data, bptr->length - 1); k64[bptr->length - 1] = 0; BIO_free_all(encoder); return static_cast(k64); } } // namespace CocoaTweet::Authentication