aboutsummaryrefslogtreecommitdiff
path: root/lib/pluginloader/pluginloader.cpp
blob: d4c3dff1e6fa361e5c6bd909f7c5a1bc1483fc31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/*
 * This file is part of smolbote. It's copyrighted by the contributors recorded
 * in the version control history of the file, available from its original
 * location: https://library.iserlohn-fortress.net/aqua/smolbote.git
 *
 * SPDX-License-Identifier: GPL-3.0
 */

#include "pluginloader.h"
#include "publicKey.h"
#include <QFile>
#include <filesystem>
#include <fstream>
#include <openssl/evp.h>
#include <openssl/pem.h>

bool PluginLoader::verify(const char *hashName)
{
    const std::filesystem::path plugin_path(fileName().toStdString());
    if(!std::filesystem::exists(plugin_path)) {
        m_sigError = tr("Plugin doesn't exist.");
        return false;
    }

    if(m_state <= SigIgnored) {
        return true;
    }

    const std::filesystem::path signature_path(fileName().toStdString() + ".sig");
    if(!std::filesystem::is_regular_file(signature_path)) {
        if(m_state >= SigEnforced) {
            m_sigError = tr("A signature is required, but none was found.");
            return false;
        }

        return true;
    }

    auto *bio = BIO_new_mem_buf(publicKey_pem, publicKey_pem_len);
    Q_CHECK_PTR(bio);

    auto *key = PEM_read_bio_PUBKEY(bio, nullptr, nullptr, nullptr);
    Q_CHECK_PTR(key);

    auto *ctx = EVP_MD_CTX_new();
    Q_CHECK_PTR(ctx);

    const auto *md = EVP_get_digestbyname(hashName);
    Q_CHECK_PTR(md);

    int rc = EVP_DigestVerifyInit(ctx, nullptr, md, nullptr, key);
    if(rc != 1) {
        m_sigError = tr("Failed to compute signature (stage=init)");
        return false;
    }

    // read plugin into DigestVerifyUpdate
    {
        std::ifstream plugin(plugin_path, std::ios::binary);
        plugin.unsetf(std::ios::skipws);
        if(!plugin.is_open()) {
            m_sigError = tr("Cannot read plugin during signature check");
            return true;
        }

        const std::size_t buffer_size = 1024;
        std::vector<char> buffer(buffer_size);
        std::size_t sz = 0;
        while(true) {
            sz = static_cast<std::size_t>(plugin.readsome(&buffer.front(), buffer_size));
            if(sz <= 0)
                break;
            rc = EVP_DigestVerifyUpdate(ctx, reinterpret_cast<unsigned char *>(buffer.data()), sz);
            if(rc != 1) {
                m_sigError = tr("Failed to compute signature (stage=update)");
                return false;
            }
        }
    }

    // read signature into DigestVerifyFinal
    {
        std::ifstream signature(signature_path, std::ios::binary);
        signature.unsetf(std::ios::skipws);
        if(!signature.is_open()) {
            m_sigError = tr("Cannot read signature during signature check");
            return false;
        }

        const auto buffer_size = std::filesystem::file_size(signature_path);
        std::vector<unsigned char> buffer(buffer_size);

        buffer.insert(buffer.begin(), std::istream_iterator<unsigned char>(signature), std::istream_iterator<unsigned char>());
        signature.close();

        rc = EVP_DigestVerifyFinal(ctx, buffer.data(), buffer_size);
    }

    if(rc != 1) {
        m_sigError = tr("Signature does not match");
        return false;
    }
    return true;
}