diff --git a/foxotpcode.cpp b/foxotpcode.cpp new file mode 100644 index 000000000..765fd0846 --- /dev/null +++ b/foxotpcode.cpp @@ -0,0 +1,459 @@ +#ifdef FOX_OTP +// +// Time based Rolling code based on HMAC-SHA1; +// + +#include +#include +#include + +#include "foxotpcode.h" + +// The SHA1 and HMAC code included here solely to avoid the need to link to use OpenSSL. + +/*---------------------------------- start of sha1.c --------------------------------------------- + +sha1.c + + By Steve Reid +100% Public Domain + +----------------- +Modified 7/98 +By James H. Brown +Still 100% Public Domain + +Corrected a problem which generated improper hash values on 16 bit machines +Routine SHA1Update changed from + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int +len) +to + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned +long len) + +The 'len' parameter was declared an int which works fine on 32 bit machines. +However, on 16 bit machines an int is too small for the shifts being done +against +it. This caused the hash function to generate incorrect values if len was +greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). + +Since the file IO in main() reads 16K at a time, any file 8K or larger would +be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million +"a"s). + +I also changed the declaration of variables i & j in SHA1Update to +unsigned long from unsigned int for the same reason. + +These changes should make no difference to any 32 bit implementations since +an +int and a long are the same size in those environments. + +-- +I also corrected a few compiler warnings generated by Borland C. +1. Added #include for exit() prototype +2. Removed unused variable 'j' in SHA1Final +3. Changed exit(0) to return(0) at end of main. + +ALL changes I made can be located by searching for comments containing 'JHB' +----------------- +Modified 8/98 +By Steve Reid +Still 100% public domain + +1- Removed #include and used return() instead of exit() +2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) +3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net + +----------------- +Modified 4/01 +By Saul Kravitz +Still 100% PD +Modified to run on Compaq Alpha hardware. + +----------------- +Modified 07/2002 +By Ralph Giles +Still 100% public domain +modified for use with stdint types, autoconf +code cleanup, removed attribution comments +switched SHA1Final() argument order for consistency +use SHA1_ prefix for public api +move public api to sha1.h +*/ + +/* +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +#define SHA1HANDSOFF (1) + + +void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]); + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +#if defined (BYTE_ORDER) && defined(BIG_ENDIAN) && (BYTE_ORDER == BIG_ENDIAN) +#define WORDS_BIGENDIAN 1 +#endif +#ifdef _BIG_ENDIAN +#define WORDS_BIGENDIAN 1 +#endif + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +/* FIXME: can we do this in an endian-proof way? */ +#ifdef WORDS_BIGENDIAN +#define blk0(i) block->l[i] +#else +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xff00ff00) \ + |(rol(block->l[i],8)&0x00ff00ff)) +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v, w, x, y, z, i) \ + z+=((w&(x^y))^y)+blk0(i)+0x5a827999+rol(v,5);w=rol(w,30); +#define R1(v, w, x, y, z, i) \ + z+=((w&(x^y))^y)+blk(i)+0x5a827999+rol(v,5);w=rol(w,30); +#define R2(v, w, x, y, z, i) \ + z+=(w^x^y)+blk(i)+0x6ed9eba1+rol(v,5);w=rol(w,30); +#define R3(v, w, x, y, z, i) \ + z+=(((w|x)&y)|(w&x))+blk(i)+0x8f1bbcdc+rol(v,5);w=rol(w,30); +#define R4(v, w, x, y, z, i) \ + z+=(w^x^y)+blk(i)+0xca62c1d6+rol(v,5);w=rol(w,30); + + +/* Hash a single 512-bit block. This is the core of the algorithm. */ +void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]) { + uint32_t a, b, c, d, e; + typedef union { + uint8_t c[64]; + uint32_t l[16]; + } CHAR64LONG16; + CHAR64LONG16 *block; + +#ifdef SHA1HANDSOFF + CHAR64LONG16 workspace; + block = &workspace; + memcpy(block, buffer, 64); +#else + block = (CHAR64LONG16*)buffer; +#endif + + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a, b, c, d, e, 0); + R0(e, a, b, c, d, 1); + R0(d, e, a, b, c, 2); + R0(c, d, e, a, b, 3); + R0(b, c, d, e, a, 4); + R0(a, b, c, d, e, 5); + R0(e, a, b, c, d, 6); + R0(d, e, a, b, c, 7); + R0(c, d, e, a, b, 8); + R0(b, c, d, e, a, 9); + R0(a, b, c, d, e, 10); + R0(e, a, b, c, d, 11); + R0(d, e, a, b, c, 12); + R0(c, d, e, a, b, 13); + R0(b, c, d, e, a, 14); + R0(a, b, c, d, e, 15); + R1(e, a, b, c, d, 16); + R1(d, e, a, b, c, 17); + R1(c, d, e, a, b, 18); + R1(b, c, d, e, a, 19); + R2(a, b, c, d, e, 20); + R2(e, a, b, c, d, 21); + R2(d, e, a, b, c, 22); + R2(c, d, e, a, b, 23); + R2(b, c, d, e, a, 24); + R2(a, b, c, d, e, 25); + R2(e, a, b, c, d, 26); + R2(d, e, a, b, c, 27); + R2(c, d, e, a, b, 28); + R2(b, c, d, e, a, 29); + R2(a, b, c, d, e, 30); + R2(e, a, b, c, d, 31); + R2(d, e, a, b, c, 32); + R2(c, d, e, a, b, 33); + R2(b, c, d, e, a, 34); + R2(a, b, c, d, e, 35); + R2(e, a, b, c, d, 36); + R2(d, e, a, b, c, 37); + R2(c, d, e, a, b, 38); + R2(b, c, d, e, a, 39); + R3(a, b, c, d, e, 40); + R3(e, a, b, c, d, 41); + R3(d, e, a, b, c, 42); + R3(c, d, e, a, b, 43); + R3(b, c, d, e, a, 44); + R3(a, b, c, d, e, 45); + R3(e, a, b, c, d, 46); + R3(d, e, a, b, c, 47); + R3(c, d, e, a, b, 48); + R3(b, c, d, e, a, 49); + R3(a, b, c, d, e, 50); + R3(e, a, b, c, d, 51); + R3(d, e, a, b, c, 52); + R3(c, d, e, a, b, 53); + R3(b, c, d, e, a, 54); + R3(a, b, c, d, e, 55); + R3(e, a, b, c, d, 56); + R3(d, e, a, b, c, 57); + R3(c, d, e, a, b, 58); + R3(b, c, d, e, a, 59); + R4(a, b, c, d, e, 60); + R4(e, a, b, c, d, 61); + R4(d, e, a, b, c, 62); + R4(c, d, e, a, b, 63); + R4(b, c, d, e, a, 64); + R4(a, b, c, d, e, 65); + R4(e, a, b, c, d, 66); + R4(d, e, a, b, c, 67); + R4(c, d, e, a, b, 68); + R4(b, c, d, e, a, 69); + R4(a, b, c, d, e, 70); + R4(e, a, b, c, d, 71); + R4(d, e, a, b, c, 72); + R4(c, d, e, a, b, 73); + R4(b, c, d, e, a, 74); + R4(a, b, c, d, e, 75); + R4(e, a, b, c, d, 76); + R4(d, e, a, b, c, 77); + R4(c, d, e, a, b, 78); + R4(b, c, d, e, a, 79); + + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/** +* Initialize new context +* +* @param context SHA1-Context +*/ +void SHA1_Init(SHA1_CTX *context) { + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; + context->state[4] = 0xc3d2e1f0; + context->count[0] = context->count[1] = 0; +} + +/** +* Run your data through this +* +* @param context SHA1-Context +* @param p Buffer to run SHA1 on +* @param len Number of bytes +*/ +void SHA1_Update(SHA1_CTX *context, const uint8_t *p, size_t len) { + const uint8_t *data = p; + size_t i, j; + + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += (uint32_t) (len << 3)) < (len << 3)) { + context->count[1]++; + } + context->count[1] += (uint32_t) (len >> 29); + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64 - j)); + SHA1_Transform(context->state, context->buffer); + for (; i + 63 < len; i += 64) { + SHA1_Transform(context->state, data + i); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + + +/** +* Add padding and return the message digest +* +* @param digest Generated message digest +* @param context SHA1-Context +*/ +void SHA1_Final(uint8_t digest[SHA1_DIGEST_SIZE], SHA1_CTX *context) { + uint32_t i; + uint8_t finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (uint8_t) ((context->count[(i >= 4 ? 0 : 1)] + >> ((3 - (i & 3)) * 8)) & 255); + } + SHA1_Update(context, (uint8_t *) "\200", 1); + while ((context->count[0] & 504) != 448) { + SHA1_Update(context, (uint8_t *) "\0", 1); + } + SHA1_Update(context, finalcount, 8); /* Should cause SHA1_Transform */ + for (i = 0; i < SHA1_DIGEST_SIZE; i++) { + digest[i] = (uint8_t) + ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); + } + + /* Wipe variables */ + i = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(finalcount, 0, 8); /* SWR */ + +#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */ + SHA1_Transform(context->state, context->buffer); +#endif +} +/* end of sha1.c */ + +/** +* hmac_sha1.c Implements HMAC-SHA1 as of RFC 2202 +* +* from https://github.com/creytiv/re - BSD license +* hmac_sha1 function Copyright (C) 2010 Creytiv.com +*/ +#include +#include + +/** SHA-1 Block size */ +#ifndef SHA_BLOCKSIZE +#define SHA_BLOCKSIZE (64) +#endif + +/** +* Function to compute the digest +* +* @param k Secret key +* @param lk Length of the key in bytes +* @param d Data +* @param ld Length of data in bytes +* @param out Digest output +* @param t Size of digest output +*/ +void hmac_sha1(const uint8_t *k, /* secret key */ + size_t lk, /* length of the key in bytes */ + const uint8_t *d, /* data */ + size_t ld, /* length of data in bytes */ + uint8_t *out, /* output buffer, at least "t" bytes */ + size_t *t) { + SHA_CTX ictx, octx; + uint8_t isha[SHA_DIGEST_LENGTH], osha[SHA_DIGEST_LENGTH]; + uint8_t key[SHA_DIGEST_LENGTH]; + uint8_t buf[SHA_BLOCKSIZE]; + size_t i; + + if (lk > SHA_BLOCKSIZE) { + SHA_CTX tctx; + + SHA1_Init(&tctx); + SHA1_Update(&tctx, k, lk); + SHA1_Final(key, &tctx); + + k = key; + lk = SHA_DIGEST_LENGTH; + } + + /**** Inner Digest ****/ + + SHA1_Init(&ictx); + + /* Pad the key for inner digest */ + for (i = 0; i < lk; ++i) { + buf[i] = k[i] ^ 0x36; + } + for (i = lk; i < SHA_BLOCKSIZE; ++i) { + buf[i] = 0x36; + } + + SHA1_Update(&ictx, buf, SHA_BLOCKSIZE); + SHA1_Update(&ictx, d, ld); + + SHA1_Final(isha, &ictx); + + /**** Outer Digest ****/ + + SHA1_Init(&octx); + + /* Pad the key for outer digest */ + + for (i = 0; i < lk; ++i) { + buf[i] = k[i] ^ 0x5c; + } + for (i = lk; i < SHA_BLOCKSIZE; ++i) { + buf[i] = 0x5c; + } + + SHA1_Update(&octx, buf, SHA_BLOCKSIZE); + SHA1_Update(&octx, isha, SHA_DIGEST_LENGTH); + + SHA1_Final(osha, &octx); + + /* truncate and print the results */ + *t = *t > SHA_DIGEST_LENGTH ? SHA_DIGEST_LENGTH : *t; + memcpy(out, osha, *t); +} +/* end of hmac_sha1.c */ + +int create_totp(char *base32_seed, char *output, uint64_t clocktime, uint64_t interval_duration, uint64_t interval_offset) { + uint8_t seed[10], msg[8], hmac[128]; + uint32_t bits, code, count; + uint64_t clock; + unsigned int seedsize = 12, seedlen; + size_t len; + //SHA_CTX digest; + + if (output == NULL || base32_seed == NULL) return -1; + if (strlen(base32_seed) < 8 ) return -2; + + // decode the seed. + for (count = bits = seedlen = 0; *base32_seed; base32_seed++) { + char *b32_charset = (char *)BASE32_CHARSET; + if (!strchr(b32_charset, *base32_seed)) return -3; // not valid + bits = (bits << 5) | (strchr(b32_charset, *base32_seed) - b32_charset); + count += 5; + + while (count >= 8) { + if (seedlen >= seedsize) return -4; // not enough space + count -= 8; + seed[seedlen++] = bits >> count; + } + } + + clock = (clocktime / interval_duration) + interval_offset; + for (count = 0; count < 8; count++) + msg[7 - count] = clock >> 8*count; + + hmac_sha1((const uint8_t *)seed, (size_t)seedlen, (const uint8_t *)msg, (size_t) sizeof(msg), hmac, &len); + for (code = count = 0; count < 4; count++) + code += hmac[(hmac[len - 1] & 0x0f) + 3 - count] << 8*count; + code &= 0x7fffffff; + snprintf(output, 7, "%06u", code % 1000000); + output[6] = '\0'; + return 6; +} +#endif +// end of foxcode.cpp + diff --git a/foxotpcode.h b/foxotpcode.h new file mode 100644 index 000000000..96e49699a --- /dev/null +++ b/foxotpcode.h @@ -0,0 +1,45 @@ + +#ifndef WSJTX_FOXCODE_H +#define WSJTX_FOXCODE_H + +/* --------------------------- sha1.h defines --------------------------- */ +/* public api for steve reid's public domain SHA-1 implementation */ +/* this file is in the public domain */ + +/** SHA-1 Context */ +typedef struct { + uint32_t state[5]; + /**< Context state */ + uint32_t count[2]; + /**< Counter */ + uint8_t buffer[64]; /**< SHA-1 buffer */ +} SHA1_CTX; + +/** SHA-1 Context (OpenSSL compat) */ +typedef SHA1_CTX SHA_CTX; + +/** SHA-1 Digest size in bytes */ +#define SHA1_DIGEST_SIZE 20 +/** SHA-1 Digest size in bytes (OpenSSL compat) */ +#define SHA_DIGEST_LENGTH SHA1_DIGEST_SIZE + +void SHA1_Init(SHA1_CTX *context); + +void SHA1_Update(SHA1_CTX *context, const void *p, size_t len); + +void SHA1_Final(uint8_t digest[SHA1_DIGEST_SIZE], SHA1_CTX *context); + +/* --------------------------- sha1.h defines (end) --------------------------- */ + +void hmac_sha1(const uint8_t *k, /* secret key */ + size_t lk, /* length of the key in bytes */ + const uint8_t *d, /* data */ + size_t ld, /* length of data in bytes */ + uint8_t *out, /* output buffer, at least "t" bytes */ + size_t *t); + +int create_totp(char *base32_seed, char *output, uint64_t clocktime, uint64_t interval_duration, uint64_t interval_offset); + +#define BASE32_CHARSET "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" + +#endif //WSJTX_FOXCODE_H