Add initial Steam TOTP support

* Add the concept of custom TOTP encoders, each with potential for custom
  code alphabet, length, step interval and code direction (i.e. reversed)
* Select custom encoder via overload of the digits field of a loaded entry
* Allow selection of custom encoders via the "TOTP Settings" field's
  size, as currently done by KeeTrayTOTP for Steam. Use "S" for the
  short name of the Steam custom encoder
* Allow selection of custom encoders via the "otp" field by appending
  a "&encoder=<name>" field to the URL query. For example,
  "&encoder=steam"
* Update TOTP set-up dialog to permit selection between (default,
  steam, custom) settings.
This commit is contained in:
Joel Smith 2017-11-20 11:01:22 -07:00
parent 1cb91fabb6
commit 8ca52ba8f9
11 changed files with 270 additions and 34 deletions

View file

@ -25,6 +25,8 @@
#include "core/Metadata.h"
#include "totp/totp.h"
#include <QRegularExpression>
const int Entry::DefaultIconNumber = 0;
const int Entry::ResolveMaximumDepth = 10;
@ -332,12 +334,24 @@ void Entry::setTotp(const QString& seed, quint8& step, quint8& digits)
if (digits == 0) {
digits = QTotp::defaultDigits;
}
QString data;
const QTotp::Encoder & enc = QTotp::encoders.value(digits, QTotp::defaultEncoder);
if (m_attributes->hasKey("otp")) {
m_attributes->set("otp", QString("key=%1&step=%2&size=%3").arg(seed).arg(step).arg(digits), true);
data = QString("key=%1&step=%2&size=%3").arg(seed).arg(step).arg(enc.digits == 0 ? digits : enc.digits);
if (!enc.name.isEmpty()) {
data.append("&enocder=").append(enc.name);
}
m_attributes->set("otp", data, true);
} else {
m_attributes->set("TOTP Seed", seed, true);
m_attributes->set("TOTP Settings", QString("%1;%2").arg(step).arg(digits));
if (!enc.shortName.isEmpty()) {
data = QString("%1;%2").arg(step).arg(enc.shortName);
} else {
data = QString("%1;%2").arg(step).arg(digits);
}
m_attributes->set("TOTP Settings", data);
}
}
@ -355,11 +369,16 @@ QString Entry::totpSeed() const
m_data.totpStep = QTotp::defaultStep;
if (m_attributes->hasKey("TOTP Settings")) {
QRegExp rx("(\\d+);(\\d)", Qt::CaseInsensitive, QRegExp::RegExp);
int pos = rx.indexIn(m_attributes->value("TOTP Settings"));
if (pos > -1) {
m_data.totpStep = rx.cap(1).toUInt();
m_data.totpDigits = rx.cap(2).toUInt();
// this regex must be kept in sync with the set of allowed short names QTotp::shortNameToEncoder
QRegularExpression rx(QString("(\\d+);((?:\\d+)|S)"));
QRegularExpressionMatch m = rx.match(m_attributes->value("TOTP Settings"));
if (m.hasMatch()) {
m_data.totpStep = m.captured(1).toUInt();
if (QTotp::shortNameToEncoder.contains(m.captured(2))) {
m_data.totpDigits = QTotp::shortNameToEncoder[m.captured(2)];
} else {
m_data.totpDigits = m.captured(2).toUInt();
}
}
}