Don't add space to invalid TOTP strings

* Fixes #11357
* Introduces validity parameter to TOTP generator function for future use elsewhere in the code base
* Fixes this in preview panel and TOTP dialog
* Disable actions to copy/show TOTP if the settings are invalid
* Show an error message on the TOTP setup dialog if the settings are invalid
* Show a TOTP icon with an x if the settings are invalid
This commit is contained in:
Jonathan White 2025-05-17 17:08:41 -04:00
parent b5f4e98925
commit f62ea95499
20 changed files with 192 additions and 82 deletions

View file

@ -1527,7 +1527,7 @@ void DatabaseWidget::entryActivationSignalReceived(Entry* entry, EntryModel::Mod
}
break;
case EntryModel::Totp:
if (entry->hasTotp()) {
if (entry->hasValidTotp()) {
setClipboardTextAndMinimize(entry->totp());
} else {
setupTotp();
@ -2386,7 +2386,7 @@ bool DatabaseWidget::currentEntryHasTotp()
if (!currentEntry) {
return false;
}
return currentEntry->hasTotp();
return currentEntry->hasValidTotp();
}
#ifdef WITH_XC_SSHAGENT

View file

@ -70,8 +70,7 @@ EntryPreviewWidget::EntryPreviewWidget(QWidget* parent)
m_ui->entryTotpLabel->installEventFilter(this);
connect(m_ui->entryTotpButton, SIGNAL(toggled(bool)), m_ui->entryTotpLabel, SLOT(setVisible(bool)));
connect(m_ui->entryTotpButton, SIGNAL(toggled(bool)), m_ui->entryTotpProgress, SLOT(setVisible(bool)));
connect(m_ui->entryTotpButton, SIGNAL(toggled(bool)), m_ui->entryTotp, SLOT(setVisible(bool)));
connect(m_ui->entryCloseButton, SIGNAL(clicked()), SLOT(hide()));
connect(m_ui->toggleUsernameButton, SIGNAL(clicked(bool)), SLOT(setUsernameVisible(bool)));
connect(m_ui->togglePasswordButton, SIGNAL(clicked(bool)), SLOT(setPasswordVisible(bool)));
@ -260,8 +259,7 @@ void EntryPreviewWidget::updateEntryTotp()
m_ui->entryTotpProgress->setMaximum(m_currentEntry->totpSettings()->step);
updateTotpLabel();
} else {
m_ui->entryTotpLabel->hide();
m_ui->entryTotpProgress->hide();
m_ui->entryTotp->hide();
m_ui->entryTotpButton->setChecked(false);
m_ui->entryTotpLabel->clear();
m_totpTimer.stop();
@ -546,16 +544,23 @@ void EntryPreviewWidget::updateGroupSharingTab()
void EntryPreviewWidget::updateTotpLabel()
{
if (!m_locked && m_currentEntry && m_currentEntry->hasTotp()) {
auto totpCode = m_currentEntry->totp();
totpCode.insert(totpCode.size() / 2, " ");
m_ui->entryTotpLabel->setText(totpCode);
bool isValid = false;
auto totpCode = m_currentEntry->totp(&isValid);
if (isValid) {
totpCode.insert(totpCode.size() / 2, " ");
auto step = m_currentEntry->totpSettings()->step;
auto timeleft = step - (Clock::currentSecondsSinceEpoch() % step);
m_ui->entryTotpProgress->setValue(timeleft);
m_ui->entryTotpProgress->update();
auto step = m_currentEntry->totpSettings()->step;
auto timeleft = step - (Clock::currentSecondsSinceEpoch() % step);
m_ui->entryTotpProgress->setValue(timeleft);
m_ui->entryTotpProgress->update();
} else {
m_totpTimer.stop();
}
m_ui->entryTotpProgress->setVisible(isValid);
m_ui->entryTotpLabel->setText(totpCode);
} else {
m_ui->entryTotpLabel->clear();
m_ui->entryTotp->setVisible(false);
m_totpTimer.stop();
}
}

View file

@ -110,47 +110,66 @@
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
<widget class="QWidget" name="entryTotp" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<item>
<widget class="QLabel" name="entryTotpLabel">
<property name="font">
<font>
<pointsize>10</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="toolTip">
<string>Double click to copy to clipboard</string>
</property>
<property name="text">
<string notr="true">1234567</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="entryTotpProgress">
<property name="maximumSize">
<size>
<width>57</width>
<height>4</height>
</size>
</property>
<property name="value">
<number>50</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="entryTotpLabel">
<property name="font">
<font>
<pointsize>10</pointsize>
<bold>true</bold>
</font>
</property>
<property name="toolTip">
<string>Double click to copy to clipboard</string>
</property>
<property name="text">
<string notr="true">1234567</string>
</property>
<property name="textInteractionFlags">
<set>Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByKeyboard|Qt::TextInteractionFlag::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="entryTotpProgress">
<property name="maximumSize">
<size>
<width>57</width>
<height>4</height>
</size>
</property>
<property name="value">
<number>50</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QToolButton" name="entryTotpButton">

View file

@ -39,6 +39,7 @@ TotpDialog::TotpDialog(QWidget* parent, Entry* entry)
m_step = m_entry->totpSettings()->step;
resetCounter();
updateProgressBar();
updateSeconds();
connect(&m_totpUpdateTimer, SIGNAL(timeout()), this, SLOT(updateProgressBar()));
connect(&m_totpUpdateTimer, SIGNAL(timeout()), this, SLOT(updateSeconds()));
@ -88,10 +89,15 @@ void TotpDialog::updateSeconds()
void TotpDialog::updateTotp()
{
QString totpCode = m_entry->totp();
QString firstHalf = totpCode.left(totpCode.size() / 2);
QString secondHalf = totpCode.mid(totpCode.size() / 2);
m_ui->totpLabel->setText(firstHalf + " " + secondHalf);
bool isValid = false;
QString totpCode = m_entry->totp(&isValid);
if (isValid) {
totpCode.insert(totpCode.size() / 2, " ");
}
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(isValid);
m_ui->progressBar->setVisible(isValid);
m_ui->timerLabel->setVisible(isValid);
m_ui->totpLabel->setText(totpCode);
}
void TotpDialog::resetCounter()

View file

@ -127,5 +127,8 @@ void TotpSetupDialog::init()
m_ui->algorithmComboBox->setCurrentIndex(index);
}
}
auto error = Totp::checkValidSettings(settings);
m_ui->invalidKeyLabel->setVisible(!error.isEmpty());
}
}

View file

@ -14,6 +14,22 @@
<string>Setup TOTP</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="invalidKeyLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Error: secret key is invalid</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
@ -210,6 +226,7 @@
<zorder>customSettingsGroup</zorder>
<zorder>buttonBox</zorder>
<zorder>groupBox</zorder>
<zorder>invalidKeyLabel</zorder>
</widget>
<tabstops>
<tabstop>seedEdit</tabstop>

View file

@ -297,7 +297,7 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const
break;
case Totp:
if (entry->hasTotp()) {
return icons()->icon("totp");
return entry->hasValidTotp() ? icons()->icon("totp") : icons()->icon("totp-invalid");
}
break;
case PasswordStrength: