pivgeneratecardkeycommand.cpp 7.46 KB
Newer Older
1
/*  commands/pivgeneratecardkeycommand.cpp
2 3 4 5 6 7 8 9 10 11 12 13

    This file is part of Kleopatra, the KDE keymanager
    SPDX-FileCopyrightText: 2020 g10 Code GmbH
    SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>

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

#include "pivgeneratecardkeycommand.h"

#include "cardcommand_p.h"

14
#include "smartcard/pivcard.h"
15 16
#include "smartcard/readerstatus.h"

17 18
#include "commands/authenticatepivcardapplicationcommand.h"

19
#include "dialogs/gencardkeydialog.h"
20 21 22

#include <KLocalizedString>

23 24
#include <gpgme++/error.h>

25 26 27 28 29
#include <gpg-error.h>
#if GPG_ERROR_VERSION_NUMBER >= 0x12400 // 1.36
# define GPG_ERROR_HAS_NO_AUTH
#endif

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
#include "kleopatra_debug.h"

using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::SmartCard;
using namespace GpgME;

class PIVGenerateCardKeyCommand::Private : public CardCommand::Private
{
    friend class ::Kleo::Commands::PIVGenerateCardKeyCommand;
    PIVGenerateCardKeyCommand *q_func() const
    {
        return static_cast<PIVGenerateCardKeyCommand *>(q);
    }
public:
45
    explicit Private(PIVGenerateCardKeyCommand *qq, const std::string &serialNumber, QWidget *p);
46 47 48 49 50
    ~Private();

    void init();

private:
51 52
    void slotDialogAccepted();
    void slotDialogRejected();
53
    void slotResult(const Error &err);
54 55 56

private:
    void authenticate();
57 58
    void authenticationFinished();
    void authenticationCanceled();
59
    void generateKey();
60
    void ensureDialogCreated();
61 62

private:
63
    std::string keyRef;
64
    bool overwriteExistingKey = false;
65 66
    std::string algorithm;
    QPointer<GenCardKeyDialog> dialog;
67
    bool hasBeenCanceled = false;
68 69 70 71 72 73 74 75 76 77 78 79 80 81
};

PIVGenerateCardKeyCommand::Private *PIVGenerateCardKeyCommand::d_func()
{
    return static_cast<Private *>(d.get());
}
const PIVGenerateCardKeyCommand::Private *PIVGenerateCardKeyCommand::d_func() const
{
    return static_cast<const Private *>(d.get());
}

#define d d_func()
#define q q_func()

82 83
PIVGenerateCardKeyCommand::Private::Private(PIVGenerateCardKeyCommand *qq, const std::string &serialNumber, QWidget *p)
    : CardCommand::Private(qq, serialNumber, p)
84
    , dialog()
85 86 87 88 89 90 91 92
{
}

PIVGenerateCardKeyCommand::Private::~Private()
{
    qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::Private::~Private()";
}

93 94
PIVGenerateCardKeyCommand::PIVGenerateCardKeyCommand(const std::string &serialNumber, QWidget *p)
    : CardCommand(new Private(this, serialNumber, p))
95 96 97 98 99 100 101 102 103 104 105 106 107
{
    d->init();
}

void PIVGenerateCardKeyCommand::Private::init()
{
}

PIVGenerateCardKeyCommand::~PIVGenerateCardKeyCommand()
{
    qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::~PIVGenerateCardKeyCommand()";
}

108
void PIVGenerateCardKeyCommand::setKeyRef(const std::string &keyRef)
109
{
110
    d->keyRef = keyRef;
111 112 113 114 115 116
}

void PIVGenerateCardKeyCommand::doStart()
{
    qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::doStart()";

117 118 119
    // check if key exists
    auto pivCard = ReaderStatus::instance()->getCard<PIVCard>(d->serialNumber());
    if (!pivCard) {
120
        d->error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(d->serialNumber())));
121 122 123 124
        d->finished();
        return;
    }

125
    auto existingKey = pivCard->keyInfo(d->keyRef).grip;
126 127 128 129 130
    if (!existingKey.empty()) {
        const QString warningText = i18nc("@info",
            "<p>This card already contains a key in this slot. Continuing will <b>overwrite</b> that key.</p>"
            "<p>If there is no backup the existing key will be irrecoverably lost.</p>") +
            i18n("The existing key has the ID:") + QStringLiteral("<pre>%1</pre>").arg(QString::fromStdString(existingKey)) +
131
            (d->keyRef == PIVCard::keyManagementKeyRef() ?
132 133
             i18n("It will no longer be possible to decrypt past communication encrypted for the existing key.") :
             QString());
134
        const auto choice = KMessageBox::warningContinueCancel(d->parentWidgetOrView(), warningText,
135 136 137 138 139 140 141 142 143
            i18nc("@title:window", "Overwrite existing key"),
            KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous);
        if (choice != KMessageBox::Continue) {
            d->finished();
            return;
        }
        d->overwriteExistingKey = true;
    }

144 145 146
    d->ensureDialogCreated();
    Q_ASSERT(d->dialog);
    d->dialog->show();
147 148
}

149 150 151 152
void PIVGenerateCardKeyCommand::doCancel()
{
}

153 154 155 156
void PIVGenerateCardKeyCommand::Private::authenticate()
{
    qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::authenticate()";

157
    auto cmd = new AuthenticatePIVCardApplicationCommand(serialNumber(), parentWidgetOrView());
158 159 160 161 162
    connect(cmd, &AuthenticatePIVCardApplicationCommand::finished,
            q, [this]() { authenticationFinished(); });
    connect(cmd, &AuthenticatePIVCardApplicationCommand::canceled,
            q, [this]() { authenticationCanceled(); });
    cmd->start();
163 164
}

165
void PIVGenerateCardKeyCommand::Private::authenticationFinished()
166
{
167 168 169
    qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::authenticationFinished()";
    if (!hasBeenCanceled) {
        generateKey();
170
    }
171 172 173 174 175 176 177
}

void PIVGenerateCardKeyCommand::Private::authenticationCanceled()
{
    qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::authenticationCanceled()";
    hasBeenCanceled = true;
    canceled();
178 179 180 181 182 183
}

void PIVGenerateCardKeyCommand::Private::generateKey()
{
    qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::generateKey()";

184 185 186 187 188 189 190
    auto pivCard = ReaderStatus::instance()->getCard<PIVCard>(serialNumber());
    if (!pivCard) {
        error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(serialNumber())));
        finished();
        return;
    }

191 192 193 194 195
    QByteArrayList command;
    command << "SCD GENKEY";
    if (overwriteExistingKey) {
        command << "--force";
    }
196 197 198 199
    if (!algorithm.empty()) {
        command << "--algo=" + QByteArray::fromStdString(algorithm);
    }
    command << "--" << QByteArray::fromStdString(keyRef);
200
    ReaderStatus::mutableInstance()->startSimpleTransaction(pivCard, command.join(' '), q, "slotResult");
201 202
}

203
void PIVGenerateCardKeyCommand::Private::slotResult(const GpgME::Error& err)
204
{
205
    qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::slotResult():"
206 207
                           << err.asString() << "(" << err.code() << ")";
    if (err) {
208
#ifdef GPG_ERROR_HAS_NO_AUTH
209 210 211 212
        if (err.code() == GPG_ERR_NO_AUTH) {
            authenticate();
            return;
        }
213
#endif
214 215 216 217 218 219 220 221 222 223

        error(i18nc("@info", "Generating key failed: %1", QString::fromLatin1(err.asString())),
              i18nc("@title", "Error"));
    } else if (!err.isCanceled()) {
        information(i18nc("@info", "Key successfully generated."), i18nc("@title", "Success"));
        ReaderStatus::mutableInstance()->updateStatus();
    }
    finished();
}

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
void PIVGenerateCardKeyCommand::Private::slotDialogAccepted()
{
    algorithm = dialog->getKeyParams().algorithm;

    // assume that we are already authenticated to the card
    generateKey();
}

void PIVGenerateCardKeyCommand::Private::slotDialogRejected()
{
    finished();
}

void PIVGenerateCardKeyCommand::Private::ensureDialogCreated()
{
    if (dialog) {
        return;
    }

243
    dialog = new GenCardKeyDialog(GenCardKeyDialog::KeyAlgorithm, parentWidgetOrView());
244 245 246 247 248 249 250
    dialog->setAttribute(Qt::WA_DeleteOnClose);
    dialog->setSupportedAlgorithms(PIVCard::supportedAlgorithms(keyRef), "rsa2048");

    connect(dialog, SIGNAL(accepted()), q, SLOT(slotDialogAccepted()));
    connect(dialog, SIGNAL(rejected()), q, SLOT(slotDialogRejected()));
}

251 252 253 254
#undef d
#undef q

#include "moc_pivgeneratecardkeycommand.cpp"