git.fiddlerwoaroof.com
Raw Blame History
/* 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>

int ipow (int base, int exp)
{
    int result = 1;

    while (exp) {
        if (exp & 1) {
            result *= base;
        }

        exp >>= 1;
        base *= base;
    }

    return result;
}

unsigned long bytesToInt (const std::string &bytes)
{
    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;
}

time_t time_step (const time_t time, const int step)
{
    // Time is > 0 so division produces the result we want.
    return time / step;
}

class impl : public token_generator_ifc
{
private:
    const sys_time &sys_time;
    unsigned int code_digits;
    const std::shared_ptr<CryptoPP::SecByteBlock> key;

    unsigned long truncate (const std::string &mac) const;

    unsigned long hotp (const CryptoPP::SecByteBlock &key,
                        const CryptoPP::Integer &counter) const;

    // TODO: move elsewhere
    CryptoPP::SecByteBlock generate_key (unsigned int size) const;

    unsigned long totp_generator::truncate (const std::string &mac) const
    {
        uint8_t offset = static_cast<uint8_t > (mac[19]) & static_cast<uint8_t>
                         (0x0f);
        std::string  offsetBytes = mac.substr (offset, 4);
        return bytesToInt (offsetBytes) & 0x7fffffff;
    }

    unsigned long totp_generator::hotp (const CryptoPP::SecByteBlock &key,
                                        const CryptoPP::Integer &counter) const
    {
        std::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;
    }

    CryptoPP::SecByteBlock totp_generator::generate_key (unsigned int size)
    const
    {
        CryptoPP::AutoSeededRandomPool prng;

        CryptoPP::SecByteBlock key (size);
        prng.GenerateBlock (key, key.size());
        return key;
    }

public:
    std::string generate_token () const override
    {
        time_t foo = 111;
        const CryptoPP::Integer &time = sys_time.time (&foo);
        std::cout << "At the tone, the time is: " << foo << " or " << time << "\a"
                  << std::endl;
        int time_step_size = 30;
        CryptoPP::Integer current_step = time_step (time.ConvertToLong(),
                                         time_step_size);
        long otp = hotp (*key, current_step);
        std::ostringstream is;
        is << std::setfill ('0') << std::setw (6)<< otp;
        return is.str();
    }
};

// Generator goes here....
std::string totp_generator::generate_token () const
;