レポジトリ種類: Mercurial
#include <FL/fl_ask.H>
#include <string>
#include <cmath>
#include <cstdlib>
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include "base32.hh"
#include "otppass.hh"
#include "../main.hh"
#if defined(__APPLE)
#include <libkern/OSByteOrder.h>
#define htobe64(x) OSSwapHostToBigInt64(x)
#endif
int extractIntVal(const size_t &start, const std::string &url) {
if (start != std::string::npos) {
auto end = url.find('&', start);
if (end == std::string::npos) {
end = url.length();
}
std::string str =
url.substr(start + 7, end - (start + 7));
return std::atoi(str.c_str());
}
return 0;
}
std::vector<unsigned char> Otppass::extract_secret(const std::string &otpauth_url) {
digits = extractIntVal(otpauth_url.find("digits="), otpauth_url);
if (digits == 0) digits = 6;
period = extractIntVal(otpauth_url.find("period="), otpauth_url);
if (period == 0) period = 30;
auto secret_start = otpauth_url.find("secret=");
if (secret_start == std::string::npos) {
std::string err = (lang.compare(0, 2, "en") == 0 ?
"Failed to find secret in the OTPAuth URL" :
"OTPAuth URLの中に、シークレットを見つけられませんでした");
fl_alert("%s", err.c_str());
return {};
}
secret_start += 7;
auto secret_end = otpauth_url.find('&', secret_start);
if (secret_end == std::string::npos) {
secret_end = otpauth_url.length();
}
if (secret_end < secret_start) {
std::string err = (lang.compare(0, 2, "en") == 0 ?
"Incorrect secret range" :
"不正なシークレットの距離");
fl_alert("%s", err.c_str());
return {};
}
std::string secret_encoded =
otpauth_url.substr(secret_start, secret_end - secret_start);
std::vector<unsigned char> secret_decoded;
secret_decoded = Base32::decode(secret_encoded);
if (secret_decoded.empty()) {
std::string err = (lang.compare(0, 2, "en") == 0 ?
"Failed to decrypt BASE32" :
"BASE32の復号化に失敗");
fl_alert("%s", err.c_str());
return {};
}
return secret_decoded;
}
#if defined(__HAIKU__) || defined(__linux)
uint64_t Otppass::htobe64(uint64_t counter) {
uint64_t res = 0;
uint8_t *dest = reinterpret_cast<uint8_t*>(&res);
const uint8_t *src = reinterpret_cast<const uint8_t*>(&counter);
for (int i = 0; i < 8; ++i) {
dest[i] = src[7 - i];
}
return res;
}
#endif
uint32_t Otppass::generate_totp(const std::vector<unsigned char> &secret,
uint64_t counter) {
counter = htobe64(counter);
unsigned char hash[SHA_DIGEST_LENGTH];
HMAC(
EVP_sha1(),
secret.data(),
secret.size(),
reinterpret_cast<unsigned char *>(&counter),
sizeof(counter),
hash,
NULL
);
int offset = hash[SHA_DIGEST_LENGTH - 1] & 0x0F;
uint32_t truncated_hash =
(hash[offset] & 0x7F) << 24 |
(hash[offset + 1] & 0xFF) << 16 |
(hash[offset + 2] & 0xFF) << 8 |
(hash[offset + 3] & 0xFF);
return truncated_hash % static_cast<uint32_t>(std::pow(10, digits));
}
std::string Otppass::exec(const std::string &secret) {
try {
std::vector<unsigned char> secret_decoded = extract_secret(secret);
time_t current_time = time(nullptr);
uint64_t counter = current_time / period;
uint32_t otp =
generate_totp(secret_decoded, counter);
char otpres[digits + 1];
std::snprintf(otpres, sizeof(otpres), "%0*u", digits, otp);
return std::string(otpres);
} catch (const std::exception &e) {
std::string err = (lang.compare(0, 2, "en") == 0 ?
"Error" :
"エラー");
fl_alert("%s: %s", err.c_str(), e.what());
return "";
}
}