From 2bddf295963053f1c41c3d8b7eef3900355537fc Mon Sep 17 00:00:00 2001 From: SET Date: Sat, 7 Nov 2020 22:17:44 +0100 Subject: [PATCH] Allow to change key expiry date. Select new date in a popup. Controlled by a specific configuration flag. --- AppConfig.cpp | 15 ++++++ AppConfig.h | 6 +++ GpgMEWorker.cpp | 33 ++++++++++-- GpgMEWorker.h | 22 ++++++++ K7Main.cpp | 10 +++- KeyEdit.cpp | 33 ++++++++++++ KeyEdit.h | 11 ++++ PopupCertifyUserId.cpp | 1 - PopupExpiryTime.cpp | 88 +++++++++++++++++++++++++++++++ PopupExpiryTime.h | 70 ++++++++++++++++++++++++ README.md | 4 +- WTAPPROOT/K7/K7.xml | 5 ++ WTAPPROOT/K7/K7_fr.xml | 5 ++ nbproject/Makefile-ARM-Release.mk | 6 +++ nbproject/Makefile-Debug.mk | 6 +++ nbproject/Makefile-Release.mk | 6 +++ nbproject/configurations.xml | 14 +++++ 17 files changed, 328 insertions(+), 7 deletions(-) create mode 100644 PopupExpiryTime.cpp create mode 100644 PopupExpiryTime.h diff --git a/AppConfig.cpp b/AppConfig.cpp index fd58988..196a534 100644 --- a/AppConfig.cpp +++ b/AppConfig.cpp @@ -31,6 +31,7 @@ using namespace std; "canDelete" : true, "canEditOwnerTrust" : true, "canEditUidValidity" : true, + "canEditExpiryTime" : true, "privKeyIds" : [ "fullKeyId1", "fullKeyId2" @@ -141,6 +142,20 @@ bool AppConfig::CanEditUidValidity() const return cnObject.get("canEditUidValidity"); } +bool AppConfig::CanEditExpiryTime() const +{ + if (PrivateKeyIds().size() == 0) + return false; + const WString commonName = GetSubjectDnAttribute(WSslCertificate::DnAttributeName::CommonName); + if (!m_SubjectCNObject.contains(commonName.toUTF8())) + return false; + Json::Object cnObject = m_SubjectCNObject.get(commonName.toUTF8()); + if (!cnObject.contains("canEditExpiryTime")) + return false; + return cnObject.get("canEditExpiryTime"); +} + + vector AppConfig::PrivateKeyIds() const { // List private key identifiers. diff --git a/AppConfig.h b/AppConfig.h index 9e24383..0e4715f 100644 --- a/AppConfig.h +++ b/AppConfig.h @@ -54,6 +54,12 @@ public: * @return */ bool CanEditUidValidity() const; + /** + * Allows to edit expiry time of a key. + * Only private keys are concerned. + * @return + */ + bool CanEditExpiryTime() const; /** * List of full private key identifiers. The user may delete these private keys. * Must be full keyid, short keyid or fingerprint. diff --git a/GpgMEWorker.cpp b/GpgMEWorker.cpp index 48ab850..f3af16e 100644 --- a/GpgMEWorker.cpp +++ b/GpgMEWorker.cpp @@ -19,7 +19,7 @@ GpgMEWorker::GpgMEWorker() m_ctx = Context::createForProtocol(Protocol::OpenPGP); // Allow to list key certifications m_ctx->setKeyListMode(GpgME::KeyListMode::Signatures - | GpgME::KeyListMode::Validate); + | GpgME::KeyListMode::Validate); m_ppp = NULL; } @@ -110,14 +110,14 @@ const Error GpgMEWorker::CertifyKey(const char* fprSigningKey, Key keyToSign = FindKey(fprKeyToSign, e, false); if (e.code() != 0) return e; - + // GPG engine will fetch for passphrase in the custom provider. m_ctx->setPinentryMode(Context::PinentryMode::PinentryLoopback); if (m_ppp == NULL) m_ppp = new LoopbackPassphraseProvider(); m_ppp->SetPassphrase(passphrase); m_ctx->setPassphraseProvider(m_ppp); - + SetSignKeyEditInteractor * interactor = new SetSignKeyEditInteractor(); interactor->setKey(keyToSign); interactor->setUserIDsToSign(userIDsToSign); @@ -133,3 +133,30 @@ const Error GpgMEWorker::CertifyKey(const char* fprSigningKey, */ 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(); + + return e; +} diff --git a/GpgMEWorker.h b/GpgMEWorker.h index 32a3acc..8ac5008 100644 --- a/GpgMEWorker.h +++ b/GpgMEWorker.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "LoopbackPassphraseProvider.h" @@ -75,6 +76,14 @@ public: const char * fprKeyToSign, vector& userIDsToSign, int options, const string& passphrase); + /** + * Set new expiry time of a secret key. + * @param timeString + * @return + */ + const Error SetExpiryTime(const char * keyFpr, + const string& passphrase, + const string& timeString = "0"); private: Context * m_ctx; @@ -121,5 +130,18 @@ public: }; +class SetExpiryTimeEditInteractor : public GpgSetExpiryTimeEditInteractor +{ +public: + + SetExpiryTimeEditInteractor(const std::string& timeString = "0") + : GpgSetExpiryTimeEditInteractor(timeString) + { + }; + + virtual ~SetExpiryTimeEditInteractor() + { + }; +}; #endif /* GPGMEWORKER_H */ diff --git a/K7Main.cpp b/K7Main.cpp index d93b963..c758090 100644 --- a/K7Main.cpp +++ b/K7Main.cpp @@ -376,6 +376,9 @@ void K7Main::DisplaySubKeys(const WString& fullKeyID, bool secret) rootNode->setChildCountPolicy(ChildCountPolicy::Enabled); m_ttbSubKeys->setTreeRoot(unique_ptr (rootNode), TR("SubKeys")); rootNode->expand(); + bool canEditExpiry = m_config->CanEditExpiryTime() + && Tools::KeyHasSecret(k.primaryFingerprint()) + && m_keyEdit->IsOurKey(k.primaryFingerprint()); for (uint i = 0; i < k.numSubkeys(); i++) { Subkey sk = k.subkey(i); @@ -384,7 +387,12 @@ void K7Main::DisplaySubKeys(const WString& fullKeyID, bool secret) TreeTableNodeText * ttntFpr = new TreeTableNodeText(sk.fingerprint(), skNode, 1); skNode->setColumnWidget(1, unique_ptr (ttntFpr)); WString exp = sk.neverExpires() ? TR("Never") : MakeDateTimeLabel(sk.expirationTime()); - skNode->setColumnWidget(2, cpp14::make_unique (exp)); + WText * lblExpiry = new WText(exp); + if (canEditExpiry) { + lblExpiry->setToolTip(TR("TTTDoubleCLick")); + lblExpiry->doubleClicked().connect(std::bind(&KeyEdit::OnExpiryClicked, m_keyEdit, skNode, WString(k.primaryFingerprint()))); + } + skNode->setColumnWidget(2, unique_ptr (lblExpiry)); WString usage = sk.canAuthenticate() ? WString("A") : WString::Empty; usage += sk.canCertify() ? WString("C") : WString::Empty; usage += sk.canEncrypt() ? WString("E") : WString::Empty; diff --git a/KeyEdit.cpp b/KeyEdit.cpp index b89097a..8506a70 100644 --- a/KeyEdit.cpp +++ b/KeyEdit.cpp @@ -21,7 +21,9 @@ KeyEdit::KeyEdit(K7Main * owner) { m_owner = owner; m_popupUid = NULL; + m_popupExpiryTime = NULL; m_targetUidValidityKeyFpr = WString::Empty; + m_expiryEditedKeyFpr = WString::Empty; } KeyEdit::~KeyEdit() @@ -170,3 +172,34 @@ void KeyEdit::CertifyKey() m_popupUid->ShowPassphrase(false); m_owner->DisplayUids(keyToSign); } + +void KeyEdit::OnExpiryClicked(WTreeTableNode* subkeyNode, const WString& keyFpr) +{ + if (keyFpr != m_expiryEditedKeyFpr) { + delete m_popupExpiryTime; + WText * lblExpiry = static_cast (subkeyNode->columnWidget(2)); + 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->show(); +} + +void KeyEdit::SetExpiryTime() +{ + GpgMEWorker gpgWorker; + GpgME::Error e = gpgWorker.SetExpiryTime(m_expiryEditedKeyFpr.toUTF8().c_str(), + m_popupExpiryTime->GetPassphrase(), + m_popupExpiryTime->GetExpiryTime()); + if (e.code() != 0) + { + m_owner->m_tmwMessage->SetText(TR("SetExpirationTimeFailure")); + m_popupExpiryTime->ShowPassphrase(true); + return; + } + m_owner->m_tmwMessage->SetText(TR("SetExpirationTimeSuccess")); + m_popupExpiryTime->ShowPassphrase(false); + m_owner->DisplaySubKeys(m_expiryEditedKeyFpr, true); +} + diff --git a/KeyEdit.h b/KeyEdit.h index c187697..4364758 100644 --- a/KeyEdit.h +++ b/KeyEdit.h @@ -14,6 +14,7 @@ #include "K7Main.h" #include #include "PopupCertifyUserId.h" +#include "PopupExpiryTime.h" using namespace Wt; @@ -53,11 +54,20 @@ public: * @param targetKeyFpr : The key to sign. */ void OnUidValidityClicked(WTreeTableNode * uidNode, vector& privateKeys, const WString& targetKeyFpr); + /** + * Shows a popup with parameters to change expiry date. + * @param subkeyNode + * @param keyFpr + */ + void OnExpiryClicked(WTreeTableNode * subkeyNode, const WString& keyFpr); private: K7Main * m_owner; PopupCertifyUserId * m_popupUid; WString m_targetUidValidityKeyFpr; + + PopupExpiryTime * m_popupExpiryTime; + WString m_expiryEditedKeyFpr; /** * Unknown is common. * \n If keyHasSecret is true, show only Ultimate level. @@ -68,6 +78,7 @@ private: */ void FillOwnerTrustCombo(WComboBox * cmb, bool keyHasSecret); void CertifyKey(); + void SetExpiryTime(); }; #endif /* KEYEDIT_H */ diff --git a/PopupCertifyUserId.cpp b/PopupCertifyUserId.cpp index e5933a5..151978a 100644 --- a/PopupCertifyUserId.cpp +++ b/PopupCertifyUserId.cpp @@ -44,7 +44,6 @@ void PopupCertifyUserId::Create(vector& privateKeys, { m_fprKeyToSign = fprKeyToSign; m_cwMain = static_cast (implementation()); - // White in css file, like default background color. m_cwMain->setStyleClass("popup"); WVBoxLayout * vblMain = new WVBoxLayout(); diff --git a/PopupExpiryTime.cpp b/PopupExpiryTime.cpp new file mode 100644 index 0000000..aff4575 --- /dev/null +++ b/PopupExpiryTime.cpp @@ -0,0 +1,88 @@ +/* + * File: PopupExpiryTime.cpp + * Author: SET - nmset@yandex.com + * License : GPL v2 + * Copyright SET - © 2019 + * + * Created on November 7, 2020, 6:05 PM + */ + +#include "PopupExpiryTime.h" +#include "global.h" +#include +#include +#include + +using namespace std; + +PopupExpiryTime::PopupExpiryTime(WWidget * anchorWidget, TransientMessageWidget * txtMessage, + const WLength& width) +: WPopupWidget(cpp14::make_unique()) +{ + m_tmwMessage = txtMessage; + m_cwMain = NULL; + m_keyFpr = WString::Empty; + setTransient(false); + setAnchorWidget(anchorWidget); + setWidth(width); +} + +PopupExpiryTime::~PopupExpiryTime() +{ +} + +void PopupExpiryTime::Create(const WString& keyFpr) +{ + m_keyFpr = keyFpr; + m_cwMain = static_cast (implementation()); + m_cwMain->setStyleClass("popup"); + + WVBoxLayout * vblMain = new WVBoxLayout(); + m_cwMain->setLayout(unique_ptr (vblMain)); + + WHBoxLayout * hblExpiry = new WHBoxLayout(); + WText * lblExpiry = new WText(TR("ExpiryDate")); + hblExpiry->addWidget(unique_ptr (lblExpiry)); + m_deExpiry = new WDateEdit(); + hblExpiry->addWidget(unique_ptr (m_deExpiry), 1); + vblMain->addLayout(unique_ptr (hblExpiry)); + + WHBoxLayout * hblPassphrase = new WHBoxLayout(); + m_lblPassphrase = new WText(TR("Passphrase")); + hblPassphrase->addWidget(unique_ptr (m_lblPassphrase)); + m_lePassphrase = new WLineEdit(); + m_lePassphrase->setEchoMode(EchoMode::Password); + hblPassphrase->addWidget(unique_ptr (m_lePassphrase), 1); + vblMain->addLayout(unique_ptr (hblPassphrase)); + + WHBoxLayout * hblButtons = new WHBoxLayout(); + WPushButton * btnClose = new WPushButton(TR("Close")); + btnClose->clicked().connect(this, &WPopupWidget::hide); + hblButtons->addWidget(unique_ptr (btnClose)); + m_btnApply = new WPushButton(TR("Apply")); + hblButtons->addWidget(unique_ptr (m_btnApply)); + vblMain->addLayout(unique_ptr (hblButtons)); +} + +const string PopupExpiryTime::GetExpiryTime() const +{ + if (m_deExpiry->text().empty()) + return "0"; + return m_deExpiry->text().toUTF8(); +} + +void PopupExpiryTime::ShowPassphrase(bool show) +{ + // See comments in PopupCertifyUserId::ShowPassphrase + if (show) + { + m_lblPassphrase->show(); + m_lePassphrase->show(); + m_lePassphrase->setText(WString::Empty); + } + else + { + m_lblPassphrase->hide(); + m_lePassphrase->hide(); + } +} diff --git a/PopupExpiryTime.h b/PopupExpiryTime.h new file mode 100644 index 0000000..78ec676 --- /dev/null +++ b/PopupExpiryTime.h @@ -0,0 +1,70 @@ +/* + * File: PopupExpiryTime.h + * Author: SET - nmset@yandex.com + * License : GPL v2 + * Copyright SET - © 2019 + * + * Created on November 7, 2020, 6:05 PM + */ + +#ifndef POPUPEXPIRYTIME_H +#define POPUPEXPIRYTIME_H + +#include +#include +#include +#include +#include +#include "TransientMessageWidget.h" + +using namespace Wt; + +class PopupExpiryTime : public WPopupWidget +{ +public: + PopupExpiryTime(WWidget * anchorWidget, TransientMessageWidget * txtMessage, + const WLength& width = 300); + virtual ~PopupExpiryTime(); + void Create(const WString& keyFpr); + /** + * Controls visibility of passphrase widgets. + * \n Need not be always visible as the passphrase is cached by gpg-agent. + * \n During that caching period, a wrong input passphrase will not be looked + * for by GPG engine, and may be confusing. + * @param show + */ + void ShowPassphrase(bool show = true); + /** + * Used to forward the passphrase to the loopback passphrase provider. + * @return + */ + const string GetPassphrase() + { + return m_lePassphrase->text().toUTF8(); + } + /** + * Returns the new expiry date. + */ + const std::string GetExpiryTime() const; + /** + * Caller binds its function here. + * @return + */ + WPushButton* GetApplyButton() + { + return m_btnApply; + } + +private: + TransientMessageWidget * m_tmwMessage; + WContainerWidget * m_cwMain; + WDateEdit * m_deExpiry; + WLineEdit * m_lePassphrase; + WPushButton * m_btnApply; + WText * m_lblPassphrase; + WString m_keyFpr; + +}; + +#endif /* POPUPEXPIRYTIME_H */ + diff --git a/README.md b/README.md index 368ca02..1f75ca4 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ It is developed in C++ as a NetBeans project on the [WebToolkit](https://www.webtoolkit.eu/)(Wt) libraries. -It allows to view, import, delete and certify keys. Certification trust level can also be changed. -Key generation, changing expiry date and adding user identities are not (yet) implemented. +It allows to view, import, delete and certify keys. Certification trust level and secret key expiry date can also be changed. +Key generation, exporting keys and adding user identities are not (yet) implemented. These keys can then be used by other Wt applications, or applications based on other libraries, to encrypt and sign data. As such, it suits my personal needs. diff --git a/WTAPPROOT/K7/K7.xml b/WTAPPROOT/K7/K7.xml index e61e4ef..d32d86f 100644 --- a/WTAPPROOT/K7/K7.xml +++ b/WTAPPROOT/K7/K7.xml @@ -107,4 +107,9 @@ Expired Revocation + Expiry date + Expiration time succesfully changed + Set expiration time failed + Close + \ No newline at end of file diff --git a/WTAPPROOT/K7/K7_fr.xml b/WTAPPROOT/K7/K7_fr.xml index 12e32d7..d82d5ed 100644 --- a/WTAPPROOT/K7/K7_fr.xml +++ b/WTAPPROOT/K7/K7_fr.xml @@ -107,4 +107,9 @@ Expiré Revocation + Date d'expiration + Date d'expiration changée avec succès + Echec de changement de la date d'expiration + Fermer + diff --git a/nbproject/Makefile-ARM-Release.mk b/nbproject/Makefile-ARM-Release.mk index bcd94e8..ff9a183 100644 --- a/nbproject/Makefile-ARM-Release.mk +++ b/nbproject/Makefile-ARM-Release.mk @@ -43,6 +43,7 @@ OBJECTFILES= \ ${OBJECTDIR}/LoopbackPassphraseProvider.o \ ${OBJECTDIR}/PopupCertifyUserId.o \ ${OBJECTDIR}/PopupDeleter.o \ + ${OBJECTDIR}/PopupExpiryTime.o \ ${OBJECTDIR}/PopupUploader.o \ ${OBJECTDIR}/SensitiveTreeTableNodeText.o \ ${OBJECTDIR}/Tools.o \ @@ -114,6 +115,11 @@ ${OBJECTDIR}/PopupDeleter.o: PopupDeleter.cpp ${RM} "$@.d" $(COMPILE.cc) -O2 -s -DLARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -I/usr/local/Wt/include -I/usr/include/gpgme++ -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/PopupDeleter.o PopupDeleter.cpp +${OBJECTDIR}/PopupExpiryTime.o: PopupExpiryTime.cpp + ${MKDIR} -p ${OBJECTDIR} + ${RM} "$@.d" + $(COMPILE.cc) -O2 -s -DLARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -I/usr/local/Wt/include -I/usr/include/gpgme++ -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/PopupExpiryTime.o PopupExpiryTime.cpp + ${OBJECTDIR}/PopupUploader.o: PopupUploader.cpp ${MKDIR} -p ${OBJECTDIR} ${RM} "$@.d" diff --git a/nbproject/Makefile-Debug.mk b/nbproject/Makefile-Debug.mk index 592fb2b..5777086 100644 --- a/nbproject/Makefile-Debug.mk +++ b/nbproject/Makefile-Debug.mk @@ -43,6 +43,7 @@ OBJECTFILES= \ ${OBJECTDIR}/LoopbackPassphraseProvider.o \ ${OBJECTDIR}/PopupCertifyUserId.o \ ${OBJECTDIR}/PopupDeleter.o \ + ${OBJECTDIR}/PopupExpiryTime.o \ ${OBJECTDIR}/PopupUploader.o \ ${OBJECTDIR}/SensitiveTreeTableNodeText.o \ ${OBJECTDIR}/Tools.o \ @@ -114,6 +115,11 @@ ${OBJECTDIR}/PopupDeleter.o: PopupDeleter.cpp ${RM} "$@.d" $(COMPILE.cc) -g -DDEVTIME -I/usr/local/Wt-Debug/include -I/usr/include/gpgme++ -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/PopupDeleter.o PopupDeleter.cpp +${OBJECTDIR}/PopupExpiryTime.o: PopupExpiryTime.cpp + ${MKDIR} -p ${OBJECTDIR} + ${RM} "$@.d" + $(COMPILE.cc) -g -DDEVTIME -I/usr/local/Wt-Debug/include -I/usr/include/gpgme++ -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/PopupExpiryTime.o PopupExpiryTime.cpp + ${OBJECTDIR}/PopupUploader.o: PopupUploader.cpp ${MKDIR} -p ${OBJECTDIR} ${RM} "$@.d" diff --git a/nbproject/Makefile-Release.mk b/nbproject/Makefile-Release.mk index 4959e92..8b95ec3 100644 --- a/nbproject/Makefile-Release.mk +++ b/nbproject/Makefile-Release.mk @@ -43,6 +43,7 @@ OBJECTFILES= \ ${OBJECTDIR}/LoopbackPassphraseProvider.o \ ${OBJECTDIR}/PopupCertifyUserId.o \ ${OBJECTDIR}/PopupDeleter.o \ + ${OBJECTDIR}/PopupExpiryTime.o \ ${OBJECTDIR}/PopupUploader.o \ ${OBJECTDIR}/SensitiveTreeTableNodeText.o \ ${OBJECTDIR}/Tools.o \ @@ -114,6 +115,11 @@ ${OBJECTDIR}/PopupDeleter.o: PopupDeleter.cpp ${RM} "$@.d" $(COMPILE.cc) -O2 -s -I/usr/local/Wt/include -I/usr/include/gpgme++ -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/PopupDeleter.o PopupDeleter.cpp +${OBJECTDIR}/PopupExpiryTime.o: PopupExpiryTime.cpp + ${MKDIR} -p ${OBJECTDIR} + ${RM} "$@.d" + $(COMPILE.cc) -O2 -s -I/usr/local/Wt/include -I/usr/include/gpgme++ -MMD -MP -MF "$@.d" -o ${OBJECTDIR}/PopupExpiryTime.o PopupExpiryTime.cpp + ${OBJECTDIR}/PopupUploader.o: PopupUploader.cpp ${MKDIR} -p ${OBJECTDIR} ${RM} "$@.d" diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index acb0829..4408b73 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -12,6 +12,7 @@ LoopbackPassphraseProvider.h PopupCertifyUserId.h PopupDeleter.h + PopupExpiryTime.h PopupUploader.h SensitiveTreeTableNodeText.h Tools.h @@ -37,6 +38,7 @@ LoopbackPassphraseProvider.cpp PopupCertifyUserId.cpp PopupDeleter.cpp + PopupExpiryTime.cpp PopupUploader.cpp SensitiveTreeTableNodeText.cpp Tools.cpp @@ -129,6 +131,10 @@ + + + + @@ -229,6 +235,10 @@ + + + + @@ -333,6 +343,10 @@ + + + +