p15cardwidget.cpp 8.27 KB
Newer Older
1
2
3
/*  view/p15cardwiget.cpp

    This file is part of Kleopatra, the KDE keymanager
4
    SPDX-FileCopyrightText: 2021 g10 Code GmbH
5
6
7
8
9
10
11
12
13
    SPDX-FileContributor: Andre Heinecke <aheinecke@g10code.com>

    SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "p15cardwidget.h"

#include "smartcard/p15card.h"
#include "smartcard/openpgpcard.h"
14
#include "smartcard/readerstatus.h"
15
16
17
18
19
20
21
22
23
24
25
26

#include <QVBoxLayout>
#include <QGridLayout>
#include <QScrollArea>
#include <QStringList>

#include <KLocalizedString>
#include <KSeparator>

#include <Libkleo/KeyCache>
#include <Libkleo/Formatting>

27
28
29
30
31
32
33
34
35
36
37
#include <QGpgME/Protocol>
#include <QGpgME/KeyListJob>
#include <QGpgME/ImportFromKeyserverJob>
#include <QGpgME/CryptoConfig>
#include <gpgme++/keylistresult.h>
#include <gpgme++/importresult.h>

#include <Libkleo/GnuPG>

#include "kleopatra_debug.h"

38
39
40
41
42
43
44
45
46
using namespace Kleo;
using namespace Kleo::SmartCard;

P15CardWidget::P15CardWidget(QWidget *parent)
    : QWidget(parent)
    , mSerialNumber(new QLabel(this))
    , mVersionLabel(new QLabel(this))
    , mSigFprLabel(new QLabel(this))
    , mEncFprLabel(new QLabel(this))
47
    , mStatusLabel(new QLabel(this))
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
{
    // Set up the scroll area
    auto myLayout = new QVBoxLayout(this);
    myLayout->setContentsMargins(0, 0, 0, 0);

    auto area = new QScrollArea;
    area->setFrameShape(QFrame::NoFrame);
    area->setWidgetResizable(true);
    myLayout->addWidget(area);

    auto areaWidget = new QWidget;
    area->setWidget(areaWidget);

    auto areaVLay = new QVBoxLayout(areaWidget);

    auto cardInfoGrid = new QGridLayout;
    {
        int row = 0;

        // Version and Serialnumber
        cardInfoGrid->addWidget(mVersionLabel, row++, 0, 1, 2);
        mVersionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);

        cardInfoGrid->addWidget(new QLabel(i18n("Serial number:")), row, 0);
        cardInfoGrid->addWidget(mSerialNumber, row++, 1);
        mSerialNumber->setTextInteractionFlags(Qt::TextBrowserInteraction);

        cardInfoGrid->setColumnStretch(cardInfoGrid->columnCount(), 1);
    }
    areaVLay->addLayout(cardInfoGrid);
78
79
    areaVLay->addWidget(mStatusLabel);
    mStatusLabel->setVisible(false);
80
81
82
83
84
85

    areaVLay->addWidget(new KSeparator(Qt::Horizontal));

    areaVLay->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("OpenPGP keys:"))));
    areaVLay->addWidget(mSigFprLabel);
    areaVLay->addWidget(mEncFprLabel);
86
87
    mSigFprLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
    mEncFprLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
88
89
90
91
92
93
94
95
96

    areaVLay->addWidget(new KSeparator(Qt::Horizontal));
    areaVLay->addStretch(1);
}

P15CardWidget::~P15CardWidget()
{
}

97
98
99
100
101
102
103
104
105
106
107
108
109
110
void P15CardWidget::searchPGPFpr(const std::string &fpr)
{
    /* Only do auto import from LDAP */
    auto conf = QGpgME::cryptoConfig();
    Q_ASSERT (conf);
    const QString cmp = engineIsVersion(2, 3, 0) ? QStringLiteral("dirmngr") : QStringLiteral("gpg");
    const auto entry = conf->entry(cmp, QStringLiteral("Keyserver"), QStringLiteral("keyserver"));
    if (!entry || !entry->stringValue().startsWith(QStringLiteral("ldap"))) {
        return;
    }
    mStatusLabel->setText(i18n("Searching in directory service..."));
    mStatusLabel->setVisible(true);
    qCDebug(KLEOPATRA_LOG) << "Looking for:" << fpr.c_str() << "on ldap server";
    QGpgME::KeyListJob *job = QGpgME::openpgp()->keyListJob(true);
