Fix issues with TOTP

* otp setting is properly loaded and saved (Fix #2671)
* Removing the key from TOTP Setup clears all TOTP
settings for entry
* Santize TOTP key prior to storing in OTP format
This commit is contained in:
Jonathan White 2019-02-01 21:01:51 -05:00
parent d3a424cc74
commit cc932eff30
5 changed files with 67 additions and 30 deletions

View File

@ -427,14 +427,21 @@ QString Entry::totp() const
void Entry::setTotp(QSharedPointer<Totp::Settings> settings)
{
beginUpdate();
m_data.totpSettings = std::move(settings);
auto text = Totp::writeSettings(m_data.totpSettings, title(), username());
if (m_attributes->hasKey(Totp::ATTRIBUTE_OTP)) {
m_attributes->set(Totp::ATTRIBUTE_OTP, text, true);
if (settings->key.isEmpty()) {
m_data.totpSettings.reset();
m_attributes->remove(Totp::ATTRIBUTE_OTP);
m_attributes->remove(Totp::ATTRIBUTE_SEED);
m_attributes->remove(Totp::ATTRIBUTE_SETTINGS);
} else {
m_attributes->set(Totp::ATTRIBUTE_SEED, m_data.totpSettings->key, true);
m_attributes->set(Totp::ATTRIBUTE_SETTINGS, text);
m_data.totpSettings = std::move(settings);
auto text = Totp::writeSettings(m_data.totpSettings, title(), username());
if (m_attributes->hasKey(Totp::ATTRIBUTE_OTP)) {
m_attributes->set(Totp::ATTRIBUTE_OTP, text, true);
} else {
m_attributes->set(Totp::ATTRIBUTE_SEED, m_data.totpSettings->key, true);
m_attributes->set(Totp::ATTRIBUTE_SETTINGS, text);
}
}
endUpdate();
}

View File

@ -137,7 +137,6 @@ void EntryAttributes::remove(const QString& key)
Q_ASSERT(!isDefaultAttribute(key));
if (!m_attributes.contains(key)) {
Q_ASSERT(false);
return;
}

View File

@ -45,16 +45,21 @@ void TotpSetupDialog::saveSettings()
{
QString encShortName;
uint digits = Totp::DEFAULT_DIGITS;
if (m_ui->radio8Digits->isChecked()) {
digits = 8;
} else if (m_ui->radio7Digits->isChecked()) {
digits = 7;
} else if (m_ui->radioSteam->isChecked()) {
uint step = Totp::DEFAULT_STEP;
if (m_ui->radioSteam->isChecked()) {
digits = Totp::STEAM_DIGITS;
encShortName = Totp::STEAM_SHORTNAME;
} else if (m_ui->radioCustom->isChecked()) {
step = m_ui->stepSpinBox->value();
if (m_ui->radio8Digits->isChecked()) {
digits = 8;
} else if (m_ui->radio7Digits->isChecked()) {
digits = 7;
}
}
auto settings = Totp::createSettings(m_ui->seedEdit->text(), digits, m_ui->stepSpinBox->value(), encShortName);
auto settings = Totp::createSettings(m_ui->seedEdit->text(), digits, step, encShortName, m_entry->totpSettings());
m_entry->setTotp(settings);
emit totpUpdated();
close();

View File

@ -55,10 +55,16 @@ QSharedPointer<Totp::Settings> Totp::parseSettings(const QString& rawSettings, c
QUrlQuery query(rawSettings);
if (query.hasQueryItem("key")) {
// Compatibility with "KeeOtp" plugin
// if settings are changed, will convert to semi-colon format
settings->keeOtp = true;
settings->key = query.queryItemValue("key");
settings->digits = query.queryItemValue("size").toUInt();
settings->step = query.queryItemValue("step").toUInt();
settings->digits = DEFAULT_DIGITS;
settings->step = DEFAULT_STEP;
if (query.hasQueryItem("size")) {
settings->digits = query.queryItemValue("size").toUInt();
}
if (query.hasQueryItem("step")) {
settings->step = query.queryItemValue("step").toUInt();
}
} else {
// Parse semi-colon separated values ([step];[digits|S])
auto vars = rawSettings.split(";");
@ -88,12 +94,24 @@ QSharedPointer<Totp::Settings> Totp::parseSettings(const QString& rawSettings, c
return settings;
}
QSharedPointer<Totp::Settings>
Totp::createSettings(const QString& key, const uint digits, const uint step, const QString& encoderShortName)
QSharedPointer<Totp::Settings> Totp::createSettings(const QString& key,
const uint digits,
const uint step,
const QString& encoderShortName,
QSharedPointer<Totp::Settings> prevSettings)
{
bool isCustom = digits != DEFAULT_DIGITS || step != DEFAULT_STEP;
return QSharedPointer<Totp::Settings>(
new Totp::Settings{getEncoderByShortName(encoderShortName), key, false, isCustom, digits, step});
if (prevSettings) {
prevSettings->key = key;
prevSettings->digits = digits;
prevSettings->step = step;
prevSettings->encoder = Totp::getEncoderByShortName(encoderShortName);
prevSettings->custom = isCustom;
return prevSettings;
} else {
return QSharedPointer<Totp::Settings>(
new Totp::Settings{getEncoderByShortName(encoderShortName), key, false, false, isCustom, digits, step});
}
}
QString Totp::writeSettings(const QSharedPointer<Totp::Settings>& settings,
@ -118,15 +136,19 @@ QString Totp::writeSettings(const QSharedPointer<Totp::Settings>& settings,
urlstring.append("&encoder=").append(settings->encoder.name);
}
return urlstring;
}
// Semicolon output [step];[encoder]
if (!settings->encoder.shortName.isEmpty()) {
} else if (settings->keeOtp) {
// KeeOtp output
return QString("key=%1&size=%2&step=%3")
.arg(QString(Base32::sanitizeInput(settings->key.toLatin1())))
.arg(settings->digits)
.arg(settings->step);
} else if (!settings->encoder.shortName.isEmpty()) {
// Semicolon output [step];[encoder]
return QString("%1;%2").arg(settings->step).arg(settings->encoder.shortName);
} else {
// Semicolon output [step];[digits]
return QString("%1;%2").arg(settings->step).arg(settings->digits);
}
// Semicolon output [step];[digits]
return QString("%1;%2").arg(settings->step).arg(settings->digits);
}
QString Totp::generateTotp(const QSharedPointer<Totp::Settings>& settings, const quint64 time)

View File

@ -44,6 +44,7 @@ namespace Totp
Totp::Encoder encoder;
QString key;
bool otpUrl;
bool keeOtp;
bool custom;
uint digits;
uint step;
@ -59,8 +60,11 @@ namespace Totp
static const QString ATTRIBUTE_SETTINGS = "TOTP Settings";
QSharedPointer<Totp::Settings> parseSettings(const QString& rawSettings, const QString& key = {});
QSharedPointer<Totp::Settings>
createSettings(const QString& key, const uint digits, const uint step, const QString& encoderShortName = {});
QSharedPointer<Totp::Settings> createSettings(const QString& key,
const uint digits,
const uint step,
const QString& encoderShortName = {},
QSharedPointer<Totp::Settings> prevSettings = {});
QString writeSettings(const QSharedPointer<Totp::Settings>& settings,
const QString& title = {},
const QString& username = {},