diff --git a/GpgMECWorker.cpp b/GpgMECWorker.cpp index 414b52c..1a76538 100644 --- a/GpgMECWorker.cpp +++ b/GpgMECWorker.cpp @@ -9,37 +9,6 @@ #include "GpgMECWorker.h" -#ifdef DEVTIME - -/** - * This callback is never called when exporting private keys. - * From gpgme-1.14.0/tests/run-support.h - */ -gpgme_error_t passphrase_cb(void *opaque, const char *uid_hint, const char *passphrase_info, - int last_was_bad, int fd) -{ - int res; - char pass[] = "test_pwd\n"; - int passlen = strlen(pass); - int off = 0; - - (void) opaque; - (void) uid_hint; - (void) passphrase_info; - (void) last_was_bad; - - do - { - res = gpgme_io_write(fd, &pass[off], passlen - off); - if (res > 0) - off += res; - } - while (res > 0 && off != passlen); - - return off == passlen ? 0 : gpgme_error_from_errno(errno); -} -#endif - GpgMECWorker::GpgMECWorker() { gpgme_error_t c_err = gpgme_new(&c_ctx); @@ -68,66 +37,3 @@ bool GpgMECWorker::DeleteKey(const char * fpr, bool secret, GpgME::Error& e) } return true; } - -#ifdef DEVTIME - -/** - * Much code here is obtained from gpgme-1.14.0/tests/run-export.c - * @param fpr - * @param e - * @return - */ -bool GpgMECWorker::ExportPrivateKey(const char* fpr, GpgME::Error& e) -{ - gpgme_key_t c_key; - gpgme_data_t out; - gpgme_set_armor(c_ctx, 1); - - gpgme_error_t c_err = gpgme_get_key(c_ctx, fpr, &c_key, true); - if (c_key == NULL) - { - e = GpgME::Error::fromCode(c_err); - return false; - } - - FILE * kFp = fopen("/tmp/data", "wb"); - if (kFp == NULL) - return false; - c_err = gpgme_data_new_from_stream(&out, kFp); - if (c_err != 0) - { - fclose(kFp); - e = GpgME::Error::fromCode(c_err); - return false; - } - - gpgme_key_t aKeys[2]; - aKeys[0] = c_key; - aKeys[1] = NULL; - - c_err = gpgme_set_pinentry_mode(c_ctx, GPGME_PINENTRY_MODE_LOOPBACK); - if (c_err != 0) - { - fclose(kFp); - e = GpgME::Error::fromCode(c_err); - return false; - } - gpgme_set_passphrase_cb(c_ctx, passphrase_cb, NULL); - - gpgme_export_mode_t flags = GPGME_EXPORT_MODE_SECRET; - c_err = gpgme_op_export_keys(c_ctx, aKeys, flags, out); - if (c_err != 0) - { - fclose(kFp); - gpgme_data_release(out); - e = GpgME::Error::fromCode(c_err); - return false; - } - fflush(NULL); - fclose(kFp); - - gpgme_data_release(out); - - return true; -} -#endif diff --git a/GpgMECWorker.h b/GpgMECWorker.h index 90c2d8e..cd39369 100644 --- a/GpgMECWorker.h +++ b/GpgMECWorker.h @@ -31,20 +31,6 @@ public: */ bool DeleteKey(const char * fpr, bool secret, GpgME::Error& e); -#ifdef DEVTIME - /** - * Status : testing - * \n Result : fails to export a private key - * \n Reason : passphrase callback is never called - * \n With default pinentry mode, the password is requested normally - * and the private key is exported. But this can't be done on a web server. - * @param fpr - * @param e - * @return - */ - bool ExportPrivateKey(const char* fpr, GpgME::Error& e); -#endif - private: gpgme_ctx_t c_ctx; }; diff --git a/GpgMEWorker.cpp b/GpgMEWorker.cpp index afe35b3..ee8d7e0 100644 --- a/GpgMEWorker.cpp +++ b/GpgMEWorker.cpp @@ -308,8 +308,6 @@ const Error GpgMEWorker::CreateSubKey(GpgME::Key& k, return e; } -#ifdef DEVTIME - const Error GpgMEWorker::ExportPrivateKey(const char * pattern, string& buffer, const string& passphrase) { @@ -324,14 +322,13 @@ const Error GpgMEWorker::ExportPrivateKey(const char * pattern, string& buffer, uint flags = Context::ExportSecret; Error e = ctx->exportPublicKeys(pattern, kData, flags); - buffer = kData.toString(); // Empty + buffer = kData.toString(); delete ppp; delete ctx; return e; } -#endif const Error GpgMEWorker::ExportPublicKey(const char* pattern, string& buffer) { diff --git a/GpgMEWorker.h b/GpgMEWorker.h index d2fb62d..55f29cc 100644 --- a/GpgMEWorker.h +++ b/GpgMEWorker.h @@ -162,21 +162,14 @@ public: const char * algo, const string& passphrase, ulong expires = 63072000); - -#ifdef DEVTIME /** - * Status : testing - * \n Result : fails to export a private key - * \n Reason : loopback passphrase provider is never called - * \n With default pinentry mode, the password is requested normally - * and the private key is exported. But this can't be done on a web server. - * @param fpr - * @param e + * Export a secret key. + * @param pattern : a key fingerprint + * @param buffer : returns data in armor mode * @return */ const Error ExportPrivateKey(const char * pattern, string& buffer, const string& passphrase = ""); -#endif /** * Export a public key. * @param pattern : a key fingerprint diff --git a/K7Main.cpp b/K7Main.cpp index 53394cf..def4cfd 100644 --- a/K7Main.cpp +++ b/K7Main.cpp @@ -142,7 +142,6 @@ K7Main::Create() vblButtons->addStretch(1); // Everyone can export a key m_btnExport = new WPushButton(TR("Export")); - m_btnExport->setToolTip(TR("TTTExport")); m_btnExport->hide(); vblButtons->addWidget(unique_ptr (m_btnExport)); if (m_config->CanCreateKeys()) @@ -314,7 +313,8 @@ void K7Main::OnKeyAnchorClicked(WAnchor * source) DisplaySubKeys(id, secret); if (m_config->CanDelete()) // m_btnDelete is NULL otherwise m_btnDelete->setHidden(!m_keyringIO->CanKeyBeDeleted(id)); - + + m_btnExport->show(); m_keyringIO->PrepareExport(id, secret); } diff --git a/KeyringIO.cpp b/KeyringIO.cpp index 0914bf7..3697014 100644 --- a/KeyringIO.cpp +++ b/KeyringIO.cpp @@ -22,6 +22,7 @@ KeyringIO::KeyringIO(K7Main * owner) m_popupUpload = NULL; m_popupDelete = NULL; m_popupCreate = NULL; + m_popupExportSecretKey = NULL; m_config = m_owner->m_config; m_tmwMessage = m_owner->m_tmwMessage; m_btnUpload = m_owner->m_btnUpload; @@ -250,18 +251,54 @@ void KeyringIO::DoCreateKey() } } +void KeyringIO::ShowPopupExportSecretKey(const WString& fpr) +{ + m_popupExportSecretKey = new PopupExportSecretKey(m_btnExport, + m_owner->m_tmwMessage); + m_popupExportSecretKey->Create(); + m_popupExportSecretKey->GetPreApplyButton() + ->clicked().connect(std::bind(&KeyringIO::OnPreExportSecretKey, + this, fpr)); + m_popupExportSecretKey->show(); +} + void KeyringIO::PrepareExport(const WString& fpr, bool isSecret) { + // K7Main::m_btnExport + m_btnExport->setLink(WLink()); + m_btnExport->clicked().disconnect(m_exportSecretConnection); + if (isSecret) + { + m_exportSecretConnection = m_btnExport->clicked().connect + (std::bind(&KeyringIO::ShowPopupExportSecretKey, + this, fpr)); + } + else + { + WLink link; + shared_ptr shResource = + make_shared + (fpr, isSecret, "appliation/pgp-keys", m_tmwMessage); + link.setResource(shResource); + m_btnExport->setLink(link); + } + // Never reuse when selecting keys. + delete m_popupExportSecretKey; + m_popupExportSecretKey = NULL; +} + +void KeyringIO::OnPreExportSecretKey(const WString& fpr) +{ + // On preExport button of popup WLink link; shared_ptr shResource = make_shared - (fpr, isSecret, "appliation/pgp-keys", m_tmwMessage); + (fpr, true, "appliation/pgp-keys", m_tmwMessage); link.setResource(shResource); - m_btnExport->setLink(link); - if (isSecret) - m_btnExport->hide(); - else - m_btnExport->show(); + shResource->SetPassphrase(m_popupExportSecretKey->GetPassphrase()); + m_popupExportSecretKey->GetApplyButton()->setLink(link); + m_popupExportSecretKey->GetApplyButton()->enable(); + } ExportKeyStreamResource::ExportKeyStreamResource(const WString& fpr, @@ -271,6 +308,7 @@ ExportKeyStreamResource::ExportKeyStreamResource(const WString& fpr, { m_fpr = fpr; m_isSecret = isSecret; + m_passphrase = WString::Empty; m_tmwMessage = tmw; } @@ -282,6 +320,7 @@ ExportKeyStreamResource::ExportKeyStreamResource(const WString& fpr, { m_fpr = fpr; m_isSecret = isSecret; + m_passphrase = WString::Empty; m_tmwMessage = tmw; } @@ -294,20 +333,18 @@ void ExportKeyStreamResource::handleRequest(const Http::Request& request, Http::Response& response) { /* - * Private keys cannot be exported with loopback pinentry. - * Let's hope it gets better someday. + * Private keys can be exported as from GPGME 1.15.0. */ - if (m_isSecret) - { - m_tmwMessage->SetText(TR("TTTExport")); - return; - } + string buffer; if (!request.continuation()) // Needed for WStreamResource ? { Error e; GpgMEWorker gpgw; - e = gpgw.ExportPublicKey(m_fpr.toUTF8().c_str(), buffer); + e = m_isSecret + ? gpgw.ExportPrivateKey(m_fpr.toUTF8().c_str(), buffer, + m_passphrase.toUTF8()) + : gpgw.ExportPublicKey(m_fpr.toUTF8().c_str(), buffer); if (e.code() != 0) { m_tmwMessage->SetText(e.asString()); diff --git a/KeyringIO.h b/KeyringIO.h index a5df57f..895425b 100644 --- a/KeyringIO.h +++ b/KeyringIO.h @@ -16,6 +16,7 @@ #include "PopupUploader.h" #include "PopupDeleter.h" #include "PopupCreate.h" +#include "PopupExportSecretKey.h" using namespace Wt; @@ -41,6 +42,7 @@ private: PopupUpload * m_popupUpload; PopupDelete * m_popupDelete; PopupCreate * m_popupCreate; + PopupExportSecretKey * m_popupExportSecretKey; // Variables from m_owner mapped here. AppConfig * m_config; @@ -51,7 +53,9 @@ private: WPushButton * m_btnCreate; WPushButton * m_btnExport; WLineEdit * m_leSearch; - + + // Used to disconnect m_btnExport from previous slot. + Signals::connection m_exportSecretConnection; /** * Shows a non-blocking popup to upload a key, @@ -66,6 +70,10 @@ private: * Shows a non-blocking popup to delete a key */ void ShowPopupDelete(); + /** + * Shows a non-blocking popup to export a secret key + */ + void ShowPopupExportSecretKey(const WString& fpr); /** * All public keys can be deleted. * Private keys can be deleted only if the user @@ -91,8 +99,17 @@ private: * @param spool */ void OnUploadCompleted(const WString& spool); - + /** + * If isSecret is true, shows a popup for passphrase input befort exporting. + * Else, adds a link with a shared resource to the export button. + * @param fpr + * @param isSecret + */ void PrepareExport(const WString& fpr, bool isSecret); + /** + * Adds a link with a shared resource to the export button of the popup. + */ + void OnPreExportSecretKey(const WString& fpr); }; /** @@ -118,10 +135,15 @@ public: */ void handleRequest(const Http::Request& request, Http::Response& response) override; + void SetPassphrase(const WString& passphrase) + { + m_passphrase = passphrase; + } private: WString m_fpr; bool m_isSecret; + WString m_passphrase; TransientMessageWidget * m_tmwMessage; }; diff --git a/PopupExportSecretKey.cpp b/PopupExportSecretKey.cpp new file mode 100644 index 0000000..e24cd6d --- /dev/null +++ b/PopupExportSecretKey.cpp @@ -0,0 +1,58 @@ +/* + * File: PopupExportSecretKey.cpp + * Author: SET - nmset@yandex.com + * License : GPL v2 + * Copyright SET - © 2019 + * + * Created on November 19, 2020, 9:01 PM + */ + +#include "PopupExportSecretKey.h" +#include +#include +#include "global.h" + +PopupExportSecretKey::PopupExportSecretKey(WWidget * anchorWidget, + TransientMessageWidget * txtMessage, + const WLength& width) +: WPopupWidget(cpp14::make_unique()) +{ + m_tmwMessage = txtMessage; + m_cwMain = NULL; + m_lblPassphrase = NULL; + m_lePassphrase = NULL; + m_btnPreApply = NULL; + m_btnApply = NULL; + + setTransient(true); + setAnchorWidget(anchorWidget); + setWidth(width); +} + +PopupExportSecretKey::~PopupExportSecretKey() +{ +} + +void PopupExportSecretKey::Create() +{ + m_cwMain = static_cast (implementation()); + m_cwMain->setStyleClass("popup"); + + WVBoxLayout * vblMain = new WVBoxLayout(); + m_cwMain->setLayout(unique_ptr (vblMain)); + 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)); + vblMain->addLayout(unique_ptr (hblPassphrase)); + + m_btnPreApply = new WPushButton(TR("PreExportSecretKey")); + vblMain->addWidget(unique_ptr (m_btnPreApply)); + m_btnApply = new WPushButton(TR("ExportSecretKey")); + m_btnApply->setToolTip(TR("TTTExportSecretKey")); + vblMain->addWidget(unique_ptr (m_btnApply)); + + m_lePassphrase->keyPressed().connect(m_btnApply, &WPushButton::disable); +} diff --git a/PopupExportSecretKey.h b/PopupExportSecretKey.h new file mode 100644 index 0000000..b5ac831 --- /dev/null +++ b/PopupExportSecretKey.h @@ -0,0 +1,64 @@ +/* + * File: PopupExportSecretKey.h + * Author: SET - nmset@yandex.com + * License : GPL v2 + * Copyright SET - © 2019 + * + * Created on November 19, 2020, 9:01 PM + */ + +#ifndef POPUPEXPORTSECRETKEY_H +#define POPUPEXPORTSECRETKEY_H + +#include +#include +#include +#include +#include "TransientMessageWidget.h" + +using namespace Wt; + +class PopupExportSecretKey : public WPopupWidget +{ +public: + PopupExportSecretKey(WWidget * anchorWidget, + TransientMessageWidget * txtMessage, + const WLength& width = 300); + virtual ~PopupExportSecretKey(); + void Create(); + /** + * Used to forward the passphrase to the loopback passphrase provider. + * @return + */ + const string GetPassphrase() + { + return m_lePassphrase->text().toUTF8(); + } + /** + * Caller binds its function here to add a link to the apply button. + * @return + */ + WPushButton* GetPreApplyButton() + { + return m_btnPreApply; + } + /** + * Expects a link with a shared resource that generates the secret key + * and forwards it to the browser. + * @return + */ + WPushButton* GetApplyButton() + { + return m_btnApply; + } +private: + TransientMessageWidget * m_tmwMessage; + WContainerWidget * m_cwMain; + WLineEdit * m_lePassphrase; + WPushButton * m_btnPreApply; + WPushButton * m_btnApply; + WText * m_lblPassphrase; +}; + +#endif /* POPUPEXPORTSECRETKEY_H */ + diff --git a/WTAPPROOT/K7/K7.xml b/WTAPPROOT/K7/K7.xml index 7dfca67..63f8c0f 100644 --- a/WTAPPROOT/K7/K7.xml +++ b/WTAPPROOT/K7/K7.xml @@ -131,7 +131,9 @@ Create success : Export - Exporting secret keys is technically impossible + Prepare download link + Export secret key + Requires GPGME >= 1.15.0 Add an identity Revoke identity diff --git a/WTAPPROOT/K7/K7_fr.xml b/WTAPPROOT/K7/K7_fr.xml index 04b6746..86d65be 100644 --- a/WTAPPROOT/K7/K7_fr.xml +++ b/WTAPPROOT/K7/K7_fr.xml @@ -131,7 +131,9 @@ Création réussie : Exporter - L'export des clés secrètes est techniquement impossible + Préparer le téléchargement + Exporter la secrète + Requiert GPGME >= 1.15.0 Ajouter une identité Revoquer une identité diff --git a/nbproject/Makefile-ARM-Release.mk b/nbproject/Makefile-ARM-Release.mk index 0ca33b6..e63cb6b 100644 --- a/nbproject/Makefile-ARM-Release.mk +++ b/nbproject/Makefile-ARM-Release.mk @@ -47,6 +47,7 @@ OBJECTFILES= \ ${OBJECTDIR}/PopupCreate.o \ ${OBJECTDIR}/PopupDeleter.o \ ${OBJECTDIR}/PopupExpiryTime.o \ + ${OBJECTDIR}/PopupExportSecretKey.o \ ${OBJECTDIR}/PopupUploader.o \ ${OBJECTDIR}/SensitiveTreeTableNodeText.o \ ${OBJECTDIR}/Tools.o \ @@ -138,6 +139,11 @@ ${OBJECTDIR}/PopupExpiryTime.o: PopupExpiryTime.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}/PopupExpiryTime.o PopupExpiryTime.cpp +${OBJECTDIR}/PopupExportSecretKey.o: PopupExportSecretKey.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}/PopupExportSecretKey.o PopupExportSecretKey.cpp + ${OBJECTDIR}/PopupUploader.o: PopupUploader.cpp ${MKDIR} -p ${OBJECTDIR} ${RM} "$@.d" diff --git a/nbproject/Makefile-Debug.mk b/nbproject/Makefile-Debug.mk index eb7e9ee..b462d0c 100644 --- a/nbproject/Makefile-Debug.mk +++ b/nbproject/Makefile-Debug.mk @@ -47,6 +47,7 @@ OBJECTFILES= \ ${OBJECTDIR}/PopupCreate.o \ ${OBJECTDIR}/PopupDeleter.o \ ${OBJECTDIR}/PopupExpiryTime.o \ + ${OBJECTDIR}/PopupExportSecretKey.o \ ${OBJECTDIR}/PopupUploader.o \ ${OBJECTDIR}/SensitiveTreeTableNodeText.o \ ${OBJECTDIR}/Tools.o \ @@ -138,6 +139,11 @@ ${OBJECTDIR}/PopupExpiryTime.o: PopupExpiryTime.cpp ${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}/PopupExportSecretKey.o: PopupExportSecretKey.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}/PopupExportSecretKey.o PopupExportSecretKey.cpp + ${OBJECTDIR}/PopupUploader.o: PopupUploader.cpp ${MKDIR} -p ${OBJECTDIR} ${RM} "$@.d" diff --git a/nbproject/Makefile-Release.mk b/nbproject/Makefile-Release.mk index 9658138..b2c2219 100644 --- a/nbproject/Makefile-Release.mk +++ b/nbproject/Makefile-Release.mk @@ -47,6 +47,7 @@ OBJECTFILES= \ ${OBJECTDIR}/PopupCreate.o \ ${OBJECTDIR}/PopupDeleter.o \ ${OBJECTDIR}/PopupExpiryTime.o \ + ${OBJECTDIR}/PopupExportSecretKey.o \ ${OBJECTDIR}/PopupUploader.o \ ${OBJECTDIR}/SensitiveTreeTableNodeText.o \ ${OBJECTDIR}/Tools.o \ @@ -138,6 +139,11 @@ ${OBJECTDIR}/PopupExpiryTime.o: PopupExpiryTime.cpp ${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}/PopupExportSecretKey.o: PopupExportSecretKey.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}/PopupExportSecretKey.o PopupExportSecretKey.cpp + ${OBJECTDIR}/PopupUploader.o: PopupUploader.cpp ${MKDIR} -p ${OBJECTDIR} ${RM} "$@.d" diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index d5d6ae2..02abdb8 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -16,6 +16,7 @@ PopupCreate.h PopupDeleter.h PopupExpiryTime.h + PopupExportSecretKey.h PopupUploader.h SensitiveTreeTableNodeText.h Tools.h @@ -45,6 +46,7 @@ PopupCreate.cpp PopupDeleter.cpp PopupExpiryTime.cpp + PopupExportSecretKey.cpp PopupUploader.cpp SensitiveTreeTableNodeText.cpp Tools.cpp @@ -142,6 +144,10 @@ + + + + @@ -258,6 +264,10 @@ + + + + @@ -378,6 +388,10 @@ + + + +