git.fiddlerwoaroof.com
main.cpp
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;
 }