git.fiddlerwoaroof.com
generator.cc
9b03a29b
 /* Copyright (C) CJ Affiliate
  *
  * You may use, distribute and modify this code under  the
  * terms of the  GNU General Public License  version 2  or
  * later.
  *
  * You should have received a copy of the license with this
  * file. If not, you will find a copy in the "LICENSE" file
  * at https://github.com/cjdev/dual-control.
  */
 
 #include "generator.h"
 #include <iostream>
 
75b15a68
 #include <openssl/hmac.h>
 #include <openssl/evp.h>
 
c1080d50
 #include "base32.h"
75b15a68
 namespace
 {
c1080d50
 class hmac_failed_exception : public std::exception
 {};
 
9b03a29b
 int ipow (int base, int exp)
 {
     int result = 1;
 
     while (exp) {
         if (exp & 1) {
             result *= base;
         }
 
         exp >>= 1;
         base *= base;
     }
 
     return result;
 }
 
75b15a68
 unsigned char *timeToBytes (unsigned long long time, unsigned char *data,
                             size_t data_size)
 {
     for (int idx = data_size - 1; idx > -1; idx--) {
         unsigned char next_digit = time & 0xff;
         data[idx] = next_digit;
         time >>= 8;
     }
 
     return data;
 }
 
9b3884f4
 template <typename S>
 unsigned long bytesToInt (const S &bytes)
9b03a29b
 {
     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;
 }
 
6a23c017
 class token_generator_impl : public token_generator_ifc
9b03a29b
 {
75b15a68
 private:
2e93eae5
     const sys_time clock;
c1080d50
     const unsigned int code_digits;
     const base32 codec;
75b15a68
 
9b03a29b
 private:
75b15a68
     std::string zero_fill (unsigned long result, int digits) const
9b03a29b
     {
75b15a68
         std::ostringstream result_stream;
         result_stream << std::setw (digits) << std::setfill ('0') << result;
         return result_stream.str();
9b03a29b
     }
 
9b3884f4
     unsigned long truncate (const octet_vector &mac) const
47f1fe7f
     {
9085c009
         uint8_t offset = static_cast<uint8_t > (mac[19]) & static_cast<uint8_t>
                          (0x0f);
47f1fe7f
 
9b3884f4
         auto mac_begin = mac.begin();
         auto subseq_begin = mac_begin + offset;
         octet_vector  offsetBytes (subseq_begin, subseq_begin + 4);
47f1fe7f
 
         return bytesToInt (offsetBytes) & 0x7fffffff;
     }
 
c1080d50
     std::string hotp (const std::vector<uint8_t> &key,
                       const unsigned char *data,
75b15a68
                       size_t data_size, const int digits=6) const
9b03a29b
     {
47f1fe7f
         // TODO: see if I can use sha256/etc. with google auth...
0d8b9a17
         const unsigned char *digest = HMAC (EVP_sha1(), key.data(), key.size(),
                                             data,
                                             data_size, NULL, NULL);
47f1fe7f
 
c1080d50
         if (digest == nullptr) {
             throw hmac_failed_exception();
         }
 
9b3884f4
         size_t sha1_output_size = 20;
         // const_cast should be safe here because we generated the pointer
         octet_vector digest_s = octet_vector (digest, digest+sha1_output_size);
47f1fe7f
 
75b15a68
         unsigned long result = truncate (digest_s) % ipow (10,digits);
9b03a29b
 
75b15a68
         return zero_fill (result, digits);
9b03a29b
     }
 
 public:
2e93eae5
     token_generator_impl (const sys_time clock,
75b15a68
                           const int code_digits) :
e57144d3
         clock (clock), code_digits (code_digits)
6ab21073
     {}
 
40fd6c82
     std::string generate_token (const octet_vector &key) const override
9b03a29b
     {
e5bfbac8
         // Assuming time is > 0, integer division produces the result we want.
75b15a68
         const time_t &time_chunk = clock.time (nullptr) / 30;
 
         unsigned char data[8] = {0,0,0,0,0,0,0,0};
         timeToBytes (time_chunk, data, 8);
 
         return hotp (key, data, 8, code_digits);
9b03a29b
     }
 };
6a23c017
 }
9b03a29b
 
 // Generator goes here....
6ab21073
 
 totp_generator::totp_generator (
2e93eae5
     const sys_time clock,
6ab21073
     const int code_digits) :
e57144d3
     delegate_ (std::make_shared<token_generator_impl> (clock, code_digits))
6ab21073
 {}
0d8b9a17