Skip to content

Instantly share code, notes, and snippets.

@chomy
Last active July 24, 2016 17:09
Show Gist options
  • Save chomy/b76cf43da17e4eb7debe1caa1573da0e to your computer and use it in GitHub Desktop.
Save chomy/b76cf43da17e4eb7debe1caa1573da0e to your computer and use it in GitHub Desktop.
Time based one time password generator, based on RFC6238
// Unit test for totp.cc using CppUTest
#include <iostream>
#include <iomanip>
#include <cstring>
#include <array>
#include "CppUTest/CommandLineTestRunner.h"
#include "totp.h"
using namespace std;
TEST_GROUP(T){
public:
typedef array<unsigned char, 20> keytype;
keytype *key;
TEST_SETUP()
{
key = new keytype();
memcpy(key->data(), "12345678901234567890", key->size());
}
TEST_TEARDOWN()
{
delete key;
}
};
TEST(T, steptest)
{
auto test = [](unsigned long ticks, uint64_t val){
step_t v,t;
uint64_t vv = htobe64(val);
memcpy(v.data(), &vv, v.size());
get_step(ticks, &t);
CHECK(v==t);
};
test(59,0x0000000000000001);
test(1111111109, 0x00000000023523EC);
test(1111111111, 0x00000000023523ED);
test(1234567890, 0x000000000273EF07);
test(2000000000, 0x0000000003F940AA);
test(20000000000, 0x0000000027BC86AA);
}
TEST(T, sha1test)
{
auto test = [](const keytype *key, unsigned long ticks, uint32_t otp){
step_t s;
get_step(ticks, &s);
CHECK_EQUAL(otp,calc(key->data(), s.data(), 8));
};
test(key, 59, 94287082);
test(key, 1111111109, 7081804);
test(key, 1234567890, 89005924);
test(key, 2000000000, 69279037);
test(key, 20000000000, 65353130);
}
int main(int argc, char** argv){
return RUN_ALL_TESTS(argc, argv);
}
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
#include <time.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include <endian.h>
//#include <iterator>
//#include <iomanip>
//#include <iostream>
//#include <sstream>
#include "totp.h"
using namespace std;
typedef std::vector<uint8_t> byte_array;
const uint8_t lookup_table[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00 - 0x0f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10 - 0x1f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20 - 0x2f
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // 0x30 - 0x3f
0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40 - 0x4f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x50 - 0x5f
0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0x60 - 0x6f
};
void hex2byte(const string data, byte_array *bin)
{
auto conv = [](uint8_t hi, uint8_t lo){
return (uint8_t)((lookup_table[hi]<<4)|(lookup_table[lo]));
};
byte_array tmp;
for(int i=0; i<data.size(); i+=2)
tmp.push_back(conv(data[i],data[i+1]));
copy(tmp.begin(), tmp.end(), back_inserter(*bin));
}
uint32_t calc(const unsigned char *key, const unsigned char *step, const int digits)
{
unsigned char hash[SHA_DIGEST_LENGTH];
unsigned int len;
HMAC(EVP_sha1(), key, 20,
step, 8, hash, &len);
int offset = (hash[SHA_DIGEST_LENGTH-1]&0x0F);
uint32_t binary = ((hash[offset]&0x7F)<< 24)
| ((hash[offset+1]&0xFF)<< 16)
| ((hash[offset+2]&0xFF)<<8)
| ((hash[offset+3]&0xFF));
return binary%(uint64_t)powl(10,digits);
}
void get_step(step_t *result){
get_step(time(NULL),result);
}
void get_step(const time_t ticks, step_t *result){
uint64_t t = htobe64(ticks/30);
memcpy(result->data(), &t, result->size());
}
#include <array>
#include <cstdint>
typedef std::array<unsigned char, 8> step_t;
void get_step(const time_t, step_t*);
uint32_t calc(const unsigned char *key, const unsigned char *step, const int digits=6);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment