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/updater.cpp | |
| 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/updater.cpp')
| -rw-r--r-- | src/qt/updater.cpp | 130 |
1 files changed, 130 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"); +} |
