Files
CocoaTweet/src/cocoatweet/authentication/oauth.cc
T
2022-04-23 18:09:12 +09:00

175 lines
4.7 KiB
C++

#include "oauth.h"
#include "cocoatweet/util/util.h"
#include <random>
#include <ctime>
#include <bitset>
#include <sstream>
#include <string>
#include <cstring>
#include <iterator>
#include <nlohmann/json.hpp>
#include <cocoatweet/exception/invalidParameterException.h>
extern "C" {
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include <openssl/buffer.h>
#include <curl/curl.h>
}
#ifndef NDEBUG
#include <iostream>
#endif
namespace CocoaTweet::Authentication {
OAuth1::OAuth1() {
method_ = AuthenticationMethod::OAUTH10A;
}
OAuth1::OAuth1(const Key _key) {
key_ = _key;
method_ = AuthenticationMethod::OAUTH10A;
}
std::map<std::string, std::string> OAuth1::signature(
const std::map<std::string, std::string>& _param, const std::string& _method,
const std::string& _url) {
std::vector<std::string> 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<std::string, std::string>{{"oauth_signature", k64Sha1}};
return ret;
}
const std::string OAuth1::calculateAuthHeader(std::map<std::string, std::string> _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<std::string> 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<std::size_t> 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<std::string, std::string> OAuth1::oauthParam() const {
auto tmp = std::map<std::string, std::string>{{"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<const unsigned char*>(_key.c_str()), _key.length(),
reinterpret_cast<const unsigned char*>(_data.c_str()), _data.length(), result, &length);
auto sha1 = std::string(reinterpret_cast<char*>(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<std::string>(k64);
}
} // namespace CocoaTweet::Authentication