use more QT-specific library for TOTP

This commit is contained in:
Brian Moran 2024-09-07 08:23:02 -07:00
parent f8c31aea3e
commit 4c0eaece7e
7 changed files with 171 additions and 520 deletions

View File

@ -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

View File

@ -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"

View File

@ -1,459 +0,0 @@
#ifdef FOX_OTP
//
// Time based Rolling code based on HMAC-SHA1;
//
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#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 <sreid@sea-to-sky.net>
100% Public Domain
-----------------
Modified 7/98
By James H. Brown <jbrown@burgoyne.com>
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 <process.h> 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 <sreid@sea-to-sky.net>
Still 100% public domain
1- Removed #include <process.h> 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 <Saul.Kravitz@celera.com>
Still 100% PD
Modified to run on Compaq Alpha hardware.
-----------------
Modified 07/2002
By Ralph Giles <giles@artofcode.com>
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 <string.h>
#include <stdint.h>
/** 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

View File

@ -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

109
otpgenerator.cpp Normal file
View File

@ -0,0 +1,109 @@
#include "otpgenerator.h"
#include <QMessageAuthenticationCode>
#include <QtEndian>
#include <QDateTime>
#include <QtMath>
// 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;
}

50
otpgenerator.h Normal file
View File

@ -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 <QObject>
#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

View File

@ -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())