mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Support KeePass2 TOTP settings
* Fixes #7263 * Also improves handling of custom TOTP settings
This commit is contained in:
parent
2f0160438a
commit
c8fc25ea5c
@ -597,6 +597,11 @@ void Entry::updateTotp()
|
|||||||
m_attributes->value(Totp::ATTRIBUTE_SEED));
|
m_attributes->value(Totp::ATTRIBUTE_SEED));
|
||||||
} else if (m_attributes->contains(Totp::ATTRIBUTE_OTP)) {
|
} else if (m_attributes->contains(Totp::ATTRIBUTE_OTP)) {
|
||||||
m_data.totpSettings = Totp::parseSettings(m_attributes->value(Totp::ATTRIBUTE_OTP));
|
m_data.totpSettings = Totp::parseSettings(m_attributes->value(Totp::ATTRIBUTE_OTP));
|
||||||
|
} else if (m_attributes->contains(Totp::KP2_TOTP_SECRET)) {
|
||||||
|
m_data.totpSettings = Totp::fromKeePass2Totp(m_attributes->value(Totp::KP2_TOTP_SECRET),
|
||||||
|
m_attributes->value(Totp::KP2_TOTP_ALGORITHM),
|
||||||
|
m_attributes->value(Totp::KP2_TOTP_LENGTH),
|
||||||
|
m_attributes->value(Totp::KP2_TOTP_PERIOD));
|
||||||
} else {
|
} else {
|
||||||
m_data.totpSettings.reset();
|
m_data.totpSettings.reset();
|
||||||
}
|
}
|
||||||
|
@ -36,10 +36,11 @@ static QList<Totp::Encoder> totpEncoders{
|
|||||||
|
|
||||||
static Totp::Algorithm getHashTypeByName(const QString& name)
|
static Totp::Algorithm getHashTypeByName(const QString& name)
|
||||||
{
|
{
|
||||||
if (name.compare(QString("SHA512"), Qt::CaseInsensitive) == 0) {
|
auto nameUpper = name.toUpper();
|
||||||
|
if (nameUpper == "SHA512" || nameUpper == "HMAC-SHA-512") {
|
||||||
return Totp::Algorithm::Sha512;
|
return Totp::Algorithm::Sha512;
|
||||||
}
|
}
|
||||||
if (name.compare(QString("SHA256"), Qt::CaseInsensitive) == 0) {
|
if (nameUpper == "SHA256" || nameUpper == "HMAC-SHA-256") {
|
||||||
return Totp::Algorithm::Sha256;
|
return Totp::Algorithm::Sha256;
|
||||||
}
|
}
|
||||||
return Totp::Algorithm::Sha1;
|
return Totp::Algorithm::Sha1;
|
||||||
@ -57,6 +58,30 @@ static QString getNameForHashType(const Totp::Algorithm hashType)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSharedPointer<Totp::Settings>
|
||||||
|
Totp::fromKeePass2Totp(const QString& secret, const QString& algorithm, const QString& length, const QString& period)
|
||||||
|
{
|
||||||
|
// Must have at least a secret to continue
|
||||||
|
if (secret.isEmpty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create default settings
|
||||||
|
auto settings = createSettings(secret);
|
||||||
|
|
||||||
|
if (!algorithm.isEmpty()) {
|
||||||
|
settings->algorithm = getHashTypeByName(algorithm);
|
||||||
|
}
|
||||||
|
if (!length.isEmpty()) {
|
||||||
|
settings->digits = length.toUInt();
|
||||||
|
}
|
||||||
|
if (!period.isEmpty()) {
|
||||||
|
settings->step = period.toUInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
QSharedPointer<Totp::Settings> Totp::parseSettings(const QString& rawSettings, const QString& key)
|
QSharedPointer<Totp::Settings> Totp::parseSettings(const QString& rawSettings, const QString& key)
|
||||||
{
|
{
|
||||||
// Early out if both strings are empty
|
// Early out if both strings are empty
|
||||||
@ -65,7 +90,7 @@ QSharedPointer<Totp::Settings> Totp::parseSettings(const QString& rawSettings, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create default settings
|
// Create default settings
|
||||||
auto settings = createSettings(key, DEFAULT_DIGITS, DEFAULT_STEP);
|
auto settings = createSettings(key);
|
||||||
|
|
||||||
QUrl url(rawSettings);
|
QUrl url(rawSettings);
|
||||||
if (url.isValid() && url.scheme() == "otpauth") {
|
if (url.isValid() && url.scheme() == "otpauth") {
|
||||||
@ -113,6 +138,7 @@ QSharedPointer<Totp::Settings> Totp::parseSettings(const QString& rawSettings, c
|
|||||||
if (vars[1] == STEAM_SHORTNAME) {
|
if (vars[1] == STEAM_SHORTNAME) {
|
||||||
// Explicit steam encoder
|
// Explicit steam encoder
|
||||||
settings->encoder = steamEncoder();
|
settings->encoder = steamEncoder();
|
||||||
|
settings->digits = STEAM_DIGITS;
|
||||||
} else {
|
} else {
|
||||||
// Extract step and digits
|
// Extract step and digits
|
||||||
settings->step = vars[0].toUInt();
|
settings->step = vars[0].toUInt();
|
||||||
@ -126,13 +152,6 @@ QSharedPointer<Totp::Settings> Totp::parseSettings(const QString& rawSettings, c
|
|||||||
settings->digits = qBound(1u, settings->digits, 10u);
|
settings->digits = qBound(1u, settings->digits, 10u);
|
||||||
settings->step = qBound(1u, settings->step, 86400u);
|
settings->step = qBound(1u, settings->step, 86400u);
|
||||||
|
|
||||||
// Detect custom settings, used by setup GUI
|
|
||||||
if (settings->encoder.shortName.isEmpty()
|
|
||||||
&& (settings->digits != DEFAULT_DIGITS || settings->step != DEFAULT_STEP
|
|
||||||
|| settings->algorithm != DEFAULT_ALGORITHM)) {
|
|
||||||
settings->custom = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,9 +162,8 @@ QSharedPointer<Totp::Settings> Totp::createSettings(const QString& key,
|
|||||||
const QString& encoderShortName,
|
const QString& encoderShortName,
|
||||||
const Totp::Algorithm algorithm)
|
const Totp::Algorithm algorithm)
|
||||||
{
|
{
|
||||||
bool isCustom = digits != DEFAULT_DIGITS || step != DEFAULT_STEP || algorithm != DEFAULT_ALGORITHM;
|
|
||||||
return QSharedPointer<Totp::Settings>(
|
return QSharedPointer<Totp::Settings>(
|
||||||
new Totp::Settings{format, getEncoderByShortName(encoderShortName), algorithm, key, isCustom, digits, step});
|
new Totp::Settings{format, getEncoderByShortName(encoderShortName), algorithm, key, digits, step});
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Totp::writeSettings(const QSharedPointer<Totp::Settings>& settings,
|
QString Totp::writeSettings(const QSharedPointer<Totp::Settings>& settings,
|
||||||
@ -200,8 +218,8 @@ QString Totp::generateTotp(const QSharedPointer<Totp::Settings>& settings, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Encoder& encoder = settings->encoder;
|
const Encoder& encoder = settings->encoder;
|
||||||
uint step = settings->custom ? settings->step : encoder.step;
|
uint step = settings->step;
|
||||||
uint digits = settings->custom ? settings->digits : encoder.digits;
|
uint digits = settings->digits;
|
||||||
|
|
||||||
quint64 current;
|
quint64 current;
|
||||||
if (time == 0) {
|
if (time == 0) {
|
||||||
@ -277,6 +295,13 @@ QList<QPair<QString, Totp::Algorithm>> Totp::supportedAlgorithms()
|
|||||||
return algorithms;
|
return algorithms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Totp::hasCustomSettings(const QSharedPointer<Totp::Settings>& settings)
|
||||||
|
{
|
||||||
|
return settings
|
||||||
|
&& (settings->digits != DEFAULT_DIGITS || settings->step != DEFAULT_STEP
|
||||||
|
|| settings->algorithm != DEFAULT_ALGORITHM);
|
||||||
|
}
|
||||||
|
|
||||||
Totp::Encoder& Totp::defaultEncoder()
|
Totp::Encoder& Totp::defaultEncoder()
|
||||||
{
|
{
|
||||||
// The first encoder is always the default
|
// The first encoder is always the default
|
||||||
|
@ -56,7 +56,6 @@ namespace Totp
|
|||||||
Totp::Encoder encoder;
|
Totp::Encoder encoder;
|
||||||
Totp::Algorithm algorithm;
|
Totp::Algorithm algorithm;
|
||||||
QString key;
|
QString key;
|
||||||
bool custom;
|
|
||||||
uint digits;
|
uint digits;
|
||||||
uint step;
|
uint step;
|
||||||
};
|
};
|
||||||
@ -72,10 +71,19 @@ namespace Totp
|
|||||||
static const QString ATTRIBUTE_SEED = "TOTP Seed";
|
static const QString ATTRIBUTE_SEED = "TOTP Seed";
|
||||||
static const QString ATTRIBUTE_SETTINGS = "TOTP Settings";
|
static const QString ATTRIBUTE_SETTINGS = "TOTP Settings";
|
||||||
|
|
||||||
|
// Support for KeePass2 TOTP
|
||||||
|
static const QString KP2_TOTP_SECRET = "TimeOtp-Secret-Base32";
|
||||||
|
static const QString KP2_TOTP_ALGORITHM = "TimeOtp-Algorithm";
|
||||||
|
static const QString KP2_TOTP_LENGTH = "TimeOtp-Length";
|
||||||
|
static const QString KP2_TOTP_PERIOD = "TimeOtp-Period";
|
||||||
|
|
||||||
|
QSharedPointer<Totp::Settings>
|
||||||
|
fromKeePass2Totp(const QString& secret, const QString& algorithm, const QString& length, const QString& period);
|
||||||
|
|
||||||
QSharedPointer<Totp::Settings> parseSettings(const QString& rawSettings, const QString& key = {});
|
QSharedPointer<Totp::Settings> parseSettings(const QString& rawSettings, const QString& key = {});
|
||||||
QSharedPointer<Totp::Settings> createSettings(const QString& key,
|
QSharedPointer<Totp::Settings> createSettings(const QString& key,
|
||||||
const uint digits,
|
const uint digits = DEFAULT_DIGITS,
|
||||||
const uint step,
|
const uint step = DEFAULT_STEP,
|
||||||
const Totp::StorageFormat format = DEFAULT_FORMAT,
|
const Totp::StorageFormat format = DEFAULT_FORMAT,
|
||||||
const QString& encoderShortName = {},
|
const QString& encoderShortName = {},
|
||||||
const Totp::Algorithm algorithm = DEFAULT_ALGORITHM);
|
const Totp::Algorithm algorithm = DEFAULT_ALGORITHM);
|
||||||
@ -86,6 +94,8 @@ namespace Totp
|
|||||||
|
|
||||||
QString generateTotp(const QSharedPointer<Totp::Settings>& settings, const quint64 time = 0ull);
|
QString generateTotp(const QSharedPointer<Totp::Settings>& settings, const quint64 time = 0ull);
|
||||||
|
|
||||||
|
bool hasCustomSettings(const QSharedPointer<Totp::Settings>& settings);
|
||||||
|
|
||||||
QList<QPair<QString, QString>> supportedEncoders();
|
QList<QPair<QString, QString>> supportedEncoders();
|
||||||
QList<QPair<QString, Algorithm>> supportedAlgorithms();
|
QList<QPair<QString, Algorithm>> supportedAlgorithms();
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ TotpExportSettingsDialog::TotpExportSettingsDialog(DatabaseWidget* parent, Entry
|
|||||||
m_timer->start(1000);
|
m_timer->start(1000);
|
||||||
|
|
||||||
const auto totpSettings = entry->totpSettings();
|
const auto totpSettings = entry->totpSettings();
|
||||||
if (totpSettings->custom || !totpSettings->encoder.shortName.isEmpty()) {
|
if (Totp::hasCustomSettings(totpSettings) || !totpSettings->encoder.shortName.isEmpty()) {
|
||||||
m_warningLabel->setWordWrap(true);
|
m_warningLabel->setWordWrap(true);
|
||||||
m_warningLabel->setMargin(5);
|
m_warningLabel->setMargin(5);
|
||||||
m_warningLabel->setText(tr("NOTE: These TOTP settings are custom and may not work with other authenticators.",
|
m_warningLabel->setText(tr("NOTE: These TOTP settings are custom and may not work with other authenticators.",
|
||||||
|
@ -119,7 +119,7 @@ void TotpSetupDialog::init()
|
|||||||
|
|
||||||
if (settings->encoder.shortName == Totp::STEAM_SHORTNAME) {
|
if (settings->encoder.shortName == Totp::STEAM_SHORTNAME) {
|
||||||
m_ui->radioSteam->setChecked(true);
|
m_ui->radioSteam->setChecked(true);
|
||||||
} else if (settings->custom) {
|
} else if (Totp::hasCustomSettings(settings)) {
|
||||||
m_ui->radioCustom->setChecked(true);
|
m_ui->radioCustom->setChecked(true);
|
||||||
m_ui->digitsSpinBox->setValue(settings->digits);
|
m_ui->digitsSpinBox->setValue(settings->digits);
|
||||||
int index = m_ui->algorithmComboBox->findData(settings->algorithm);
|
int index = m_ui->algorithmComboBox->findData(settings->algorithm);
|
||||||
|
@ -40,11 +40,11 @@ void TestTotp::testParseSecret()
|
|||||||
auto settings = Totp::parseSettings(secret);
|
auto settings = Totp::parseSettings(secret);
|
||||||
QVERIFY(!settings.isNull());
|
QVERIFY(!settings.isNull());
|
||||||
QCOMPARE(settings->key, QString("HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ"));
|
QCOMPARE(settings->key, QString("HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ"));
|
||||||
QCOMPARE(settings->custom, false);
|
|
||||||
QCOMPARE(settings->format, Totp::StorageFormat::OTPURL);
|
QCOMPARE(settings->format, Totp::StorageFormat::OTPURL);
|
||||||
QCOMPARE(settings->digits, 6u);
|
QCOMPARE(settings->digits, 6u);
|
||||||
QCOMPARE(settings->step, 30u);
|
QCOMPARE(settings->step, 30u);
|
||||||
QCOMPARE(settings->algorithm, Totp::Algorithm::Sha1);
|
QCOMPARE(settings->algorithm, Totp::Algorithm::Sha1);
|
||||||
|
QCOMPARE(Totp::hasCustomSettings(settings), false);
|
||||||
|
|
||||||
// OTP URL with non-default hash type
|
// OTP URL with non-default hash type
|
||||||
secret = "otpauth://totp/"
|
secret = "otpauth://totp/"
|
||||||
@ -53,11 +53,11 @@ void TestTotp::testParseSecret()
|
|||||||
settings = Totp::parseSettings(secret);
|
settings = Totp::parseSettings(secret);
|
||||||
QVERIFY(!settings.isNull());
|
QVERIFY(!settings.isNull());
|
||||||
QCOMPARE(settings->key, QString("HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ"));
|
QCOMPARE(settings->key, QString("HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ"));
|
||||||
QCOMPARE(settings->custom, true);
|
|
||||||
QCOMPARE(settings->format, Totp::StorageFormat::OTPURL);
|
QCOMPARE(settings->format, Totp::StorageFormat::OTPURL);
|
||||||
QCOMPARE(settings->digits, 6u);
|
QCOMPARE(settings->digits, 6u);
|
||||||
QCOMPARE(settings->step, 30u);
|
QCOMPARE(settings->step, 30u);
|
||||||
QCOMPARE(settings->algorithm, Totp::Algorithm::Sha512);
|
QCOMPARE(settings->algorithm, Totp::Algorithm::Sha512);
|
||||||
|
QCOMPARE(Totp::hasCustomSettings(settings), true);
|
||||||
|
|
||||||
// Max TOTP step of 24-hours
|
// Max TOTP step of 24-hours
|
||||||
secret.replace("period=30", "period=90000");
|
secret.replace("period=30", "period=90000");
|
||||||
@ -70,33 +70,33 @@ void TestTotp::testParseSecret()
|
|||||||
settings = Totp::parseSettings(secret);
|
settings = Totp::parseSettings(secret);
|
||||||
QVERIFY(!settings.isNull());
|
QVERIFY(!settings.isNull());
|
||||||
QCOMPARE(settings->key, QString("HXDMVJECJJWSRBY="));
|
QCOMPARE(settings->key, QString("HXDMVJECJJWSRBY="));
|
||||||
QCOMPARE(settings->custom, true);
|
|
||||||
QCOMPARE(settings->format, Totp::StorageFormat::KEEOTP);
|
QCOMPARE(settings->format, Totp::StorageFormat::KEEOTP);
|
||||||
QCOMPARE(settings->digits, 8u);
|
QCOMPARE(settings->digits, 8u);
|
||||||
QCOMPARE(settings->step, 25u);
|
QCOMPARE(settings->step, 25u);
|
||||||
QCOMPARE(settings->algorithm, Totp::Algorithm::Sha256);
|
QCOMPARE(settings->algorithm, Totp::Algorithm::Sha256);
|
||||||
|
QCOMPARE(Totp::hasCustomSettings(settings), true);
|
||||||
|
|
||||||
// Semi-colon delineated "TOTP Settings"
|
// Semi-colon delineated "TOTP Settings"
|
||||||
secret = "gezdgnbvgy3tqojqgezdgnbvgy3tqojq";
|
secret = "gezdgnbvgy3tqojqgezdgnbvgy3tqojq";
|
||||||
settings = Totp::parseSettings("30;8", secret);
|
settings = Totp::parseSettings("30;8", secret);
|
||||||
QVERIFY(!settings.isNull());
|
QVERIFY(!settings.isNull());
|
||||||
QCOMPARE(settings->key, QString("gezdgnbvgy3tqojqgezdgnbvgy3tqojq"));
|
QCOMPARE(settings->key, QString("gezdgnbvgy3tqojqgezdgnbvgy3tqojq"));
|
||||||
QCOMPARE(settings->custom, true);
|
|
||||||
QCOMPARE(settings->format, Totp::StorageFormat::LEGACY);
|
QCOMPARE(settings->format, Totp::StorageFormat::LEGACY);
|
||||||
QCOMPARE(settings->digits, 8u);
|
QCOMPARE(settings->digits, 8u);
|
||||||
QCOMPARE(settings->step, 30u);
|
QCOMPARE(settings->step, 30u);
|
||||||
QCOMPARE(settings->algorithm, Totp::Algorithm::Sha1);
|
QCOMPARE(settings->algorithm, Totp::Algorithm::Sha1);
|
||||||
|
QCOMPARE(Totp::hasCustomSettings(settings), true);
|
||||||
|
|
||||||
// Bare secret (no "TOTP Settings" attribute)
|
// Bare secret (no "TOTP Settings" attribute)
|
||||||
secret = "gezdgnbvgy3tqojqgezdgnbvgy3tqojq";
|
secret = "gezdgnbvgy3tqojqgezdgnbvgy3tqojq";
|
||||||
settings = Totp::parseSettings("", secret);
|
settings = Totp::parseSettings("", secret);
|
||||||
QVERIFY(!settings.isNull());
|
QVERIFY(!settings.isNull());
|
||||||
QCOMPARE(settings->key, QString("gezdgnbvgy3tqojqgezdgnbvgy3tqojq"));
|
QCOMPARE(settings->key, QString("gezdgnbvgy3tqojqgezdgnbvgy3tqojq"));
|
||||||
QCOMPARE(settings->custom, false);
|
|
||||||
QCOMPARE(settings->format, Totp::StorageFormat::LEGACY);
|
QCOMPARE(settings->format, Totp::StorageFormat::LEGACY);
|
||||||
QCOMPARE(settings->digits, 6u);
|
QCOMPARE(settings->digits, 6u);
|
||||||
QCOMPARE(settings->step, 30u);
|
QCOMPARE(settings->step, 30u);
|
||||||
QCOMPARE(settings->algorithm, Totp::Algorithm::Sha1);
|
QCOMPARE(settings->algorithm, Totp::Algorithm::Sha1);
|
||||||
|
QCOMPARE(Totp::hasCustomSettings(settings), false);
|
||||||
|
|
||||||
// Blank settings (expected failure)
|
// Blank settings (expected failure)
|
||||||
settings = Totp::parseSettings("", "");
|
settings = Totp::parseSettings("", "");
|
||||||
@ -122,7 +122,6 @@ void TestTotp::testTotpCode()
|
|||||||
|
|
||||||
// Test 8 digit TOTP (custom)
|
// Test 8 digit TOTP (custom)
|
||||||
settings->digits = 8;
|
settings->digits = 8;
|
||||||
settings->custom = true;
|
|
||||||
time = 1111111111;
|
time = 1111111111;
|
||||||
QCOMPARE(Totp::generateTotp(settings, time), QString("14050471"));
|
QCOMPARE(Totp::generateTotp(settings, time), QString("14050471"));
|
||||||
|
|
||||||
@ -132,11 +131,19 @@ void TestTotp::testTotpCode()
|
|||||||
|
|
||||||
void TestTotp::testSteamTotp()
|
void TestTotp::testSteamTotp()
|
||||||
{
|
{
|
||||||
|
// Legacy parsing
|
||||||
|
auto settings = Totp::parseSettings("30;S", "63BEDWCQZKTQWPESARIERL5DTTQFCJTK");
|
||||||
|
QCOMPARE(settings->key, QString("63BEDWCQZKTQWPESARIERL5DTTQFCJTK"));
|
||||||
|
QCOMPARE(settings->encoder.shortName, Totp::STEAM_SHORTNAME);
|
||||||
|
QCOMPARE(settings->format, Totp::StorageFormat::LEGACY);
|
||||||
|
QCOMPARE(settings->digits, Totp::STEAM_DIGITS);
|
||||||
|
QCOMPARE(settings->step, 30u);
|
||||||
|
|
||||||
// OTP URL Parsing
|
// OTP URL Parsing
|
||||||
QString secret = "otpauth://totp/"
|
QString secret = "otpauth://totp/"
|
||||||
"test:test@example.com?secret=63BEDWCQZKTQWPESARIERL5DTTQFCJTK&issuer=Valve&algorithm="
|
"test:test@example.com?secret=63BEDWCQZKTQWPESARIERL5DTTQFCJTK&issuer=Valve&algorithm="
|
||||||
"SHA1&digits=5&period=30&encoder=steam";
|
"SHA1&digits=5&period=30&encoder=steam";
|
||||||
auto settings = Totp::parseSettings(secret);
|
settings = Totp::parseSettings(secret);
|
||||||
|
|
||||||
QCOMPARE(settings->key, QString("63BEDWCQZKTQWPESARIERL5DTTQFCJTK"));
|
QCOMPARE(settings->key, QString("63BEDWCQZKTQWPESARIERL5DTTQFCJTK"));
|
||||||
QCOMPARE(settings->encoder.shortName, Totp::STEAM_SHORTNAME);
|
QCOMPARE(settings->encoder.shortName, Totp::STEAM_SHORTNAME);
|
||||||
@ -177,3 +184,39 @@ void TestTotp::testEntryHistory()
|
|||||||
QVERIFY(!entry.hasTotp());
|
QVERIFY(!entry.hasTotp());
|
||||||
QCOMPARE(entry.historyItems().size(), 3);
|
QCOMPARE(entry.historyItems().size(), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestTotp::testKeePass2()
|
||||||
|
{
|
||||||
|
Entry entry;
|
||||||
|
auto attr = entry.attributes();
|
||||||
|
|
||||||
|
// Default settings
|
||||||
|
attr->set("TimeOtp-Secret-Base32", "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ");
|
||||||
|
|
||||||
|
auto settings = entry.totpSettings();
|
||||||
|
QVERIFY(settings);
|
||||||
|
QCOMPARE(settings->key, QString("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"));
|
||||||
|
QCOMPARE(settings->algorithm, Totp::Algorithm::Sha1);
|
||||||
|
QCOMPARE(settings->digits, 6u);
|
||||||
|
QCOMPARE(settings->step, 30u);
|
||||||
|
QCOMPARE(Totp::hasCustomSettings(settings), false);
|
||||||
|
|
||||||
|
// Custom settings
|
||||||
|
attr->set("TimeOtp-Algorithm", "HMAC-SHA-256");
|
||||||
|
attr->set("TimeOtp-Length", "8");
|
||||||
|
|
||||||
|
settings = entry.totpSettings();
|
||||||
|
QVERIFY(settings);
|
||||||
|
QCOMPARE(settings->key, QString("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"));
|
||||||
|
QCOMPARE(settings->algorithm, Totp::Algorithm::Sha256);
|
||||||
|
QCOMPARE(settings->digits, 8u);
|
||||||
|
QCOMPARE(settings->step, 30u);
|
||||||
|
QCOMPARE(Totp::hasCustomSettings(settings), true);
|
||||||
|
|
||||||
|
// Base64 and other encodings are not supported
|
||||||
|
attr->remove("TimeOtp-Secret-Base32");
|
||||||
|
attr->set("TimeOtp-Secret-Base64", "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ");
|
||||||
|
|
||||||
|
settings = entry.totpSettings();
|
||||||
|
QVERIFY(!settings);
|
||||||
|
}
|
||||||
|
@ -31,6 +31,7 @@ private slots:
|
|||||||
void testTotpCode();
|
void testTotpCode();
|
||||||
void testSteamTotp();
|
void testSteamTotp();
|
||||||
void testEntryHistory();
|
void testEntryHistory();
|
||||||
|
void testKeePass2();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_TESTTOTP_H
|
#endif // KEEPASSX_TESTTOTP_H
|
||||||
|
Loading…
Reference in New Issue
Block a user