111
112
113
114
115
    connect(KeyCache::instance().get(), &KeyCache::keysMayHaveChanged, this, [this, fpr] () {
            qCDebug(KLEOPATRA_LOG) << "Updating key info after changes";
            ReaderStatus::mutableInstance()->updateStatus();
            updateSigKeyWidgets(fpr);
    });
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
    connect(job, &QGpgME::KeyListJob::result, job, [this](GpgME::KeyListResult, std::vector<GpgME::Key> keys, QString, GpgME::Error) {
        if (keys.size() == 1) {
            auto importJob = QGpgME::openpgp()->importFromKeyserverJob();
            qCDebug(KLEOPATRA_LOG) << "Importing: " << keys[0].primaryFingerprint();
            connect(importJob, &QGpgME::ImportFromKeyserverJob::result, importJob, [this](GpgME::ImportResult, QString, GpgME::Error) {
                qCDebug(KLEOPATRA_LOG) << "import job done";
                mStatusLabel->setText(i18n("Automatic import finished."));
            });
            importJob->start(keys);
        } else if (keys.size() > 1) {
            qCDebug(KLEOPATRA_LOG) << "Multiple keys found on server";
            mStatusLabel->setText(i18n("Error multiple keys found on server."));
        } else {
            qCDebug(KLEOPATRA_LOG) << "No key found";
            mStatusLabel->setText(i18n("Key not found in directory service."));
        }
    });
    job->start(QStringList() << QString::fromStdString(fpr));

}

137
void P15CardWidget::updateSigKeyWidgets(const std::string &fpr)
138
{
139
    std::string keyid = fpr;
140
141
    if (!keyid.empty()) {
        QString text = i18n("Signing key:") +
142
            QStringLiteral(" %1")
143
            .arg(Formatting::prettyID(keyid.c_str()));
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
        text += QStringLiteral("<br/><br/>");

        keyid.erase(0, keyid.size() - 16);
        const auto subkeys = KeyCache::instance()->findSubkeysByKeyID({keyid});
        if (subkeys.empty() || subkeys[0].isNull()) {
            text += i18n("Public key not found.");
        } else {

            QStringList toolTips;
            toolTips.reserve(subkeys.size());
            for (const auto &sub: subkeys) {
                // Yep you can have one subkey associated with multiple
                // primary keys.
                toolTips << Formatting::toolTip(sub.parent(), Formatting::Validity |
                        Formatting::ExpiryDates |
                        Formatting::UserIDs |
                        Formatting::Fingerprint);
            }
            text += toolTips.join(QLatin1String("<br/>"));
        }
        mSigFprLabel->setText(text);
    } else {
        mSigFprLabel->setVisible(false);
    }
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
}

void P15CardWidget::setCard(const P15Card *card)
{
    mCardSerialNumber = card->serialNumber();
    mVersionLabel->setText(i18nc("%1 is a smartcard manufacturer", "%1 PKCS#15 card",
                           QString::fromStdString(card->manufacturer())));
    mSerialNumber->setText(card->displaySerialNumber());
    mSerialNumber->setToolTip(QString::fromStdString(card->serialNumber()));

    const auto sigInfo = card->keyInfo(card->signingKeyRef());
    if (!sigInfo.grip.empty()) {
        const auto key = KeyCache::instance()->findSubkeyByKeyGrip(sigInfo.grip, GpgME::OpenPGP).parent();
        if (key.isNull()) {
            qCDebug(KLEOPATRA_LOG) << "Failed to find key for grip:" << sigInfo.grip.c_str();
183
            const auto pgpSigFpr = card->keyFingerprint(OpenPGPCard::pgpSigKeyRef());
184
185
186
187
188
189
190
191
192
            if (!pgpSigFpr.empty()) {
                qCDebug(KLEOPATRA_LOG) << "Should be pgp key:" << pgpSigFpr.c_str();
                searchPGPFpr(pgpSigFpr);
            }
        } else {
            mStatusLabel->setVisible(false);
        }
    }

193
    std::string keyid = card->keyFingerprint(OpenPGPCard::pgpSigKeyRef());
194
    updateSigKeyWidgets(keyid);
195
    keyid = card->keyFingerprint(OpenPGPCard::pgpEncKeyRef());
196
197
    if (!keyid.empty()) {
        mEncFprLabel->setText(i18n("Encryption key:") +
198
199
                QStringLiteral(" %1")
                .arg(Formatting::prettyID(keyid.c_str())));
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
        keyid.erase(0, keyid.size() - 16);
        const auto subkeys = KeyCache::instance()->findSubkeysByKeyID({keyid});
        if (subkeys.empty() || subkeys[0].isNull()) {
            mEncFprLabel->setToolTip(i18n("Public key not found."));
        } else {
            QStringList toolTips;
            toolTips.reserve(subkeys.size());
            for (const auto &sub: subkeys) {
                // Yep you can have one subkey associated with multiple
                // primary keys.
                toolTips << Formatting::toolTip(sub.parent(), Formatting::Validity |
                        Formatting::StorageLocation |
                        Formatting::ExpiryDates |
                        Formatting::UserIDs |
                        Formatting::Fingerprint);

            }
            mEncFprLabel->setToolTip(toolTips.join(QLatin1String("<br/>")));
        }
    } else {
        mEncFprLabel->setVisible(false);
    }

//    updateKeyWidgets(OpenPGPCard::pgpSigKeyRef(), card);
//    updateKeyWidgets(OpenPGPCard::pgpEncKeyRef(), card);
}