aboutsummaryrefslogtreecommitdiff
path: root/src/qt
diff options
context:
space:
mode:
authorxiphon <xiphon@protonmail.com>2020-04-06 16:57:32 +0000
committerxiphon <xiphon@protonmail.com>2020-04-12 21:34:22 +0000
commit5f27a45910fcba954e652f3214bc489f03262346 (patch)
tree151d35b69a466881628096f7130eefdd02758596 /src/qt
parent042400b83fdea6c18ea8a7970cb0a8af2070c03a (diff)
downloadmonzero-gui-5f27a45910fcba954e652f3214bc489f03262346.tar.gz
monzero-gui-5f27a45910fcba954e652f3214bc489f03262346.tar.xz
monzero-gui-5f27a45910fcba954e652f3214bc489f03262346.zip
'--verify-update', shasum support, OpenPGP signatures verification
Diffstat (limited to 'src/qt')
-rw-r--r--src/qt/updater.cpp130
-rw-r--r--src/qt/updater.h55
-rw-r--r--src/qt/utils.cpp18
-rw-r--r--src/qt/utils.h1
4 files changed, 204 insertions, 0 deletions
diff --git a/src/qt/updater.cpp b/src/qt/updater.cpp
new file mode 100644
index 00000000..5968c2b5
--- /dev/null
+++ b/src/qt/updater.cpp
@@ -0,0 +1,130 @@
+// Copyright (c) 2020, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "updater.h"
+
+#include <openpgp/hash.h>
+
+#include "utils.h"
+
+Updater::Updater()
+{
+ m_maintainers.emplace_back(fileGetContents(":/monero/utils/gpg_keys/binaryfate.asc").toStdString());
+ m_maintainers.emplace_back(fileGetContents(":/monero/utils/gpg_keys/fluffypony.asc").toStdString());
+ m_maintainers.emplace_back(fileGetContents(":/monero/utils/gpg_keys/luigi1111.asc").toStdString());
+}
+
+QPair<QString, QString> Updater::verifySignaturesAndHashSum(
+ const QByteArray &armoredSignedHashes,
+ const QByteArray &secondDetachedSignature,
+ const QString &binaryFilename,
+ const void *binaryData,
+ size_t binarySize) const
+{
+ QString firstSigner;
+ const QString signedMessage = verifySignature(armoredSignedHashes, firstSigner);
+
+ QString secondSigner = verifySignature(
+ epee::span<const uint8_t>(
+ reinterpret_cast<const uint8_t *>(armoredSignedHashes.data()),
+ armoredSignedHashes.size()),
+ openpgp::signature_rsa::from_buffer(epee::span<const uint8_t>(
+ reinterpret_cast<const uint8_t *>(secondDetachedSignature.data()),
+ secondDetachedSignature.size())));
+
+ if (firstSigner == secondSigner)
+ {
+ throw std::runtime_error("both signatures were generated by the same person");
+ }
+
+ const QByteArray signedHash = parseShasumOutput(signedMessage, binaryFilename);
+ const QByteArray calculatedHash = getHash(binaryData, binarySize);
+ if (signedHash != calculatedHash)
+ {
+ throw std::runtime_error("hash sum mismatch");
+ }
+
+ return {firstSigner, secondSigner};
+}
+
+QByteArray Updater::getHash(const void *data, size_t size) const
+{
+ openpgp::hash hasher(openpgp::hash::algorithm::sha256);
+ hasher << epee::span<const uint8_t>(reinterpret_cast<const uint8_t *>(data), size);
+ const std::vector<uint8_t> hash = hasher.finish();
+ return QByteArray(reinterpret_cast<const char *>(&hash[0]), hash.size());
+}
+
+QByteArray Updater::parseShasumOutput(const QString &message, const QString &filename) const
+{
+ for (const auto &line : message.splitRef("\n"))
+ {
+ const auto trimmed = line.trimmed();
+ if (trimmed.endsWith(filename))
+ {
+ const int pos = trimmed.indexOf(' ');
+ if (pos != -1)
+ {
+ return QByteArray::fromHex(trimmed.left(pos).toUtf8());
+ }
+ }
+ else if (trimmed.startsWith(filename))
+ {
+ const int pos = trimmed.lastIndexOf(' ');
+ if (pos != -1)
+ {
+ return QByteArray::fromHex(trimmed.right(trimmed.size() - pos).toUtf8());
+ }
+ }
+ }
+
+ throw std::runtime_error("hash not found");
+}
+
+QString Updater::verifySignature(const QByteArray &armoredSignedMessage, QString &signer) const
+{
+ const std::string messageString = armoredSignedMessage.toStdString();
+ const openpgp::message_armored signedMessage(messageString);
+ signer = verifySignature(signedMessage, openpgp::signature_rsa::from_armored(messageString));
+
+ const epee::span<const uint8_t> message = signedMessage;
+ return QString(QByteArray(reinterpret_cast<const char *>(&message[0]), message.size()));
+}
+
+QString Updater::verifySignature(const epee::span<const uint8_t> data, const openpgp::signature_rsa &signature) const
+{
+ for (const auto &maintainer : m_maintainers)
+ {
+ if (signature.verify(data, maintainer))
+ {
+ return QString::fromStdString(maintainer.user_id());
+ }
+ }
+
+ throw std::runtime_error("not signed by a maintainer");
+}
diff --git a/src/qt/updater.h b/src/qt/updater.h
new file mode 100644
index 00000000..bc06d0c0
--- /dev/null
+++ b/src/qt/updater.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2020, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <QPair>
+
+#include <openpgp/openpgp.h>
+
+class Updater
+{
+public:
+ Updater();
+
+ QPair<QString, QString> verifySignaturesAndHashSum(
+ const QByteArray &armoredSignedHashes,
+ const QByteArray &secondDetachedSignature,
+ const QString &binaryFilename,
+ const void *binaryData,
+ size_t binarySize) const;
+
+private:
+ QByteArray getHash(const void *data, size_t size) 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;
+
+private:
+ std::vector<openpgp::public_key_rsa> m_maintainers;
+};
diff --git a/src/qt/utils.cpp b/src/qt/utils.cpp
index bcf69917..258400c3 100644
--- a/src/qt/utils.cpp
+++ b/src/qt/utils.cpp
@@ -37,6 +37,24 @@ bool fileExists(QString path) {
return check_file.exists() && check_file.isFile();
}
+QByteArray fileGetContents(QString path)
+{
+ QFile file(path);
+ if (!file.open(QFile::ReadOnly))
+ {
+ throw std::runtime_error(QString("failed to open %1").arg(path).toStdString());
+ }
+
+ QByteArray data;
+ data.resize(file.size());
+ if (file.read(data.data(), data.size()) != data.size())
+ {
+ throw std::runtime_error(QString("failed to read %1").arg(path).toStdString());
+ }
+
+ return data;
+}
+
QByteArray fileOpen(QString path) {
QFile file(path);
if(!file.open(QFile::ReadOnly | QFile::Text))
diff --git a/src/qt/utils.h b/src/qt/utils.h
index 17fb44d9..aace5d00 100644
--- a/src/qt/utils.h
+++ b/src/qt/utils.h
@@ -34,6 +34,7 @@
#include <QApplication>
bool fileExists(QString path);
+QByteArray fileGetContents(QString path);
QByteArray fileOpen(QString path);
bool fileWrite(QString path, QString data);
QString getAccountName();