diff options
Diffstat (limited to 'src/core/HibpOffline.cpp')
-rw-r--r-- | src/core/HibpOffline.cpp | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/src/core/HibpOffline.cpp b/src/core/HibpOffline.cpp new file mode 100644 index 000000000..ebc5d9a46 --- /dev/null +++ b/src/core/HibpOffline.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2019 KeePassXC Team <team@keepassxc.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "HibpOffline.h" + +#include <QCryptographicHash> +#include <QMultiHash> + +#include "core/Database.h" +#include "core/Group.h" + +namespace HibpOffline +{ + const std::size_t SHA1_BYTES = 20; + + enum class ParseResult + { + Ok, + Eof, + Error + }; + + ParseResult parseHibpLine(QIODevice& input, QByteArray& sha1, int& count) + { + QByteArray hexSha1(SHA1_BYTES * 2, '\0'); + const qint64 rc = input.read(hexSha1.data(), hexSha1.size()); + if (rc == 0) { + return ParseResult::Eof; + } else if (rc != hexSha1.size()) { + return ParseResult::Error; + } + + sha1 = QByteArray::fromHex(hexSha1); + + char c; + if (!input.getChar(&c) || c != ':') { + return ParseResult::Error; + } + + count = 0; + while (true) { + if (!input.getChar(&c)) { + return ParseResult::Error; + } + + if (c == '\n' || c == '\r') { + break; + } + + if (!('0' <= c && c <= '9')) { + return ParseResult::Error; + } + + count *= 10; + count += (c - '0'); + } + + while (1 == input.peek(&c, 1) && (c == '\n' || c == '\r')) { + input.getChar(&c); + } + + return ParseResult::Ok; + } + + bool + report(QSharedPointer<Database> db, QIODevice& hibpInput, QList<QPair<const Entry*, int>>& findings, QString* error) + { + QMultiHash<QByteArray, const Entry*> entriesBySha1; + for (const auto* entry : db->rootGroup()->entriesRecursive()) { + if (!entry->isRecycled()) { + const auto sha1 = QCryptographicHash::hash(entry->password().toUtf8(), QCryptographicHash::Sha1); + entriesBySha1.insert(sha1, entry); + } + } + + QByteArray sha1; + for (quint64 lineNum = 1;; ++lineNum) { + int count = 0; + + switch (parseHibpLine(hibpInput, sha1, count)) { + case ParseResult::Eof: + return true; + case ParseResult::Error: + *error = QObject::tr("HIBP file, line %1: parse error").arg(lineNum); + return false; + default: + break; + } + + for (const auto* entry : entriesBySha1.values(sha1)) { + findings.append({entry, count}); + } + } + } +} // namespace HibpOffline |