File indexing completed on 2024-06-23 05:13:36
0001 /* commands/certificatetopivcardcommand.cpp 0002 0003 This file is part of Kleopatra, the KDE keymanager 0004 SPDX-FileCopyrightText: 2020 g10 Code GmbH 0005 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include <config-kleopatra.h> 0011 0012 #include "certificatetopivcardcommand.h" 0013 0014 #include "cardcommand_p.h" 0015 0016 #include "commands/authenticatepivcardapplicationcommand.h" 0017 0018 #include "smartcard/pivcard.h" 0019 #include "smartcard/readerstatus.h" 0020 0021 #include "utils/writecertassuantransaction.h" 0022 0023 #include <Libkleo/Compat> 0024 #include <Libkleo/Dn> 0025 #include <Libkleo/Formatting> 0026 #include <Libkleo/KeyCache> 0027 0028 #include <KLocalizedString> 0029 0030 #include <qgpgme/dataprovider.h> 0031 0032 #include <gpgme++/context.h> 0033 0034 #include <gpg-error.h> 0035 #if GPG_ERROR_VERSION_NUMBER >= 0x12400 // 1.36 0036 #define GPG_ERROR_HAS_NO_AUTH 0037 #endif 0038 0039 #include "kleopatra_debug.h" 0040 0041 using namespace Kleo; 0042 using namespace Kleo::Commands; 0043 using namespace Kleo::SmartCard; 0044 using namespace GpgME; 0045 0046 class CertificateToPIVCardCommand::Private : public CardCommand::Private 0047 { 0048 friend class ::Kleo::Commands::CertificateToPIVCardCommand; 0049 CertificateToPIVCardCommand *q_func() const 0050 { 0051 return static_cast<CertificateToPIVCardCommand *>(q); 0052 } 0053 0054 public: 0055 explicit Private(CertificateToPIVCardCommand *qq, const std::string &slot, const std::string &serialno); 0056 ~Private() override; 0057 0058 private: 0059 void start(); 0060 void startCertificateToPIVCard(); 0061 0062 void authenticate(); 0063 void authenticationFinished(); 0064 void authenticationCanceled(); 0065 0066 private: 0067 std::string cardSlot; 0068 Key certificate; 0069 bool hasBeenCanceled = false; 0070 }; 0071 0072 CertificateToPIVCardCommand::Private *CertificateToPIVCardCommand::d_func() 0073 { 0074 return static_cast<Private *>(d.get()); 0075 } 0076 const CertificateToPIVCardCommand::Private *CertificateToPIVCardCommand::d_func() const 0077 { 0078 return static_cast<const Private *>(d.get()); 0079 } 0080 0081 #define q q_func() 0082 #define d d_func() 0083 0084 CertificateToPIVCardCommand::Private::Private(CertificateToPIVCardCommand *qq, const std::string &slot, const std::string &serialno) 0085 : CardCommand::Private(qq, serialno, nullptr) 0086 , cardSlot(slot) 0087 { 0088 } 0089 0090 CertificateToPIVCardCommand::Private::~Private() 0091 { 0092 } 0093 0094 namespace 0095 { 0096 static Key getCertificateToWriteToPIVCard(const std::string &cardSlot, const std::shared_ptr<PIVCard> &card) 0097 { 0098 if (!cardSlot.empty()) { 0099 const std::string cardKeygrip = card->keyInfo(cardSlot).grip; 0100 const auto certificate = KeyCache::instance()->findSubkeyByKeyGrip(cardKeygrip).parent(); 0101 if (certificate.isNull() || certificate.protocol() != GpgME::CMS) { 0102 return Key(); 0103 } 0104 if ((cardSlot == PIVCard::pivAuthenticationKeyRef() && Kleo::keyHasSign(certificate)) 0105 || (cardSlot == PIVCard::cardAuthenticationKeyRef() && Kleo::keyHasSign(certificate)) 0106 || (cardSlot == PIVCard::digitalSignatureKeyRef() && Kleo::keyHasSign(certificate)) 0107 || (cardSlot == PIVCard::keyManagementKeyRef() && Kleo::keyHasEncrypt(certificate))) { 0108 return certificate; 0109 } 0110 } 0111 0112 return Key(); 0113 } 0114 } 0115 0116 void CertificateToPIVCardCommand::Private::start() 0117 { 0118 qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::Private::start()"; 0119 0120 const auto pivCard = SmartCard::ReaderStatus::instance()->getCard<PIVCard>(serialNumber()); 0121 if (!pivCard) { 0122 error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(serialNumber()))); 0123 finished(); 0124 return; 0125 } 0126 0127 certificate = getCertificateToWriteToPIVCard(cardSlot, pivCard); 0128 if (certificate.isNull()) { 0129 error(i18n("Sorry! No suitable certificate to write to this card slot was found.")); 0130 finished(); 0131 return; 0132 } 0133 0134 const QString certificateInfo = i18nc("X.509 certificate DN (validity, created: date)", 0135 "%1 (%2, created: %3)", 0136 DN(certificate.userID(0).id()).prettyDN(), 0137 Formatting::complianceStringShort(certificate), 0138 Formatting::creationDateString(certificate)); 0139 const QString message = i18nc("@info %1 name of card slot, %2 serial number of card", 0140 "<p>Please confirm that you want to write the following certificate to the %1 slot of card %2:</p>" 0141 "<center>%3</center>", 0142 PIVCard::keyDisplayName(cardSlot), 0143 QString::fromStdString(serialNumber()), 0144 certificateInfo); 0145 auto confirmButton = KStandardGuiItem::ok(); 0146 confirmButton.setText(i18nc("@action:button", "Write certificate")); 0147 confirmButton.setToolTip(QString()); 0148 const auto choice = KMessageBox::questionTwoActions(parentWidgetOrView(), 0149 message, 0150 i18nc("@title:window", "Write certificate to card"), 0151 confirmButton, 0152 KStandardGuiItem::cancel(), 0153 QString(), 0154 KMessageBox::Notify | KMessageBox::WindowModal); 0155 if (choice != KMessageBox::ButtonCode::PrimaryAction) { 0156 finished(); 0157 return; 0158 } 0159 0160 startCertificateToPIVCard(); 0161 } 0162 0163 void CertificateToPIVCardCommand::Private::startCertificateToPIVCard() 0164 { 0165 qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::Private::startCertificateToPIVCard()"; 0166 0167 auto ctx = Context::createForProtocol(GpgME::CMS); 0168 QGpgME::QByteArrayDataProvider dp; 0169 Data data(&dp); 0170 const Error err = ctx->exportPublicKeys(certificate.primaryFingerprint(), data); 0171 if (err) { 0172 error(i18nc("@info", "Exporting the certificate failed: %1", Formatting::errorAsString(err))); 0173 finished(); 0174 return; 0175 } 0176 const QByteArray certificateData = dp.data(); 0177 0178 const auto pivCard = SmartCard::ReaderStatus::instance()->getCard<PIVCard>(serialNumber()); 0179 if (!pivCard) { 0180 error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(serialNumber()))); 0181 finished(); 0182 return; 0183 } 0184 0185 const QByteArray command = QByteArrayLiteral("SCD WRITECERT ") + QByteArray::fromStdString(cardSlot); 0186 auto transaction = std::unique_ptr<AssuanTransaction>(new WriteCertAssuanTransaction(certificateData)); 0187 ReaderStatus::mutableInstance()->startTransaction( 0188 pivCard, 0189 command, 0190 q_func(), 0191 [this](const GpgME::Error &err) { 0192 q->certificateToPIVCardDone(err); 0193 }, 0194 std::move(transaction)); 0195 } 0196 0197 void CertificateToPIVCardCommand::Private::authenticate() 0198 { 0199 qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::authenticate()"; 0200 0201 auto cmd = new AuthenticatePIVCardApplicationCommand(serialNumber(), parentWidgetOrView()); 0202 cmd->setAutoResetCardToOpenPGP(false); 0203 connect(cmd, &AuthenticatePIVCardApplicationCommand::finished, q, [this]() { 0204 authenticationFinished(); 0205 }); 0206 connect(cmd, &AuthenticatePIVCardApplicationCommand::canceled, q, [this]() { 0207 authenticationCanceled(); 0208 }); 0209 cmd->start(); 0210 } 0211 0212 void CertificateToPIVCardCommand::Private::authenticationFinished() 0213 { 0214 qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::authenticationFinished()"; 0215 if (!hasBeenCanceled) { 0216 startCertificateToPIVCard(); 0217 } 0218 } 0219 0220 void CertificateToPIVCardCommand::Private::authenticationCanceled() 0221 { 0222 qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::authenticationCanceled()"; 0223 hasBeenCanceled = true; 0224 canceled(); 0225 } 0226 0227 CertificateToPIVCardCommand::CertificateToPIVCardCommand(const std::string &cardSlot, const std::string &serialno) 0228 : CardCommand(new Private(this, cardSlot, serialno)) 0229 { 0230 } 0231 0232 CertificateToPIVCardCommand::~CertificateToPIVCardCommand() 0233 { 0234 qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::~CertificateToPIVCardCommand()"; 0235 } 0236 0237 void CertificateToPIVCardCommand::certificateToPIVCardDone(const Error &err) 0238 { 0239 qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::certificateToPIVCardDone():" << Formatting::errorAsString(err) << "(" << err.code() << ")"; 0240 if (err) { 0241 #ifdef GPG_ERROR_HAS_NO_AUTH 0242 // gpgme 1.13 reports "BAD PIN" instead of "NO AUTH" 0243 if (err.code() == GPG_ERR_NO_AUTH || err.code() == GPG_ERR_BAD_PIN) { 0244 d->authenticate(); 0245 return; 0246 } 0247 #endif 0248 0249 d->error(i18nc("@info", "Writing the certificate to the card failed: %1", Formatting::errorAsString(err))); 0250 } else if (!err.isCanceled()) { 0251 d->success(i18nc("@info", "Writing the certificate to the card succeeded.")); 0252 ReaderStatus::mutableInstance()->updateStatus(); 0253 } 0254 0255 d->finished(); 0256 } 0257 0258 void CertificateToPIVCardCommand::doStart() 0259 { 0260 qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::doStart()"; 0261 0262 d->start(); 0263 } 0264 0265 void CertificateToPIVCardCommand::doCancel() 0266 { 0267 } 0268 0269 #undef q_func 0270 #undef d_func 0271 0272 #include "moc_certificatetopivcardcommand.cpp"