From 110e5e4fb33885891f74e3663032b4846ccbc23c Mon Sep 17 00:00:00 2001 From: SET Date: Sun, 22 Nov 2020 14:22:08 +0100 Subject: [PATCH] Allow revoking key certifications. Certified user identities in keys can be revoked using the same popup for certification. Requires GnuPG 2.2.24 (not available in distro's repository, not fully tested to date). --- GpgMEWorker.cpp | 28 +++++++++++++++++++++ GpgMEWorker.h | 13 ++++++++++ KeyEdit.cpp | 42 ++++++++++++++++++++++++------- PopupCertifyUserId.cpp | 57 +++++++++++++++++++++++++++++++----------- PopupCertifyUserId.h | 55 ++++++++++++++++++++++++++++++++-------- WTAPPROOT/K7/K7.xml | 4 ++- WTAPPROOT/K7/K7_fr.xml | 4 ++- 7 files changed, 168 insertions(+), 35 deletions(-) diff --git a/GpgMEWorker.cpp b/GpgMEWorker.cpp index a1919ce..114ea2f 100644 --- a/GpgMEWorker.cpp +++ b/GpgMEWorker.cpp @@ -145,6 +145,34 @@ const Error GpgMEWorker::CertifyKey(const char* fprSigningKey, return e; } +const Error GpgMEWorker::RevokeKeyCertifications(const char* fprSigningKey, + const char* fprKeyToSign, + vector& userIDsToRevoke, + const string& passphrase) +{ + Error e; + Key signingKey = FindKey(fprSigningKey, e, true); + if (e.code() != 0) + return e; + e = m_ctx->addSigningKey(signingKey); // +++ + if (e.code() != 0) + return e; + Key keyToSign = FindKey(fprKeyToSign, e, false); + if (e.code() != 0) + return e; + + m_ctx->setPinentryMode(Context::PinentryMode::PinentryLoopback); + if (m_ppp == NULL) + m_ppp = new LoopbackPassphraseProvider(); + m_ppp->SetPassphrase(passphrase); + m_ctx->setPassphraseProvider(m_ppp); + + e = m_ctx->revokeSignature(keyToSign, signingKey, userIDsToRevoke); + m_ctx->clearSigningKeys(); + + return e; +} + const Error GpgMEWorker::SetSubkeyExpiryTime(const char* keyFpr, const char* subkeyFpr, const string& passphrase, diff --git a/GpgMEWorker.h b/GpgMEWorker.h index 10c8c44..024a9af 100644 --- a/GpgMEWorker.h +++ b/GpgMEWorker.h @@ -77,6 +77,19 @@ public: const char * fprKeyToSign, vector& userIDsToSign, int options, const string& passphrase); + /** + * Revoke UserID certifications. + * \n Requires GnuPG >= 2.2.24 + * @param fprSigningKey + * @param fprKeyToSign + * @param userIDsToRevoke : vector of ::UserID + * @param passphrase + * @return + */ + const Error RevokeKeyCertifications(const char * fprSigningKey, + const char * fprKeyToSign, + vector& userIDsToRevoke, + const string& passphrase); /** * Sets the expiry time of a single subkey. Requires GPGME >= 1.15.0. * \n If no subkey is found (wrong fpr), the expiry time of key is set diff --git a/KeyEdit.cpp b/KeyEdit.cpp index 0096ef1..b295b6e 100644 --- a/KeyEdit.cpp +++ b/KeyEdit.cpp @@ -153,19 +153,43 @@ void KeyEdit::CertifyKey() } const WString signingKey = m_popupUid->GetSelectedKey(); const WString keyToSign = m_popupUid->GetKeyToSign(); - int options = m_popupUid->GetCertifyOptions(); GpgMEWorker gpgWorker; - GpgME::Error e = gpgWorker.CertifyKey(signingKey.toUTF8().c_str(), - keyToSign.toUTF8().c_str(), - uidsToSign, options, - m_popupUid->GetPassphrase()); + GpgME::Error e; + if (m_popupUid->WhatToDo() == PopupCertifyUserId::CertifyUid) + { + int options = m_popupUid->GetCertifyOptions(); + e = gpgWorker.CertifyKey(signingKey.toUTF8().c_str(), + keyToSign.toUTF8().c_str(), + uidsToSign, options, + m_popupUid->GetPassphrase()); + } + else + { + vector uidsToRevoke + = m_popupUid->GetUidsToRevokeCertification(); + e = gpgWorker.RevokeKeyCertifications(signingKey.toUTF8().c_str(), + keyToSign.toUTF8().c_str(), + uidsToRevoke, + m_popupUid->GetPassphrase()); + } if (e.code() != 0) { - m_owner->m_tmwMessage->SetText(TR("CertificationFailure")); - m_popupUid->ShowPassphrase(true); - return; + if (m_popupUid->WhatToDo() == PopupCertifyUserId::CertifyUid) + { + m_owner->m_tmwMessage->SetText(e.asString()); + m_popupUid->ShowPassphrase(true); + return; + } + else { + m_owner->m_tmwMessage->SetText(e.asString()); + m_popupUid->ShowPassphrase(true); + return; + } } - m_owner->m_tmwMessage->SetText(TR("CertificationSuccess")); + if (m_popupUid->WhatToDo() == PopupCertifyUserId::CertifyUid) + m_owner->m_tmwMessage->SetText(TR("CertificationSuccess")); + else + m_owner->m_tmwMessage->SetText(TR("RevocationSuccess")); m_popupUid->ShowPassphrase(false); m_owner->DisplayUids(keyToSign); } diff --git a/PopupCertifyUserId.cpp b/PopupCertifyUserId.cpp index d942ca1..b6d0b12 100644 --- a/PopupCertifyUserId.cpp +++ b/PopupCertifyUserId.cpp @@ -12,7 +12,9 @@ #include "Tools.h" #include #include +#include #include +#include using namespace std; @@ -63,10 +65,10 @@ void PopupCertifyUserId::Create(vector& privateKeys, m_hblPreferences->addLayout(unique_ptr (m_vblEmail)); PresentEmail(); // Column 1 + m_gbOptions = new WGroupBox(TR("Options")); WVBoxLayout * vblOptions = new WVBoxLayout(); - m_hblPreferences->addLayout(unique_ptr (vblOptions)); - WText * lblOptions = new WText(TR("Options")); - vblOptions->addWidget(unique_ptr (lblOptions)); + m_gbOptions->setLayout(unique_ptr (vblOptions)); + m_hblPreferences->addWidget(unique_ptr (m_gbOptions)); m_cbOptionExportable = new WCheckBox(TR("ExportableCertification")); m_cbOptionExportable->setToolTip(TR("OneWayHint")); vblOptions->addWidget(unique_ptr (m_cbOptionExportable)); @@ -74,7 +76,7 @@ void PopupCertifyUserId::Create(vector& privateKeys, m_cbOptionNonRevocable->setToolTip(TR("OneWayHint")); vblOptions->addWidget(unique_ptr (m_cbOptionNonRevocable)); /*m_cbOptionTrust = new WCheckBox(TR("TrustCertification")); - vblOptions->addWidget(unique_ptr (m_cbOptionTrust));*/ + gbOptions->addWidget(unique_ptr (m_cbOptionTrust));*/ WHBoxLayout * hblPassphrase = new WHBoxLayout(); m_lblPassphrase = new WText(TR("Passphrase")); @@ -84,6 +86,18 @@ void PopupCertifyUserId::Create(vector& privateKeys, hblPassphrase->addWidget(unique_ptr (m_lePassphrase), 1); vblMain->addLayout(unique_ptr (hblPassphrase)); + WHBoxLayout * hblWhat = new WHBoxLayout(); + WRadioButton * rbCertifyUid = new WRadioButton(TR("CertifyUid")); + hblWhat->addWidget(unique_ptr (rbCertifyUid)); + WRadioButton * rbRevokeCertification = + new WRadioButton(TR("RevokeUidCertification")); + hblWhat->addWidget(unique_ptr (rbRevokeCertification)); + m_bgWhat = make_shared(); + m_bgWhat->addButton(rbCertifyUid, What::CertifyUid); + m_bgWhat->addButton(rbRevokeCertification, What::RevokeUidCertification); + m_bgWhat->setCheckedButton(rbCertifyUid); + vblMain->addLayout(unique_ptr (hblWhat)); + WHBoxLayout * hblButtons = new WHBoxLayout(); WPushButton * btnClose = new WPushButton(TR("Close")); hblButtons->addWidget(unique_ptr (btnClose)); @@ -99,6 +113,7 @@ void PopupCertifyUserId::Create(vector& privateKeys, m_cbOptionNonRevocable->unChecked().connect(std::bind(&PopupCertifyUserId::OnCertifyOptionUnChecked, this, 2)); // m_cbOptionTrust->unChecked().connect(std::bind(&PopupCertifyUserId::OnCertifyOptionUnChecked, this, 4)); btnClose->clicked().connect(this, &WPopupWidget::hide); + m_bgWhat->checkedChanged().connect(this, &PopupCertifyUserId::OnButtonGroupWhat); } void PopupCertifyUserId::FillPrivateKeyComboBox(vector& privateKeys) @@ -175,8 +190,8 @@ void PopupCertifyUserId::PresentEmail() WCheckBox * cbEmail = new WCheckBox(it->email()); m_vblEmail->addWidget(unique_ptr (cbEmail)); cbEmail->setId(std::to_string(id)); - cbEmail->checked().connect(std::bind(&PopupCertifyUserId::OnEmailChecked, this, cbEmail)); - cbEmail->unChecked().connect(std::bind(&PopupCertifyUserId::OnEmailUnChecked, this, cbEmail)); + cbEmail->checked().connect(std::bind(&PopupCertifyUserId::OnEmailChecked, this, cbEmail, (*it))); + cbEmail->unChecked().connect(std::bind(&PopupCertifyUserId::OnEmailUnChecked, this, cbEmail, (*it))); id++; } } @@ -220,25 +235,32 @@ void PopupCertifyUserId::ShowPassphrase(bool show) } } -void PopupCertifyUserId::OnEmailChecked(WCheckBox* cb) +void PopupCertifyUserId::OnEmailChecked(WCheckBox* cb, GpgME::UserID& uid) { int id = Tools::ToInt(cb->id()); m_uidsToSign.push_back(id); + m_uidsToRevokeCertification.push_back(uid); } -void PopupCertifyUserId::OnEmailUnChecked(WCheckBox* cb) +void PopupCertifyUserId::OnEmailUnChecked(WCheckBox* cb, GpgME::UserID& uid) { - // Any tip to locate a known value in a vector without iterating over? const uint id = Tools::ToInt(cb->id()); - vector::iterator it; - for (it = m_uidsToSign.begin(); it != m_uidsToSign.end(); it++) + vector::iterator it = + std::find(m_uidsToSign.begin(), m_uidsToSign.end(), id); + if (it != m_uidsToSign.end()) + m_uidsToSign.erase(it); + + vector::iterator uit; + for (uit = m_uidsToRevokeCertification.begin(); + uit != m_uidsToRevokeCertification.end(); uit++) { - if ((*it) == id) + if ((*uit).uidhash() == uid.uidhash()) { - m_uidsToSign.erase(it); - return; + m_uidsToRevokeCertification.erase(uit); + break; } } + } void PopupCertifyUserId::OnCertifyOptionChecked(int id) @@ -250,3 +272,10 @@ void PopupCertifyUserId::OnCertifyOptionUnChecked(int id) { m_certifyOptions -= id; } + +void PopupCertifyUserId::OnButtonGroupWhat(WRadioButton* btn) +{ + m_gbOptions->setDisabled(m_bgWhat->checkedId() + == What::RevokeUidCertification); + +} diff --git a/PopupCertifyUserId.h b/PopupCertifyUserId.h index 32a6629..98a5773 100644 --- a/PopupCertifyUserId.h +++ b/PopupCertifyUserId.h @@ -19,28 +19,32 @@ #include #include #include +#include +#include #include +#include #include "TransientMessageWidget.h" using namespace Wt; /** - * A popup with required parameters to certify a key : + * A popup with required parameters to certify or revoke a key uid : *
    *
  • Signer's private keys
  • *
  • Target key user identities (email)
  • *
  • Signing options : Exportable and non-revocable
  • *
  • Passphrase for selected private key
  • *
- * The passphrase is cached by gpg-agent for default 10 mins. - * \n The popup hides the passphrase widget until a certify op fails. - * \n UserID::Validity:: lists many validity levels. How to selectively apply - * an arbitrary level ? Before signing, it's 'Unknown'. After signing, it's - * always 'Full'. + * \n For revocation, GnuPG >= 2.2.24 is required. */ class PopupCertifyUserId : public WPopupWidget { public: + + enum What + { + RevokeUidCertification = 0, CertifyUid + }; PopupCertifyUserId(WWidget * anchorWidget, TransientMessageWidget * txtMessage, const WLength& width = 500); virtual ~PopupCertifyUserId(); @@ -102,6 +106,27 @@ public: return m_uidsToSign; } + /** + * Get the certifications to revoke. + * \n GPGME >= 1.15.0 is required. It expects + * it as a vector of GpgME::UserID, instead of a vector of indices used in + * GetUidsToSign(). + * @return + */ + vector& GetUidsToRevokeCertification() + { + return m_uidsToRevokeCertification; + } + + /** + * Certify selected user identities or revoke certifications. + * @return + */ + const What WhatToDo() const + { + return (What) m_bgWhat->checkedId(); + } + /** * Sum of option values *
    @@ -141,7 +166,10 @@ private: WPushButton * m_btnApply; vector m_uidsToSign; + vector m_uidsToRevokeCertification; int m_certifyOptions; + WGroupBox * m_gbOptions; + shared_ptr m_bgWhat; /** * Available private fingerprints in a combobox. The selected item is the * signing key. @@ -154,15 +182,17 @@ private: */ void PresentEmail(); /** - * Add the identity index in a vector. + * Add the identity index in a vector for certification. + * \n Add the ::UserID in a vector for signature revocation. * @param cb */ - void OnEmailChecked(WCheckBox * cb); + void OnEmailChecked(WCheckBox * cb, GpgME::UserID& uid); /** - * Removes the identity index in a vector. + * Removes the identity index from a vector for certification. + * \n Removes the ::UserID from a vector for signature revocation. * @param cb */ - void OnEmailUnChecked(WCheckBox * cb); + void OnEmailUnChecked(WCheckBox * cb, GpgME::UserID& uid); /** * Adds the option value in m_certifyOptions. * @param id @@ -173,6 +203,11 @@ private: * @param id */ void OnCertifyOptionUnChecked(int id); + /** + * Show certification options if certifying, else hide them. + * @param btn + */ + void OnButtonGroupWhat(WRadioButton * btn); }; #endif /* POPUPCERTIFYUSERID_H */ diff --git a/WTAPPROOT/K7/K7.xml b/WTAPPROOT/K7/K7.xml index 7dfca67..ed898d1 100644 --- a/WTAPPROOT/K7/K7.xml +++ b/WTAPPROOT/K7/K7.xml @@ -83,8 +83,10 @@ No email address is selected Apply Key succesfully certified - Key certification failed + Identity certification succesfully revoked Once set, this option cannot be removed. Use gpg cli for further edit. + Certify identities + Revoke certifications Click to be able to copy next diff --git a/WTAPPROOT/K7/K7_fr.xml b/WTAPPROOT/K7/K7_fr.xml index 04b6746..62af995 100644 --- a/WTAPPROOT/K7/K7_fr.xml +++ b/WTAPPROOT/K7/K7_fr.xml @@ -83,8 +83,10 @@ Aucun courriel n'est sélectionné Appliquer Succès de certification de la clé - Échec de certification de la clé + Succès de révocation de l'identité Une fois appliquée, cette option ne pourra plus être enlevée. Utilisez gpg en ligne de commande pour édition ultérieure. + Certifier les identités + Revoquer les certifications Cliquez pour pouvoir ensuite copier