diff --git a/CMakeLists.txt b/CMakeLists.txt index 1179fb27a..a7f5be719 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ project (wsjtx ) set (PROJECT_DESCRIPTION "WSJT-X: Digital Modes for Weak Signal Communications in Amateur Radio") set (CMAKE_PROJECT_DESCRIPTION ${PROJECT_DESCRIPTION}) +set (CMAKE_AUTOUIC ON) # # Local CMake modules and support files @@ -164,7 +165,7 @@ set (WSJT_QT_CONF_DESTINATION ${QT_CONF_DESTINATION} CACHE PATH "Path for the qt if (WSJT_FOX_OTP) set (wsjt_fox_CXXSRCS - foxotpcode.cpp + ) message (STATUS "Including Fox verification code feature") endif () @@ -250,6 +251,7 @@ set (wsjt_qt_CXXSRCS widgets/LazyFillComboBox.cpp widgets/CheckableItemComboBox.cpp widgets/BandComboBox.cpp + otpgenerator.cpp ) set (wsjt_qtmm_CXXSRCS diff --git a/Configuration.hpp b/Configuration.hpp index 4d801b9cf..b8b6fe504 100644 --- a/Configuration.hpp +++ b/Configuration.hpp @@ -9,7 +9,7 @@ #include "models/IARURegions.hpp" #include "Audio/AudioDevice.hpp" #include "Transceiver/Transceiver.hpp" -#include "foxotpcode.h" +#include "otpgenerator.h" #include "pimpl_h.hpp" diff --git a/foxotpcode.cpp b/foxotpcode.cpp deleted file mode 100644 index 765fd0846..000000000 --- a/foxotpcode.cpp +++ /dev/null @@ -1,459 +0,0 @@ -#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 deleted file mode 100644 index 96e49699a..000000000 --- a/foxotpcode.h +++ /dev/null @@ -1,45 +0,0 @@ - -#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 diff --git a/otpgenerator.cpp b/otpgenerator.cpp new file mode 100644 index 000000000..3a4c50501 --- /dev/null +++ b/otpgenerator.cpp @@ -0,0 +1,109 @@ + +#include "otpgenerator.h" + +#include +#include +#include +#include + +// FROM https://github.com/RikudouSage/QtOneTimePassword/ +/* +MIT License + +Copyright (c) 2023 Dominik Chrástecký + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +OTPGenerator::OTPGenerator(QObject *parent) + : QObject{parent} +{ + +} + +QByteArray OTPGenerator::generateHOTP(const QByteArray &rawSecret, quint64 counter, int length) +{ +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + counter = qToBigEndian(counter); +#endif + QByteArray data; + data.reserve(8); + for (int i = 7; i >= 0; --i) { + data.append(counter & 0xff); + counter >>= 8; + } + QMessageAuthenticationCode mac(QCryptographicHash::Sha1); + mac.setKey(rawSecret); + mac.addData(data); + QByteArray hmac = mac.result(); + int offset = hmac.at(hmac.length() - 1) & 0xf; + quint32 truncatedHash = ((hmac.at(offset) & 0x7f) << 24) + | ((hmac.at(offset + 1) & 0xff) << 16) + | ((hmac.at(offset + 2) & 0xff) << 8) + | (hmac.at(offset + 3) & 0xff); + int modulus = int(qPow(10, length)); + return QByteArray::number(truncatedHash % modulus, 10).rightJustified(length, '0'); +} + +QString OTPGenerator::generateHOTP(const QString &secret, quint64 counter, int length) +{ + return generateHOTP(fromBase32(secret), counter, length); +} + +QByteArray OTPGenerator::generateTOTP(const QByteArray &rawSecret, int length) +{ + const qint64 counter = QDateTime::currentDateTime().toMSecsSinceEpoch() / 30000; + return generateHOTP(rawSecret, counter, length); +} + +QString OTPGenerator::generateTOTP(const QString &secret, int length) +{ + return generateTOTP(fromBase32(secret), length); +} + +QString OTPGenerator::generateTOTP(const QString &secret, QDateTime dt, int length) +{ + const qint64 counter = dt.toMSecsSinceEpoch() / 30000; + return generateHOTP(fromBase32(secret), counter, length); +} + +QByteArray OTPGenerator::fromBase32(const QString &input) +{ + QByteArray result; + result.reserve((input.length() * 5 + 7) / 8); + int buffer = 0; + int bitsLeft = 0; + for (int i = 0; i < input.length(); i++) { + int ch = input[i].toLatin1(); + int value; + if (ch >= 'A' && ch <= 'Z') + value = ch - 'A'; + else if (ch >= '2' && ch <= '7') + value = 26 + ch - '2'; + else + continue; + buffer = (buffer << 5) | value; + bitsLeft += 5; + if (bitsLeft >= 8) { + result.append(buffer >> (bitsLeft - 8)); + bitsLeft -= 8; + } + } + return result; +} \ No newline at end of file diff --git a/otpgenerator.h b/otpgenerator.h new file mode 100644 index 000000000..d4d1a5b2b --- /dev/null +++ b/otpgenerator.h @@ -0,0 +1,50 @@ +#ifndef OTPGENERATOR_H +#define OTPGENERATOR_H +/* +MIT License + +Copyright (c) 2023 Dominik Chrástecký + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include + +#define BASE32_CHARSET "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" + +class OTPGenerator : public QObject +{ + Q_OBJECT +public: + explicit OTPGenerator(QObject *parent = nullptr); + + QByteArray generateHOTP(const QByteArray &rawSecret, quint64 counter, int length); + Q_INVOKABLE QString generateHOTP(const QString &secret, quint64 counter, int length); + + QByteArray generateTOTP(const QByteArray &rawSecret, int length); + Q_INVOKABLE QString generateTOTP(const QString &secret, QDateTime dt, int length); + Q_INVOKABLE QString generateTOTP(const QString &secret, int length); +private: + QByteArray fromBase32(const QString &input); + + signals: + +}; + +#endif // OTPGENERATOR_H diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index e5f22b4fc..c00c3a256 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -58,7 +58,7 @@ #include "echograph.h" #include "fastplot.h" #include "fastgraph.h" -#include "foxotpcode.h" +#include "otpgenerator.h" #include "about.h" #include "messageaveraging.h" #include "activeStations.h" @@ -4684,6 +4684,7 @@ void MainWindow::auto_sequence (DecodedText const& message, unsigned start_toler && message_words.at (3).contains (Radio::base_callsign (ui->dxCallEntry->text ()))) { // auto stop to avoid accidental QRM ui->stopTxButton->click (); // halt any transmission + LOG_INFO("STOPPED!"); } else if (m_auto // transmit allowed && ui->cbAutoSeq->isVisible () && ui->cbAutoSeq->isEnabled () && ui->cbAutoSeq->isChecked () // auto-sequencing allowed && ((!m_bCallingCQ // not calling CQ/QRZ @@ -10214,19 +10215,11 @@ QString MainWindow::foxOTPcode() QString code; if (!m_config.OTPSeed().isEmpty()) { - char output[7]; + + OTPGenerator totp; QDateTime dateTime = dateTime.currentDateTime(); - QByteArray ba = m_config.OTPSeed().toLocal8Bit(); - char *c_str = ba.data(); - int return_length; - if (6 == (return_length = create_totp(c_str, output, dateTime.toTime_t(), 30, 0))) - { - code = QString(output); - } else - { - code = "000000"; - LOG_INFO(QString("foxOTPcode: Incorrect return length %1").arg(return_length)); - } + code = totp.generateTOTP(m_config.OTPSeed(), dateTime, 6); + LOG_INFO(QString("foxOTPcode: code is %1").arg(code)); } else { code = "000000"; @@ -11396,11 +11389,12 @@ void MainWindow::sfox_tx() { auto fname{QDir::toNativeSeparators(m_config.writeable_data_dir().absoluteFilePath("sfox_1.dat")).toLocal8Bit()}; QStringList args{fname}; args.append(m_config.my_callsign()); + LOG_INFO(QString("sfox_tx: OTP code is %1").arg(foxOTPcode())); #ifdef FOX_OTP qint32 otp_key = 0; if (m_config.OTPEnabled()) { - LOG_INFO("TOTP: Generating OTP key"); + LOG_INFO(QString("TOTP: Generating OTP key with %1").arg(m_config.OTPSeed())); if (m_config.OTPSeed().length() == 16) { QString output=foxOTPcode(); if (6 == output.length())