From e9a55a1f69b9113b742e951ef0e9f8ad59824034 Mon Sep 17 00:00:00 2001 From: SET Date: Sat, 21 Nov 2020 20:53:01 +0100 Subject: [PATCH 01/12] Allow changing subkey expiry time. Needs GPGME 1.15.0. Using the same UI for changing expiry time of the primary secret key. --- GpgMEWorker.cpp | 38 +++++++++++++++++++++++++++++++++++++- GpgMEWorker.h | 8 ++++++-- K7Main.cpp | 5 ++++- KeyEdit.cpp | 23 +++++++++++++++++++---- KeyEdit.h | 3 ++- PopupExpiryTime.cpp | 6 ++++++ PopupExpiryTime.h | 29 ++++++++++++++++++++++++++--- 7 files changed, 100 insertions(+), 12 deletions(-) diff --git a/GpgMEWorker.cpp b/GpgMEWorker.cpp index afe35b3..a1919ce 100644 --- a/GpgMEWorker.cpp +++ b/GpgMEWorker.cpp @@ -145,6 +145,42 @@ const Error GpgMEWorker::CertifyKey(const char* fprSigningKey, return e; } +const Error GpgMEWorker::SetSubkeyExpiryTime(const char* keyFpr, + const char* subkeyFpr, + const string& passphrase, + ulong expires) +{ + Error e; + Key k = FindKey(keyFpr, e, true); + if (e.code() != 0) + return e; + e = m_ctx->addSigningKey(k); // +++ + if (e.code() != 0) + return e; + + vector subkey; + for (uint i = 0; i < k.subkeys().size(); i++) + { + GpgME::Subkey sk = k.subkey(i); + if (string(sk.fingerprint()) == string(subkeyFpr)) + { + subkey.push_back(sk); + break; + } + } + // There should always be at least one subkey (the key itself). + + 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->setExpire(k, expires, subkey); + + return e; +} + const Error GpgMEWorker::SetExpiryTime(const char * keyFpr, const string& passphrase, const string& timeString) @@ -168,7 +204,7 @@ const Error GpgMEWorker::SetExpiryTime(const char * keyFpr, GpgME::Data d; e = m_ctx->edit(k, std::unique_ptr (interactor), d); m_ctx->clearSigningKeys(); - + // NB : with a wrong passphrase, e.code() is 0 ! return e; } diff --git a/GpgMEWorker.h b/GpgMEWorker.h index d2fb62d..44d5404 100644 --- a/GpgMEWorker.h +++ b/GpgMEWorker.h @@ -77,6 +77,10 @@ public: const char * fprKeyToSign, vector& userIDsToSign, int options, const string& passphrase); + const Error SetSubkeyExpiryTime(const char * keyFpr, + const char * subkeyFpr, + const string& passphrase, + ulong expires = 63072000); /** * Set new expiry time of a secret key. * @param timeString @@ -111,8 +115,8 @@ public: * @return */ const Error RevokeUserID(const char * keyFpr, const string& passphrase, - const string& name, const string& email, - const string& comment); + const string& name, const string& email, + const string& comment); /** * Creates a pair of secret and public keys with the default engine * algorithms. Default expiry time is 2 * 365 days. diff --git a/K7Main.cpp b/K7Main.cpp index 53394cf..502d383 100644 --- a/K7Main.cpp +++ b/K7Main.cpp @@ -428,7 +428,10 @@ void K7Main::DisplaySubKeys(const WString& fullKeyID, bool secret) if (canEditExpiry) { lblExpiry->setToolTip(TR("TTTDoubleCLick")); - lblExpiry->doubleClicked().connect(std::bind(&KeyEdit::OnExpiryClicked, m_keyEdit, skNode, WString(k.primaryFingerprint()))); + lblExpiry->doubleClicked().connect(std::bind(&KeyEdit::OnExpiryClicked, + m_keyEdit, skNode, + WString(k.primaryFingerprint()), + WString(sk.fingerprint()))); } skNode->setColumnWidget(2, unique_ptr (lblExpiry)); WString usage = sk.canAuthenticate() ? WString("A") : WString::Empty; diff --git a/KeyEdit.cpp b/KeyEdit.cpp index acfe565..0096ef1 100644 --- a/KeyEdit.cpp +++ b/KeyEdit.cpp @@ -170,7 +170,8 @@ void KeyEdit::CertifyKey() m_owner->DisplayUids(keyToSign); } -void KeyEdit::OnExpiryClicked(WTreeTableNode* subkeyNode, const WString& keyFpr) +void KeyEdit::OnExpiryClicked(WTreeTableNode* subkeyNode, const WString& keyFpr, + const WString& subkeyFpr) { if (keyFpr != m_expiryEditedKeyFpr) { @@ -181,15 +182,29 @@ void KeyEdit::OnExpiryClicked(WTreeTableNode* subkeyNode, const WString& keyFpr) m_expiryEditedKeyFpr = keyFpr; m_popupExpiryTime->GetApplyButton()->clicked().connect(this, &KeyEdit::SetExpiryTime); } + m_popupExpiryTime->SetSubkeyFpr(subkeyFpr); m_popupExpiryTime->show(); } void KeyEdit::SetExpiryTime() { GpgMEWorker gpgWorker; - GpgME::Error e = gpgWorker.SetExpiryTime(m_expiryEditedKeyFpr.toUTF8().c_str(), - m_popupExpiryTime->GetPassphrase(), - m_popupExpiryTime->GetExpiryTime()); + GpgME::Error e; + const WString keyFpr = m_popupExpiryTime->GetKeyFpr(); + const WString subkeyFpr = m_popupExpiryTime->GetSubkeyFpr(); + if (keyFpr == subkeyFpr) + { + e = gpgWorker.SetExpiryTime(keyFpr.toUTF8().c_str(), + m_popupExpiryTime->GetPassphrase(), + m_popupExpiryTime->GetExpiryTime()); + } + else + { + e = gpgWorker.SetSubkeyExpiryTime(keyFpr.toUTF8().c_str(), + subkeyFpr.toUTF8().c_str(), + m_popupExpiryTime->GetPassphrase(), + m_popupExpiryTime->GetExpiry()); + } if (e.code() != 0) { m_owner->m_tmwMessage->SetText(TR("SetExpirationTimeFailure")); diff --git a/KeyEdit.h b/KeyEdit.h index 62744b2..cfb7525 100644 --- a/KeyEdit.h +++ b/KeyEdit.h @@ -85,7 +85,8 @@ private: * @param subkeyNode * @param keyFpr */ - void OnExpiryClicked(WTreeTableNode * subkeyNode, const WString& keyFpr); + void OnExpiryClicked(WTreeTableNode * subkeyNode, const WString& keyFpr, + const WString& subkeyFpr); void OnUidEmailClicked(WTreeTableNode * uidNode, const WString& keyFpr); diff --git a/PopupExpiryTime.cpp b/PopupExpiryTime.cpp index 7b47893..dfb6331 100644 --- a/PopupExpiryTime.cpp +++ b/PopupExpiryTime.cpp @@ -22,6 +22,7 @@ PopupExpiryTime::PopupExpiryTime(WWidget * anchorWidget, TransientMessageWidget m_tmwMessage = txtMessage; m_cwMain = NULL; m_keyFpr = WString::Empty; + m_subkeyFpr = WString::Empty; /* * Trade-off. * When the calendar of WDateEdit is clicked, this popup gets hidden, @@ -82,6 +83,11 @@ const string PopupExpiryTime::GetExpiryTime() const return m_deExpiry->text().toUTF8(); } +const ulong PopupExpiryTime::GetExpiry() const +{ + return ((WDate::currentDate().daysTo(m_deExpiry->date())) * 24 * 3600); +} + void PopupExpiryTime::ShowPassphrase(bool show) { // See comments in PopupCertifyUserId::ShowPassphrase diff --git a/PopupExpiryTime.h b/PopupExpiryTime.h index 78ec676..dde804b 100644 --- a/PopupExpiryTime.h +++ b/PopupExpiryTime.h @@ -23,9 +23,24 @@ class PopupExpiryTime : public WPopupWidget { public: PopupExpiryTime(WWidget * anchorWidget, TransientMessageWidget * txtMessage, - const WLength& width = 300); + const WLength& width = 300); virtual ~PopupExpiryTime(); void Create(const WString& keyFpr); + + const WString GetKeyFpr() + { + return m_keyFpr; + } + + void SetSubkeyFpr(const WString& subkeyFpr) + { + m_subkeyFpr = subkeyFpr; + } + + const WString GetSubkeyFpr() + { + return m_subkeyFpr; + } /** * Controls visibility of passphrase widgets. * \n Need not be always visible as the passphrase is cached by gpg-agent. @@ -34,6 +49,7 @@ public: * @param show */ void ShowPassphrase(bool show = true); + /** * Used to forward the passphrase to the loopback passphrase provider. * @return @@ -43,9 +59,15 @@ public: return m_lePassphrase->text().toUTF8(); } /** - * Returns the new expiry date. + * Returns the new expiry date, or 0 if date is invalid. */ const std::string GetExpiryTime() const; + /** + * Number of seconds from now. + * @return + */ + const ulong GetExpiry() const; + /** * Caller binds its function here. * @return @@ -54,7 +76,7 @@ public: { return m_btnApply; } - + private: TransientMessageWidget * m_tmwMessage; WContainerWidget * m_cwMain; @@ -63,6 +85,7 @@ private: WPushButton * m_btnApply; WText * m_lblPassphrase; WString m_keyFpr; + WString m_subkeyFpr; }; From 898764be7b46079ce429a609802c2c1fdbc923ff Mon Sep 17 00:00:00 2001 From: SET Date: Sun, 22 Nov 2020 09:21:38 +0100 Subject: [PATCH 02/12] Document changing subkey expiry time. --- GpgMEWorker.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/GpgMEWorker.h b/GpgMEWorker.h index 44d5404..10c8c44 100644 --- a/GpgMEWorker.h +++ b/GpgMEWorker.h @@ -77,6 +77,16 @@ public: const char * fprKeyToSign, vector& userIDsToSign, int options, 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 + * instead. + * @param keyFpr + * @param subkeyFpr + * @param passphrase + * @param expires : seconds from now. Use 0 for no expiry. + * @return + */ const Error SetSubkeyExpiryTime(const char * keyFpr, const char * subkeyFpr, const string& passphrase, From 110e5e4fb33885891f74e3663032b4846ccbc23c Mon Sep 17 00:00:00 2001 From: SET Date: Sun, 22 Nov 2020 14:22:08 +0100 Subject: [PATCH 03/12] 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 From 6f15ab4d066d844d0d8215fb516ce7c1ca2d88c8 Mon Sep 17 00:00:00 2001 From: SET Date: Sun, 22 Nov 2020 14:28:52 +0100 Subject: [PATCH 04/12] Refractor some identifiers to be more meaningful. m_popupUid -> m_popupCertifyUid CertifyKey() -> EditUidValidity() --- KeyEdit.cpp | 48 ++++++++++++++++++++++++------------------------ KeyEdit.h | 4 ++-- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/KeyEdit.cpp b/KeyEdit.cpp index b295b6e..e14f674 100644 --- a/KeyEdit.cpp +++ b/KeyEdit.cpp @@ -20,7 +20,7 @@ KeyEdit::KeyEdit(K7Main * owner) : WObject() { m_owner = owner; - m_popupUid = NULL; + m_popupCertifyUid = NULL; m_popupExpiryTime = NULL; m_popupAddUid = NULL; m_targetUidValidityKeyFpr = WString::Empty; @@ -29,7 +29,7 @@ KeyEdit::KeyEdit(K7Main * owner) KeyEdit::~KeyEdit() { - delete m_popupUid; + delete m_popupCertifyUid; } void KeyEdit::OnOwnerTrustDoubleClicked(WTreeTableNode * keyNode, bool keyHasSecret) @@ -130,67 +130,67 @@ void KeyEdit::OnUidValidityClicked(WTreeTableNode* uidNode, vector& pri if (targetKeyFpr != m_targetUidValidityKeyFpr) { bool passwordVisibility = true; - if (m_popupUid) - passwordVisibility = m_popupUid->IsPasswordVisible(); - delete m_popupUid; + if (m_popupCertifyUid) + passwordVisibility = m_popupCertifyUid->IsPasswordVisible(); + delete m_popupCertifyUid; WText * lblUidValidity = static_cast (uidNode->columnWidget(2)); - m_popupUid = new PopupCertifyUserId(lblUidValidity, m_owner->m_tmwMessage); - m_popupUid->Create(privateKeys, targetKeyFpr); - m_popupUid->ShowPassphrase(passwordVisibility); + m_popupCertifyUid = new PopupCertifyUserId(lblUidValidity, m_owner->m_tmwMessage); + m_popupCertifyUid->Create(privateKeys, targetKeyFpr); + m_popupCertifyUid->ShowPassphrase(passwordVisibility); m_targetUidValidityKeyFpr = targetKeyFpr; - m_popupUid->GetCertifyButton()->clicked().connect(this, &KeyEdit::CertifyKey); + m_popupCertifyUid->GetCertifyButton()->clicked().connect(this, &KeyEdit::EditUidValidity); } - m_popupUid->show(); + m_popupCertifyUid->show(); } -void KeyEdit::CertifyKey() +void KeyEdit::EditUidValidity() { - vector& uidsToSign = m_popupUid->GetUidsToSign(); + vector& uidsToSign = m_popupCertifyUid->GetUidsToSign(); if (uidsToSign.size() == 0) { m_owner->m_tmwMessage->SetText(TR("NoUidSelected")); return; } - const WString signingKey = m_popupUid->GetSelectedKey(); - const WString keyToSign = m_popupUid->GetKeyToSign(); + const WString signingKey = m_popupCertifyUid->GetSelectedKey(); + const WString keyToSign = m_popupCertifyUid->GetKeyToSign(); GpgMEWorker gpgWorker; GpgME::Error e; - if (m_popupUid->WhatToDo() == PopupCertifyUserId::CertifyUid) + if (m_popupCertifyUid->WhatToDo() == PopupCertifyUserId::CertifyUid) { - int options = m_popupUid->GetCertifyOptions(); + int options = m_popupCertifyUid->GetCertifyOptions(); e = gpgWorker.CertifyKey(signingKey.toUTF8().c_str(), keyToSign.toUTF8().c_str(), uidsToSign, options, - m_popupUid->GetPassphrase()); + m_popupCertifyUid->GetPassphrase()); } else { vector uidsToRevoke - = m_popupUid->GetUidsToRevokeCertification(); + = m_popupCertifyUid->GetUidsToRevokeCertification(); e = gpgWorker.RevokeKeyCertifications(signingKey.toUTF8().c_str(), keyToSign.toUTF8().c_str(), uidsToRevoke, - m_popupUid->GetPassphrase()); + m_popupCertifyUid->GetPassphrase()); } if (e.code() != 0) { - if (m_popupUid->WhatToDo() == PopupCertifyUserId::CertifyUid) + if (m_popupCertifyUid->WhatToDo() == PopupCertifyUserId::CertifyUid) { m_owner->m_tmwMessage->SetText(e.asString()); - m_popupUid->ShowPassphrase(true); + m_popupCertifyUid->ShowPassphrase(true); return; } else { m_owner->m_tmwMessage->SetText(e.asString()); - m_popupUid->ShowPassphrase(true); + m_popupCertifyUid->ShowPassphrase(true); return; } } - if (m_popupUid->WhatToDo() == PopupCertifyUserId::CertifyUid) + if (m_popupCertifyUid->WhatToDo() == PopupCertifyUserId::CertifyUid) m_owner->m_tmwMessage->SetText(TR("CertificationSuccess")); else m_owner->m_tmwMessage->SetText(TR("RevocationSuccess")); - m_popupUid->ShowPassphrase(false); + m_popupCertifyUid->ShowPassphrase(false); m_owner->DisplayUids(keyToSign); } diff --git a/KeyEdit.h b/KeyEdit.h index cfb7525..2cca0e4 100644 --- a/KeyEdit.h +++ b/KeyEdit.h @@ -39,7 +39,7 @@ private: virtual ~KeyEdit(); K7Main * m_owner; - PopupCertifyUserId * m_popupUid; + PopupCertifyUserId * m_popupCertifyUid; WString m_targetUidValidityKeyFpr; PopupExpiryTime * m_popupExpiryTime; @@ -57,7 +57,7 @@ private: * @param keyHasSecret */ void FillOwnerTrustCombo(WComboBox * cmb, bool keyHasSecret); - void CertifyKey(); + void EditUidValidity(); void SetExpiryTime(); void AddOrRevokeUid(); From 14964f986298f6c44c0a7169146ff1d1311d3c11 Mon Sep 17 00:00:00 2001 From: SET Date: Sun, 22 Nov 2020 14:48:27 +0100 Subject: [PATCH 05/12] Validate uid certification input. Check all input in the popup before calling KeyEdit:: --- KeyEdit.cpp | 6 +++--- PopupCertifyUserId.cpp | 21 +++++++++++++++++++++ PopupCertifyUserId.h | 6 ++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/KeyEdit.cpp b/KeyEdit.cpp index e14f674..0f6f633 100644 --- a/KeyEdit.cpp +++ b/KeyEdit.cpp @@ -145,10 +145,9 @@ void KeyEdit::OnUidValidityClicked(WTreeTableNode* uidNode, vector& pri void KeyEdit::EditUidValidity() { - vector& uidsToSign = m_popupCertifyUid->GetUidsToSign(); - if (uidsToSign.size() == 0) + if (!m_popupCertifyUid->Validate()) { - m_owner->m_tmwMessage->SetText(TR("NoUidSelected")); + m_owner->m_tmwMessage->SetText(TR("InvalidInput")); return; } const WString signingKey = m_popupCertifyUid->GetSelectedKey(); @@ -157,6 +156,7 @@ void KeyEdit::EditUidValidity() GpgME::Error e; if (m_popupCertifyUid->WhatToDo() == PopupCertifyUserId::CertifyUid) { + vector& uidsToSign = m_popupCertifyUid->GetUidsToSign(); int options = m_popupCertifyUid->GetCertifyOptions(); e = gpgWorker.CertifyKey(signingKey.toUTF8().c_str(), keyToSign.toUTF8().c_str(), diff --git a/PopupCertifyUserId.cpp b/PopupCertifyUserId.cpp index b6d0b12..bef647a 100644 --- a/PopupCertifyUserId.cpp +++ b/PopupCertifyUserId.cpp @@ -28,6 +28,7 @@ PopupCertifyUserId::PopupCertifyUserId(WWidget * anchorWidget, TransientMessageW m_cbOptionExportable = NULL; m_cbOptionNonRevocable = NULL; // m_cbOptionTrust = NULL; + m_cbConfirm = NULL; m_lblPassphrase = NULL; m_lePassphrase = NULL; m_btnApply = NULL; @@ -98,6 +99,9 @@ void PopupCertifyUserId::Create(vector& privateKeys, m_bgWhat->setCheckedButton(rbCertifyUid); vblMain->addLayout(unique_ptr (hblWhat)); + m_cbConfirm = new WCheckBox(TR("Confirm")); + vblMain->addWidget(unique_ptr (m_cbConfirm)); + WHBoxLayout * hblButtons = new WHBoxLayout(); WPushButton * btnClose = new WPushButton(TR("Close")); hblButtons->addWidget(unique_ptr (btnClose)); @@ -279,3 +283,20 @@ void PopupCertifyUserId::OnButtonGroupWhat(WRadioButton* btn) == What::RevokeUidCertification); } + +bool PopupCertifyUserId::Validate() const +{ + if (!m_cbConfirm->isChecked() || m_lePassphrase->text().empty()) + return false; + if (m_bgWhat->checkedId() == What::CertifyUid) + { + if (m_uidsToSign.size() == 0) + return false; + } + else + { + if (m_uidsToRevokeCertification.size() == 0) + return false; + } + return true; +} diff --git a/PopupCertifyUserId.h b/PopupCertifyUserId.h index 98a5773..95b4b48 100644 --- a/PopupCertifyUserId.h +++ b/PopupCertifyUserId.h @@ -149,6 +149,11 @@ public: { return m_btnApply; } + /** + * Check required parameters are set and confirmed. + * @return + */ + bool Validate() const; private: TransientMessageWidget * m_tmwMessage; WContainerWidget * m_cwMain; @@ -163,6 +168,7 @@ private: // WCheckBox * m_cbOptionTrust; // Always fails WText * m_lblPassphrase; WLineEdit * m_lePassphrase; + WCheckBox * m_cbConfirm; WPushButton * m_btnApply; vector m_uidsToSign; From bbc62a6efa6f43ccd245c822e9f52e883dce46f6 Mon Sep 17 00:00:00 2001 From: SET Date: Sun, 22 Nov 2020 18:58:04 +0100 Subject: [PATCH 06/12] Preserve source widget on click. Remove TreeTableNodeText from parent, store it in TreeTableNodeLineEdit and moves it back when the latter loses focus. --- SensitiveTreeTableNodeText.cpp | 11 +++++++---- SensitiveTreeTableNodeText.h | 10 ++++++++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/SensitiveTreeTableNodeText.cpp b/SensitiveTreeTableNodeText.cpp index d56c212..dc9afe9 100644 --- a/SensitiveTreeTableNodeText.cpp +++ b/SensitiveTreeTableNodeText.cpp @@ -51,6 +51,7 @@ void TreeTableNodeText::OnClick() m_lineEdit->setReadOnly(true); m_lineEdit->blurred().connect(m_lineEdit, &TreeTableNodeLineEdit::OnBlurred); m_lineEdit->setSelection(0, text().toUTF8().length()); + m_lineEdit->HoldSourceWidget(removeFromParent()); m_node->setColumnWidget(m_colIndex, unique_ptr (m_lineEdit)); m_lineEdit->setFocus(true); // +++ } @@ -78,13 +79,15 @@ void TreeTableNodeLineEdit::Init(WTreeTableNode* node, uint colIndex) { m_node = node; m_colIndex = colIndex; - m_text = NULL; clicked().connect(this, &TreeTableNodeLineEdit::OnBlurred); } void TreeTableNodeLineEdit::OnBlurred() { - m_text = new TreeTableNodeText(text(), m_node, m_colIndex); - m_text->clicked().connect(m_text, &TreeTableNodeText::OnClick); - m_node->setColumnWidget(m_colIndex, unique_ptr (m_text)); + m_node->setColumnWidget(m_colIndex, std::move(m_sourceWidget)); +} + +void TreeTableNodeLineEdit::HoldSourceWidget(std::unique_ptr sourceWidget) +{ + m_sourceWidget.swap(sourceWidget); } diff --git a/SensitiveTreeTableNodeText.h b/SensitiveTreeTableNodeText.h index f716ad4..24b8078 100644 --- a/SensitiveTreeTableNodeText.h +++ b/SensitiveTreeTableNodeText.h @@ -58,13 +58,19 @@ public: WTreeTableNode * node, uint colIndex); virtual ~TreeTableNodeLineEdit(); /** - * Creates back a TreeTableNodeText replacing this TreeTableNodeLineEdit. + * Move back the original TreeTableNodeText replacing this + * TreeTableNodeLineEdit. */ void OnBlurred(); + /** + * Keep the original TreeTableNodeText verbatim. + * @param text + */ + void HoldSourceWidget(std::unique_ptr sourceWidget); private: WTreeTableNode * m_node; uint m_colIndex; - TreeTableNodeText * m_text; + std::unique_ptr m_sourceWidget; void Init(WTreeTableNode * node, uint colIndex); }; From c9defdc02a64e15b8e901d10ac0cd156949b2074 Mon Sep 17 00:00:00 2001 From: SET Date: Sun, 22 Nov 2020 22:36:41 +0100 Subject: [PATCH 07/12] Apply some CSS makeup. Bad keys, subkeys, identities and signatures are tagged in red. A secret key not owned by the user is shown in bold. Application name is bold and bigger. Popup background color is changed to floralwhite. --- K7Main.cpp | 16 ++++++++++++++-- WTAPPROOT/K7/K7.css | 12 +++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/K7Main.cpp b/K7Main.cpp index 502d383..6ca86eb 100644 --- a/K7Main.cpp +++ b/K7Main.cpp @@ -83,7 +83,9 @@ K7Main::Create() WContainerWidget * cwHeader = new WContainerWidget(); WHBoxLayout * hblHeader = new WHBoxLayout(); cwHeader->setLayout(unique_ptr (hblHeader)); - hblHeader->addWidget(cpp14::make_unique(_APPNAME_)); + WText * lblTitle = + hblHeader->addWidget(cpp14::make_unique(_APPNAME_)); + lblTitle->setStyleClass("title"); // Error messages will go here m_tmwMessage = new TransientMessageWidget(); m_tmwMessage->setTextAlignment(AlignmentFlag::Right); @@ -294,6 +296,10 @@ void K7Main::DisplayKeys(const vector& kList, const WString& grpLabe } keyNode->setColumnWidget(2, unique_ptr (lblOwnerTrust)); TreeTableNodeText * ttntFpr = new TreeTableNodeText(k.primaryFingerprint(), keyNode, 3); + if (k.isBad()) + ttntFpr->addStyleClass("red", true); + if (!k.hasSecret() && Tools::KeyHasSecret(k.primaryFingerprint())) + ttntFpr->addStyleClass("bold", true); keyNode->setColumnWidget(3, unique_ptr (ttntFpr)); grpNode->addChildNode(unique_ptr (keyNode)); } @@ -355,6 +361,8 @@ void K7Main::DisplayUids(const WString& fullKeyID, bool secret) lblUidEmail->setToolTip(TR("TTTDoubleCLick")); lblUidEmail->doubleClicked().connect(std::bind(&KeyEdit::OnUidEmailClicked, m_keyEdit, uidNode, WString(k.primaryFingerprint()))); } + if (uid.isBad()) + lblUidEmail->setStyleClass("red"); uidNode->setColumnWidget(1, unique_ptr (lblUidEmail)); // Show key certify popup on double click WText * lblUidValidity = new WText(UidValidities[uid.validity()]); @@ -376,6 +384,8 @@ void K7Main::DisplayUids(const WString& fullKeyID, bool secret) WTreeTableNode * sigNode = new WTreeTableNode(signer); sigNode->setToolTip(Tools::GetSigStatus(sig)); TreeTableNodeText * ttntEmail = new TreeTableNodeText(sig.signerEmail(), sigNode, 1); + if (sig.isBad()) + ttntEmail->addStyleClass("red", true); sigNode->setColumnWidget(1, unique_ptr (ttntEmail)); WString exp = TR("Expiration") + _SPACE_ + _COLON_ + _SPACE_; exp += sig.neverExpires() ? TR("Never") : MakeDateTimeLabel(sig.expirationTime()); @@ -422,6 +432,8 @@ void K7Main::DisplaySubKeys(const WString& fullKeyID, bool secret) WTreeTableNode * skNode = new WTreeTableNode(sk.keyID()); skNode->setToolTip(Tools::GetKeyStatus(k)); TreeTableNodeText * ttntFpr = new TreeTableNodeText(sk.fingerprint(), skNode, 1); + if (sk.isBad()) + ttntFpr->setStyleClass("red"); skNode->setColumnWidget(1, unique_ptr (ttntFpr)); WString exp = sk.neverExpires() ? TR("Never") : MakeDateTimeLabel(sk.expirationTime()); WText * lblExpiry = new WText(exp); @@ -429,7 +441,7 @@ void K7Main::DisplaySubKeys(const WString& fullKeyID, bool secret) { lblExpiry->setToolTip(TR("TTTDoubleCLick")); lblExpiry->doubleClicked().connect(std::bind(&KeyEdit::OnExpiryClicked, - m_keyEdit, skNode, + m_keyEdit, skNode, WString(k.primaryFingerprint()), WString(sk.fingerprint()))); } diff --git a/WTAPPROOT/K7/K7.css b/WTAPPROOT/K7/K7.css index 2e289cf..c7d6113 100644 --- a/WTAPPROOT/K7/K7.css +++ b/WTAPPROOT/K7/K7.css @@ -6,5 +6,15 @@ * are all 0. */ div.popup { - background-color: lavender; + background-color: floralwhite; } +div.red { + color: red; +} +div.bold { + font-weight: bold; +} +span.title { + font-size: 125%; + font-weight: bold; +} From b726b89086cd952310ec8152d070d1028e932c20 Mon Sep 17 00:00:00 2001 From: SET Date: Mon, 23 Nov 2020 11:45:33 +0100 Subject: [PATCH 08/12] Use new Context::setExpire to set expiration dates. If no subkey is found (wrong fpr) or not provided, the expiry time of the key is set instead. setExpire() allows to expire all subkeys at once. Not implemented here. Requires GPGME >= 1.15.0. --- GpgMEWorker.cpp | 30 ++---------------------------- GpgMEWorker.h | 30 ++++-------------------------- KeyEdit.cpp | 23 ++++++++--------------- KeyEdit.h | 2 +- 4 files changed, 15 insertions(+), 70 deletions(-) diff --git a/GpgMEWorker.cpp b/GpgMEWorker.cpp index 114ea2f..437856f 100644 --- a/GpgMEWorker.cpp +++ b/GpgMEWorker.cpp @@ -173,7 +173,7 @@ const Error GpgMEWorker::RevokeKeyCertifications(const char* fprSigningKey, return e; } -const Error GpgMEWorker::SetSubkeyExpiryTime(const char* keyFpr, +const Error GpgMEWorker::SetKeyExpiryTime(const char* keyFpr, const char* subkeyFpr, const string& passphrase, ulong expires) @@ -204,38 +204,12 @@ const Error GpgMEWorker::SetSubkeyExpiryTime(const char* keyFpr, m_ppp->SetPassphrase(passphrase); m_ctx->setPassphraseProvider(m_ppp); + // setExpire() allows to expire all subkeys at once. Not implemented here. e = m_ctx->setExpire(k, expires, subkey); return e; } -const Error GpgMEWorker::SetExpiryTime(const char * keyFpr, - const string& passphrase, - const string& timeString) -{ - Error e; - Key k = FindKey(keyFpr, e, true); - if (e.code() != 0) - return e; - e = m_ctx->addSigningKey(k); // +++ - 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); - - SetExpiryTimeEditInteractor * interactor - = new SetExpiryTimeEditInteractor(timeString); - GpgME::Data d; - e = m_ctx->edit(k, std::unique_ptr (interactor), d); - m_ctx->clearSigningKeys(); - // NB : with a wrong passphrase, e.code() is 0 ! - return e; -} - const Error GpgMEWorker::AddUserID(const char* keyFpr, const string& passphrase, const string& name, const string& email, const string& comment) diff --git a/GpgMEWorker.h b/GpgMEWorker.h index 024a9af..0527304 100644 --- a/GpgMEWorker.h +++ b/GpgMEWorker.h @@ -91,27 +91,19 @@ public: 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 - * instead. + * Sets the expiry time of a single (sub)key. Requires GPGME >= 1.15.0. + * \n If no subkey is found (wrong fpr) or not provided, the expiry time of + * key is set instead. * @param keyFpr * @param subkeyFpr * @param passphrase * @param expires : seconds from now. Use 0 for no expiry. * @return */ - const Error SetSubkeyExpiryTime(const char * keyFpr, + const Error SetKeyExpiryTime(const char * keyFpr, const char * subkeyFpr, const string& passphrase, ulong expires = 63072000); - /** - * Set new expiry time of a secret key. - * @param timeString - * @return - */ - const Error SetExpiryTime(const char * keyFpr, - const string& passphrase, - const string& timeString = "0"); /** * Adds a user identity to a key. * \n The email parameter must have a valid email address format here, else @@ -259,20 +251,6 @@ public: }; -class SetExpiryTimeEditInteractor : public GpgSetExpiryTimeEditInteractor -{ -public: - - SetExpiryTimeEditInteractor(const std::string& timeString = "0") - : GpgSetExpiryTimeEditInteractor(timeString) - { - }; - - virtual ~SetExpiryTimeEditInteractor() - { - }; -}; - class AddUserIDEditInteractor : public GpgAddUserIDEditInteractor { public: diff --git a/KeyEdit.cpp b/KeyEdit.cpp index 0f6f633..62c3c75 100644 --- a/KeyEdit.cpp +++ b/KeyEdit.cpp @@ -204,31 +204,24 @@ void KeyEdit::OnExpiryClicked(WTreeTableNode* subkeyNode, const WString& keyFpr, m_popupExpiryTime = new PopupExpiryTime(lblExpiry, m_owner->m_tmwMessage); m_popupExpiryTime->Create(keyFpr); m_expiryEditedKeyFpr = keyFpr; - m_popupExpiryTime->GetApplyButton()->clicked().connect(this, &KeyEdit::SetExpiryTime); + m_popupExpiryTime->GetApplyButton()->clicked().connect(this, &KeyEdit::SetKeyExpiryTime); } m_popupExpiryTime->SetSubkeyFpr(subkeyFpr); m_popupExpiryTime->show(); } -void KeyEdit::SetExpiryTime() +void KeyEdit::SetKeyExpiryTime() { GpgMEWorker gpgWorker; GpgME::Error e; const WString keyFpr = m_popupExpiryTime->GetKeyFpr(); - const WString subkeyFpr = m_popupExpiryTime->GetSubkeyFpr(); + WString subkeyFpr = m_popupExpiryTime->GetSubkeyFpr(); if (keyFpr == subkeyFpr) - { - e = gpgWorker.SetExpiryTime(keyFpr.toUTF8().c_str(), - m_popupExpiryTime->GetPassphrase(), - m_popupExpiryTime->GetExpiryTime()); - } - else - { - e = gpgWorker.SetSubkeyExpiryTime(keyFpr.toUTF8().c_str(), - subkeyFpr.toUTF8().c_str(), - m_popupExpiryTime->GetPassphrase(), - m_popupExpiryTime->GetExpiry()); - } + subkeyFpr = WString::Empty; + e = gpgWorker.SetKeyExpiryTime(keyFpr.toUTF8().c_str(), + subkeyFpr.toUTF8().c_str(), + m_popupExpiryTime->GetPassphrase(), + m_popupExpiryTime->GetExpiry()); if (e.code() != 0) { m_owner->m_tmwMessage->SetText(TR("SetExpirationTimeFailure")); diff --git a/KeyEdit.h b/KeyEdit.h index 2cca0e4..3973685 100644 --- a/KeyEdit.h +++ b/KeyEdit.h @@ -58,7 +58,7 @@ private: */ void FillOwnerTrustCombo(WComboBox * cmb, bool keyHasSecret); void EditUidValidity(); - void SetExpiryTime(); + void SetKeyExpiryTime(); void AddOrRevokeUid(); /** From e14a5c19f4d2f702b39ca0adda685e09779d44b5 Mon Sep 17 00:00:00 2001 From: SET Date: Mon, 23 Nov 2020 20:34:11 +0100 Subject: [PATCH 09/12] Log GpgME errors. In file WT_APP_ROOT/gpgme.log. Independently of usual Wt logging. --- GpgMELogger.h | 39 ++++++++++++++++++++++++++++ K7Main.cpp | 5 ++++ KeyEdit.cpp | 5 ++++ KeyringIO.cpp | 7 +++++ PopupCertifyUserId.cpp | 3 +++ Tools.cpp | 2 ++ nbproject/configurations.xml | 7 +++++ nbproject/private/configurations.xml | 2 -- 8 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 GpgMELogger.h diff --git a/GpgMELogger.h b/GpgMELogger.h new file mode 100644 index 0000000..f0777dd --- /dev/null +++ b/GpgMELogger.h @@ -0,0 +1,39 @@ +/* + * File: GpgMELogger.h + * Author: SET - nmset@yandex.com + * License : GPL v2 + * Copyright SET - © 2019 + * + * Created on November 23, 2020, 4:00 PM + */ + +#ifndef GPGMELOGGER_H +#define GPGMELOGGER_H + +#include +#include +#include +#include +#include + +using namespace Wt; + +static std::mutex gs_logGpgMEError; +#define LGE(e) LogGpgMEError(e) + +static void LogGpgMEError(const GpgME::Error& e) +{ + if (e.code() == 0) + return; + std::unique_lock lock(gs_logGpgMEError); + const string logfile = WApplication::appRoot() + string("gpgme.log"); + WLogger logger; + logger.setFile(logfile); + logger.entry("") << WLogger::timestamp + << WLogger::sep << WApplication::instance()->environment().userAgent() + << WLogger::sep << std::to_string(e.code()) + << WLogger::sep << e.asString(); +} + +#endif /* GPGMELOGGER_H */ + diff --git a/K7Main.cpp b/K7Main.cpp index 6ca86eb..7366ecc 100644 --- a/K7Main.cpp +++ b/K7Main.cpp @@ -21,6 +21,7 @@ #include "GpgMECWorker.h" #include "Tools.h" #include "SensitiveTreeTableNodeText.h" +#include "GpgMELogger.h" using namespace std; @@ -207,6 +208,7 @@ void K7Main::Search() { privkList.clear(); m_tmwMessage->SetText(e.asString()); + LGE(e); return; } /* @@ -232,6 +234,7 @@ void K7Main::Search() { pubkList.clear(); m_tmwMessage->SetText(e.asString()); + LGE(e); return; } } @@ -334,6 +337,7 @@ void K7Main::DisplayUids(const WString& fullKeyID, bool secret) if (e.code() != 0) { m_tmwMessage->SetText(e.asString()); + LGE(e); return; } if (m_ttbUids->columnCount() == 1) @@ -408,6 +412,7 @@ void K7Main::DisplaySubKeys(const WString& fullKeyID, bool secret) if (e.code() != 0) { m_tmwMessage->SetText(e.asString()); + LGE(e); return; } if (m_ttbSubKeys->columnCount() == 1) diff --git a/KeyEdit.cpp b/KeyEdit.cpp index 62c3c75..822cc66 100644 --- a/KeyEdit.cpp +++ b/KeyEdit.cpp @@ -13,6 +13,7 @@ #include #include "GpgMEWorker.h" #include "Tools.h" +#include "GpgMELogger.h" using namespace std; @@ -91,6 +92,7 @@ void KeyEdit::OnOwnerTrustBlurred(WTreeTableNode* keyNode, bool keyHasSecret) { lblOwnerTrust->setText(previousTrustLevel); m_owner->m_tmwMessage->SetText(TR("OwnerTrustFailure")); + LGE(e); return; } m_owner->m_tmwMessage->SetText(TR("OwnerTrustSuccess")); @@ -174,6 +176,7 @@ void KeyEdit::EditUidValidity() } if (e.code() != 0) { + LGE(e); if (m_popupCertifyUid->WhatToDo() == PopupCertifyUserId::CertifyUid) { m_owner->m_tmwMessage->SetText(e.asString()); @@ -226,6 +229,7 @@ void KeyEdit::SetKeyExpiryTime() { m_owner->m_tmwMessage->SetText(TR("SetExpirationTimeFailure")); m_popupExpiryTime->ShowPassphrase(true); + LGE(e); return; } m_owner->m_tmwMessage->SetText(TR("SetExpirationTimeSuccess")); @@ -282,6 +286,7 @@ void KeyEdit::AddOrRevokeUid() { m_popupAddUid->ShowPassphrase(true); m_owner->m_tmwMessage->SetText(e.asString()); + LGE(e); } else { diff --git a/KeyringIO.cpp b/KeyringIO.cpp index 0914bf7..1bc6900 100644 --- a/KeyringIO.cpp +++ b/KeyringIO.cpp @@ -13,6 +13,7 @@ #include "Tools.h" #include #include +#include "GpgMELogger.h" using namespace std; @@ -80,6 +81,7 @@ void KeyringIO::DoImportKey() if (e.code() != 0) { m_tmwMessage->SetText(e.asString()); + LGE(e); return; } if (fpr.empty()) @@ -92,6 +94,7 @@ void KeyringIO::DoImportKey() if (e.code() != 0) { m_tmwMessage->SetText(e.asString()); + LGE(e); return; } m_tmwMessage->SetText(TR("ImportSuccess") + fpr + WString(" - ") + WString(k.userID(0).name())); @@ -110,6 +113,7 @@ bool KeyringIO::CanKeyBeDeleted(const WString& fullKeyID) if (e.code() != 0 && e.code() != 16383) { // 16383 : end of file, when key is not private m_tmwMessage->SetText(e.asString()); + LGE(e); return false; } // k can now be secret or public @@ -167,6 +171,7 @@ void KeyringIO::DoDeleteKey() if (e.code() != 0) { m_tmwMessage->SetText(e.asString()); + LGE(e); return; } // Delete the key using the C API @@ -233,6 +238,7 @@ void KeyringIO::DoCreateKey() if (e.code() != 0) { m_tmwMessage->SetText(e.asString()); + LGE(e); } else { @@ -311,6 +317,7 @@ void ExportKeyStreamResource::handleRequest(const Http::Request& request, if (e.code() != 0) { m_tmwMessage->SetText(e.asString()); + LGE(e); return; } suggestFileName(m_fpr + WString(".asc"), ContentDisposition::Attachment); diff --git a/PopupCertifyUserId.cpp b/PopupCertifyUserId.cpp index bef647a..8be0ebe 100644 --- a/PopupCertifyUserId.cpp +++ b/PopupCertifyUserId.cpp @@ -15,6 +15,7 @@ #include #include #include +#include "GpgMELogger.h" using namespace std; @@ -135,6 +136,7 @@ void PopupCertifyUserId::FillPrivateKeyComboBox(vector& privateKeys) if (e.code() != 0) { m_tmwMessage->SetText(e.asString()); + LGE(e); return; } /* @@ -177,6 +179,7 @@ void PopupCertifyUserId::PresentEmail() if (e.code() != 0) { m_tmwMessage->SetText(e.asString()); + LGE(e); return; } if (lst.size() != 1) diff --git a/Tools.cpp b/Tools.cpp index b4d5466..e37befc 100644 --- a/Tools.cpp +++ b/Tools.cpp @@ -10,6 +10,7 @@ #include "Tools.h" #include #include +#include "GpgMELogger.h" using namespace std; @@ -101,6 +102,7 @@ bool Tools::KeyHasSecret(const WString& fpr) GpgME::Key k = gpgw.FindKey(fpr.toUTF8().c_str(), e, true); // Look for a private key if (e.code() != 0 && e.code() != 16383) { // 16383 : end of file, when key is not private + LGE(e); return false; } return (!k.isNull()); diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index d5d6ae2..8f4d4b7 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -6,6 +6,7 @@ projectFiles="true"> AppConfig.h GpgMECWorker.h + GpgMELogger.h GpgMEWorker.h K7Main.h KeyEdit.h @@ -102,6 +103,8 @@ + + @@ -218,6 +221,8 @@ + + @@ -338,6 +343,8 @@ + + diff --git a/nbproject/private/configurations.xml b/nbproject/private/configurations.xml index 1f82c50..2c9b830 100644 --- a/nbproject/private/configurations.xml +++ b/nbproject/private/configurations.xml @@ -13,8 +13,6 @@ - - From e4f8ac9c3a219408b74af7203195dcdd16afe527 Mon Sep 17 00:00:00 2001 From: SET Date: Mon, 23 Nov 2020 20:37:09 +0100 Subject: [PATCH 10/12] Remove useless conditional. Not forwarding customized message to UI in EditUidValidity(). --- KeyEdit.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/KeyEdit.cpp b/KeyEdit.cpp index 822cc66..2ca9786 100644 --- a/KeyEdit.cpp +++ b/KeyEdit.cpp @@ -176,18 +176,10 @@ void KeyEdit::EditUidValidity() } if (e.code() != 0) { + m_owner->m_tmwMessage->SetText(e.asString()); + m_popupCertifyUid->ShowPassphrase(true); LGE(e); - if (m_popupCertifyUid->WhatToDo() == PopupCertifyUserId::CertifyUid) - { - m_owner->m_tmwMessage->SetText(e.asString()); - m_popupCertifyUid->ShowPassphrase(true); - return; - } - else { - m_owner->m_tmwMessage->SetText(e.asString()); - m_popupCertifyUid->ShowPassphrase(true); - return; - } + return; } if (m_popupCertifyUid->WhatToDo() == PopupCertifyUserId::CertifyUid) m_owner->m_tmwMessage->SetText(TR("CertificationSuccess")); From e56d1bd6eb370690658ff24dcaeb6659b5e31777 Mon Sep 17 00:00:00 2001 From: SET Date: Mon, 23 Nov 2020 21:20:13 +0100 Subject: [PATCH 11/12] Fix download code. We need not check request::continuation() for stream resource. --- KeyringIO.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/KeyringIO.cpp b/KeyringIO.cpp index 1bc6900..4e7829b 100644 --- a/KeyringIO.cpp +++ b/KeyringIO.cpp @@ -309,19 +309,16 @@ void ExportKeyStreamResource::handleRequest(const Http::Request& request, return; } string buffer; - if (!request.continuation()) // Needed for WStreamResource ? + Error e; + GpgMEWorker gpgw; + e = gpgw.ExportPublicKey(m_fpr.toUTF8().c_str(), buffer); + if (e.code() != 0) { - Error e; - GpgMEWorker gpgw; - e = gpgw.ExportPublicKey(m_fpr.toUTF8().c_str(), buffer); - if (e.code() != 0) - { - m_tmwMessage->SetText(e.asString()); - LGE(e); - return; - } - suggestFileName(m_fpr + WString(".asc"), ContentDisposition::Attachment); + m_tmwMessage->SetText(e.asString()); + LGE(e); + return; } + suggestFileName(m_fpr + WString(".asc"), ContentDisposition::Attachment); istrstream bufStream(buffer.c_str()); handleRequestPiecewise(request, response, bufStream); From ab4e6d85bcea35c5a8971e9415bfcfed71395d06 Mon Sep 17 00:00:00 2001 From: SET Date: Tue, 24 Nov 2020 19:07:54 +0100 Subject: [PATCH 12/12] Log subject common name also. May help understanding problems. --- GpgMELogger.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/GpgMELogger.h b/GpgMELogger.h index f0777dd..f915a3e 100644 --- a/GpgMELogger.h +++ b/GpgMELogger.h @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include @@ -26,10 +28,26 @@ static void LogGpgMEError(const GpgME::Error& e) if (e.code() == 0) return; std::unique_lock lock(gs_logGpgMEError); + + const vector * dnAttr + = &(WApplication::instance()->environment().sslInfo() + ->clientCertificate().subjectDn()); + WString subjectCommonName = WString::Empty; + for (uint i = 0; i < dnAttr->size(); i++) + { + if (dnAttr->at(i).name() == WSslCertificate::DnAttributeName::CommonName) + { + subjectCommonName = dnAttr->at(i).value(); + break; + } + } + + const string logfile = WApplication::appRoot() + string("gpgme.log"); WLogger logger; logger.setFile(logfile); logger.entry("") << WLogger::timestamp + << WLogger::sep << subjectCommonName << WLogger::sep << WApplication::instance()->environment().userAgent() << WLogger::sep << std::to_string(e.code()) << WLogger::sep << e.asString();