updater: fetch signed hashes from getmonero.org, verify downloads

This commit is contained in:
xiphon
2020-04-14 21:03:15 +00:00
parent 8e4124f06a
commit ea25b71ca6
10 changed files with 131 additions and 33 deletions

View File

@@ -31,6 +31,8 @@
#include <QReadLocker>
#include <QWriteLocker>
#include "updater.h"
namespace
{
@@ -112,10 +114,10 @@ void Downloader::cancel()
m_contents.clear();
}
bool Downloader::get(const QString &url, const QJSValue &callback)
bool Downloader::get(const QString &url, const QString &hash, const QJSValue &callback)
{
auto future = m_scheduler.run(
[this, url]() {
[this, url, hash]() {
DownloaderStateGuard stateGuard(m_active, m_mutex, [this]() {
emit activeChanged();
});
@@ -153,6 +155,19 @@ bool Downloader::get(const QString &url, const QJSValue &callback)
return QJSValueList({"empty response"});
}
try
{
const QByteArray calculatedHash = Updater().getHash(&response[0], response.size());
if (QByteArray::fromHex(hash.toUtf8()) != calculatedHash)
{
return QJSValueList({"hash sum mismatch"});
}
}
catch (const std::exception &e)
{
return QJSValueList({e.what()});
}
{
QWriteLocker locker(&m_mutex);

View File

@@ -44,7 +44,7 @@ public:
~Downloader();
Q_INVOKABLE void cancel();
Q_INVOKABLE bool get(const QString &url, const QJSValue &callback);
Q_INVOKABLE bool get(const QString &url, const QString &hash, const QJSValue &callback);
Q_INVOKABLE bool saveToFile(const QString &path) const;
signals:

View File

@@ -117,6 +117,17 @@ void Network::getJSON(const QString &url, const QJSValue &callback) const
get(url, callback, "application/json; charset=utf-8");
}
std::string Network::get(const QString &url, const QString &contentType /* = {} */) const
{
std::string response;
QString error = get(std::shared_ptr<http_simple_client>(new http_simple_client()), url, response, contentType);
if (!error.isEmpty())
{
throw std::runtime_error(QString("failed to fetch %1: %2").arg(url).arg(error).toStdString());
}
return response;
}
QString Network::get(
std::shared_ptr<http_simple_client> httpClient,
const QString &url,

View File

@@ -77,6 +77,7 @@ public:
Q_INVOKABLE void get(const QString &url, const QJSValue &callback, const QString &contentType = {}) const;
Q_INVOKABLE void getJSON(const QString &url, const QJSValue &callback) const;
std::string get(const QString &url, const QString &contentType = {}) const;
QString get(
std::shared_ptr<epee::net_utils::http::http_simple_client> httpClient,
const QString &url,

View File

@@ -30,6 +30,7 @@
#include <openpgp/hash.h>
#include "network.h"
#include "utils.h"
Updater::Updater()
@@ -39,17 +40,41 @@ Updater::Updater()
m_maintainers.emplace_back(fileGetContents(":/monero/utils/gpg_keys/luigi1111.asc").toStdString());
}
QPair<QString, QString> Updater::verifySignaturesAndHashSum(
QByteArray Updater::fetchSignedHash(
const QString &binaryFilename,
const QByteArray &hashFromDns,
QPair<QString, QString> &signers) const
{
static constexpr const char hashesTxtUrl[] = "https://web.getmonero.org/downloads/hashes.txt";
static constexpr const char hashesTxtSigUrl[] = "https://web.getmonero.org/downloads/hashes.txt.sig";
const Network network;
std::string hashesTxt = network.get(hashesTxtUrl);
std::string hashesTxtSig = network.get(hashesTxtSigUrl);
const QByteArray signedHash = verifyParseSignedHahes(
QByteArray(&hashesTxt[0], hashesTxt.size()),
QByteArray(&hashesTxtSig[0], hashesTxtSig.size()),
binaryFilename,
signers);
if (signedHash != hashFromDns)
{
throw std::runtime_error("DNS hash mismatch");
}
return signedHash;
}
QByteArray Updater::verifyParseSignedHahes(
const QByteArray &armoredSignedHashes,
const QByteArray &secondDetachedSignature,
const QString &binaryFilename,
const void *binaryData,
size_t binarySize) const
QPair<QString, QString> &signers) const
{
QString firstSigner;
const QString signedMessage = verifySignature(armoredSignedHashes, firstSigner);
const QString signedMessage = verifySignature(armoredSignedHashes, signers.first);
QString secondSigner = verifySignature(
signers.second = verifySignature(
epee::span<const uint8_t>(
reinterpret_cast<const uint8_t *>(armoredSignedHashes.data()),
armoredSignedHashes.size()),
@@ -57,19 +82,31 @@ QPair<QString, QString> Updater::verifySignaturesAndHashSum(
reinterpret_cast<const uint8_t *>(secondDetachedSignature.data()),
secondDetachedSignature.size())));
if (firstSigner == secondSigner)
if (signers.first == signers.second)
{
throw std::runtime_error("both signatures were generated by the same person");
}
const QByteArray signedHash = parseShasumOutput(signedMessage, binaryFilename);
return parseShasumOutput(signedMessage, binaryFilename);
}
QPair<QString, QString> Updater::verifySignaturesAndHashSum(
const QByteArray &armoredSignedHashes,
const QByteArray &secondDetachedSignature,
const QString &binaryFilename,
const void *binaryData,
size_t binarySize) const
{
QPair<QString, QString> signers;
const QByteArray signedHash =
verifyParseSignedHahes(armoredSignedHashes, secondDetachedSignature, binaryFilename, signers);
const QByteArray calculatedHash = getHash(binaryData, binarySize);
if (signedHash != calculatedHash)
{
throw std::runtime_error("hash sum mismatch");
}
return {firstSigner, secondSigner};
return signers;
}
QByteArray Updater::getHash(const void *data, size_t size) const

View File

@@ -37,6 +37,11 @@ class Updater
public:
Updater();
QByteArray fetchSignedHash(
const QString &binaryFilename,
const QByteArray &hashFromDns,
QPair<QString, QString> &signers) const;
QByteArray getHash(const void *data, size_t size) const;
QPair<QString, QString> verifySignaturesAndHashSum(
const QByteArray &armoredSignedHashes,
const QByteArray &secondDetachedSignature,
@@ -45,7 +50,11 @@ public:
size_t binarySize) const;
private:
QByteArray getHash(const void *data, size_t size) const;
QByteArray verifyParseSignedHahes(
const QByteArray &armoredSignedHashes,
const QByteArray &secondDetachedSignature,
const QString &binaryFilename,
QPair<QString, QString> &signers) const;
QString verifySignature(const QByteArray &armoredSignedMessage, QString &signer) const;
QString verifySignature(const epee::span<const uint8_t> data, const openpgp::signature_rsa &signature) const;
QByteArray parseShasumOutput(const QString &message, const QString &filename) const;