45ab05a9 |
#include <cmath>
#include <ctime>
#include <functional>
#include <iomanip>
#include <iostream>
#include <unistd.h>
#include <cryptopp/base32.h>
#include <cryptopp/hex.h>
#include <cryptopp/hmac.h>
#include <cryptopp/osrng.h>
using namespace std;
// CLASSES
class OTPGenerator {
public:
virtual unsigned long generate(const CryptoPP::SecByteBlock &key, const CryptoPP::Integer &input) const = 0;
};
int ipow(int base, int exp) {
int result = 1;
while (exp) {
if (exp & 1)
result *= base;
exp >>= 1;
base *= base;
}
return result;
}
class HOTP : public OTPGenerator {
private:
unsigned int code_digits;
unsigned long bytesToInt(const string &bytes) const {
unsigned long result = 0;
auto byteCount = bytes.size() - 1;
for (auto byte = bytes.cbegin(); byte < bytes.cend(); byte++, byteCount--) {
const uint8_t val = static_cast<uint8_t>(*byte);
result |= val << (byteCount * 8);
}
return result;
}
unsigned long truncate(const string &mac) const {
uint8_t offset = static_cast<uint8_t >(mac[19]) & static_cast<uint8_t>(0x0f);
string offsetBytes = mac.substr(offset, 4);
return bytesToInt(offsetBytes) & 0x7fffffff;
}
public:
HOTP(unsigned int digits):
code_digits(digits)
{}
virtual unsigned long generate(const CryptoPP::SecByteBlock &key, const CryptoPP::Integer &counter) const override {
string mac;
CryptoPP::SecByteBlock counter_bytes(8);
// Do I know that 8 is sufficient here? . . .
counter.Encode(counter_bytes.BytePtr(), 8, CryptoPP::Integer::UNSIGNED);
CryptoPP::HMAC<CryptoPP::SHA1> hmac(key, key.size());
CryptoPP::StringSink *stringSink = new CryptoPP::StringSink(mac);
CryptoPP::HashFilter *hashFilter = new CryptoPP::HashFilter(hmac, stringSink);
CryptoPP::StringSource ss2(counter_bytes, counter_bytes.size(), true, hashFilter);
unsigned long result = truncate(mac);
result = result % ipow(10, code_digits);
return result;
}
};
class TOTP : public HOTP {
private:
unsigned int time_step_size;
time_t time_step(const time_t time, const int step = 30) const {
// Time is > 0 so division produces the result we want.
return time / step;
}
public:
TOTP(unsigned int step, unsigned int digits):
time_step_size(step), HOTP(digits)
{}
virtual unsigned long generate(const CryptoPP::SecByteBlock &key, const CryptoPP::Integer &time) const override {
CryptoPP::Integer current_step = time_step(time.ConvertToLong(), time_step_size);
return HOTP::generate(key, current_step);
}
};
// Frontend
CryptoPP::SecByteBlock generate_key(unsigned int size) {
CryptoPP::AutoSeededRandomPool prng;
CryptoPP::SecByteBlock key(size);
prng.GenerateBlock(key, key.size());
return key;
}
time_t get_time() {
time_t theTime = time(0);
if (theTime == static_cast<time_t>(-1)) {
std::cerr << "time() failed..." << endl;
}
return theTime;
}
typedef std::function<std::string(const CryptoPP::SecByteBlock&)> byte_encoder;
typedef std::function<time_t()> time_cb;
std::string base32_encode(const CryptoPP::SecByteBlock string) {
std::string encoded;
CryptoPP::StringSink *stringSink = new CryptoPP::StringSink(encoded);
CryptoPP::Base32Encoder *base32Encoder = new CryptoPP::Base32Encoder(stringSink);
const byte ALPHABET[32+1] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
CryptoPP::AlgorithmParameters params = CryptoPP::MakeParameters(CryptoPP::Name::EncodingLookupArray(),(const byte *)ALPHABET);
base32Encoder->IsolatedInitialize(params);
CryptoPP::StringSource ss1(string, string.size(), true, base32Encoder);
return encoded;
}
void deterministic_main(
const CryptoPP::SecByteBlock& key,
const time_t theTime,
const byte_encoder key_encoder,
const OTPGenerator& code_generator
) {
try {
std::cout << "key: " << std::setw(41) << key_encoder(key) << std::endl;
unsigned long result = code_generator.generate(key, theTime);
cout << setw(46) << setfill('-') << "-" << endl << setfill(' ');
cout << "code: " << setw(34) << " " << setw(6) << setfill('0') << result << endl << setfill(' ');
} catch (const CryptoPP::Exception &e) {
std::cerr << e.what() << endl;
exit(1);
}
}
int main() {
unsigned int digits = 6;
unsigned int time_step = 30;
unsigned int key_size = 16;
CryptoPP::SecByteBlock key = generate_key(key_size);
time_t theTime = get_time();
TOTP code_generator = TOTP(time_step, digits);
deterministic_main(key, theTime, base32_encode, code_generator);
return 0;
}
|