diff options
| author | xiphon <xiphon@protonmail.com> | 2020-04-06 16:57:32 +0000 |
|---|---|---|
| committer | xiphon <xiphon@protonmail.com> | 2020-04-12 21:34:22 +0000 |
| commit | 5f27a45910fcba954e652f3214bc489f03262346 (patch) | |
| tree | 151d35b69a466881628096f7130eefdd02758596 /src/qt | |
| parent | 042400b83fdea6c18ea8a7970cb0a8af2070c03a (diff) | |
| download | monzero-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.cpp | 130 | ||||
| -rw-r--r-- | src/qt/updater.h | 55 | ||||
| -rw-r--r-- | src/qt/utils.cpp | 18 | ||||
| -rw-r--r-- | src/qt/utils.h | 1 |
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(); |
