Merge branch 'master' into TestExportPrivateKeys

This commit is contained in:
SET
2020-11-24 20:13:23 +01:00
19 changed files with 422 additions and 115 deletions

57
GpgMELogger.h Normal file
View File

@@ -0,0 +1,57 @@
/*
* 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 <Wt/WLogger.h>
#include <Wt/WApplication.h>
#include <Wt/WEnvironment.h>
#include <Wt/WSslCertificate.h>
#include <Wt/WSslInfo.h>
#include <gpgme++/error.h>
#include <mutex>
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<std::mutex> lock(gs_logGpgMEError);
const vector<WSslCertificate::DnAttribute> * 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();
}
#endif /* GPGMELOGGER_H */

View File

@@ -145,15 +145,19 @@ const Error GpgMEWorker::CertifyKey(const char* fprSigningKey,
return e;
}
const Error GpgMEWorker::SetExpiryTime(const char * keyFpr,
const string& passphrase,
const string& timeString)
const Error GpgMEWorker::RevokeKeyCertifications(const char* fprSigningKey,
const char* fprKeyToSign,
vector<GpgME::UserID>& userIDsToRevoke,
const string& passphrase)
{
Error e;
Key k = FindKey(keyFpr, e, true);
Key signingKey = FindKey(fprSigningKey, e, true);
if (e.code() != 0)
return e;
e = m_ctx->addSigningKey(k); // +++
e = m_ctx->addSigningKey(signingKey); // +++
if (e.code() != 0)
return e;
Key keyToSign = FindKey(fprKeyToSign, e, false);
if (e.code() != 0)
return e;
@@ -163,15 +167,49 @@ const Error GpgMEWorker::SetExpiryTime(const char * keyFpr,
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<SetExpiryTimeEditInteractor> (interactor), d);
e = m_ctx->revokeSignature(keyToSign, signingKey, userIDsToRevoke);
m_ctx->clearSigningKeys();
return e;
}
const Error GpgMEWorker::SetKeyExpiryTime(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<GpgME::Subkey> 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);
// setExpire() allows to expire all subkeys at once. Not implemented here.
e = m_ctx->setExpire(k, expires, subkey);
return e;
}
const Error GpgMEWorker::AddUserID(const char* keyFpr, const string& passphrase,
const string& name, const string& email,
const string& comment)

View File

@@ -78,13 +78,32 @@ public:
vector<uint>& userIDsToSign, int options,
const string& passphrase);
/**
* Set new expiry time of a secret key.
* @param timeString
* Revoke UserID certifications.
* \n Requires GnuPG >= 2.2.24
* @param fprSigningKey
* @param fprKeyToSign
* @param userIDsToRevoke : vector of ::UserID
* @param passphrase
* @return
*/
const Error SetExpiryTime(const char * keyFpr,
const Error RevokeKeyCertifications(const char * fprSigningKey,
const char * fprKeyToSign,
vector<GpgME::UserID>& userIDsToRevoke,
const string& passphrase);
/**
* 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 SetKeyExpiryTime(const char * keyFpr,
const char * subkeyFpr,
const string& passphrase,
const string& timeString = "0");
ulong expires = 63072000);
/**
* Adds a user identity to a key.
* \n The email parameter must have a valid email address format here, else
@@ -225,20 +244,6 @@ public:
};
class SetExpiryTimeEditInteractor : public GpgSetExpiryTimeEditInteractor
{
public:
SetExpiryTimeEditInteractor(const std::string& timeString = "0")
: GpgSetExpiryTimeEditInteractor(timeString)
{
};
virtual ~SetExpiryTimeEditInteractor()
{
};
};
class AddUserIDEditInteractor : public GpgAddUserIDEditInteractor
{
public:

View File

@@ -21,6 +21,7 @@
#include "GpgMECWorker.h"
#include "Tools.h"
#include "SensitiveTreeTableNodeText.h"
#include "GpgMELogger.h"
using namespace std;
@@ -83,7 +84,9 @@ K7Main::Create()
WContainerWidget * cwHeader = new WContainerWidget();
WHBoxLayout * hblHeader = new WHBoxLayout();
cwHeader->setLayout(unique_ptr<WHBoxLayout> (hblHeader));
WText * lblTitle =
hblHeader->addWidget(cpp14::make_unique<WText>(_APPNAME_));
lblTitle->setStyleClass("title");
// Error messages will go here
m_tmwMessage = new TransientMessageWidget();
m_tmwMessage->setTextAlignment(AlignmentFlag::Right);
@@ -204,6 +207,7 @@ void K7Main::Search()
{
privkList.clear();
m_tmwMessage->SetText(e.asString());
LGE(e);
return;
}
/*
@@ -229,6 +233,7 @@ void K7Main::Search()
{
pubkList.clear();
m_tmwMessage->SetText(e.asString());
LGE(e);
return;
}
}
@@ -293,6 +298,10 @@ void K7Main::DisplayKeys(const vector<GpgME::Key>& kList, const WString& grpLabe
}
keyNode->setColumnWidget(2, unique_ptr<WText> (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<TreeTableNodeText> (ttntFpr));
grpNode->addChildNode(unique_ptr<WTreeTableNode> (keyNode));
}
@@ -328,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)
@@ -355,6 +365,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<WText> (lblUidEmail));
// Show key certify popup on double click
WText * lblUidValidity = new WText(UidValidities[uid.validity()]);
@@ -376,6 +388,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<TreeTableNodeText> (ttntEmail));
WString exp = TR("Expiration") + _SPACE_ + _COLON_ + _SPACE_;
exp += sig.neverExpires() ? TR("Never") : MakeDateTimeLabel(sig.expirationTime());
@@ -398,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)
@@ -422,13 +437,18 @@ 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<TreeTableNodeText> (ttntFpr));
WString exp = sk.neverExpires() ? TR("Never") : MakeDateTimeLabel(sk.expirationTime());
WText * lblExpiry = new WText(exp);
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<WText> (lblExpiry));
WString usage = sk.canAuthenticate() ? WString("A") : WString::Empty;

View File

@@ -13,6 +13,7 @@
#include <Wt/WStandardItem.h>
#include "GpgMEWorker.h"
#include "Tools.h"
#include "GpgMELogger.h"
using namespace std;
@@ -20,7 +21,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 +30,7 @@ KeyEdit::KeyEdit(K7Main * owner)
KeyEdit::~KeyEdit()
{
delete m_popupUid;
delete m_popupCertifyUid;
}
void KeyEdit::OnOwnerTrustDoubleClicked(WTreeTableNode * keyNode, bool keyHasSecret)
@@ -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"));
@@ -130,47 +132,65 @@ void KeyEdit::OnUidValidityClicked(WTreeTableNode* uidNode, vector<WString>& 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<WText*> (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<uint>& uidsToSign = m_popupUid->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_popupUid->GetSelectedKey();
const WString keyToSign = m_popupUid->GetKeyToSign();
int options = m_popupUid->GetCertifyOptions();
const WString signingKey = m_popupCertifyUid->GetSelectedKey();
const WString keyToSign = m_popupCertifyUid->GetKeyToSign();
GpgMEWorker gpgWorker;
GpgME::Error e = gpgWorker.CertifyKey(signingKey.toUTF8().c_str(),
GpgME::Error e;
if (m_popupCertifyUid->WhatToDo() == PopupCertifyUserId::CertifyUid)
{
vector<uint>& uidsToSign = m_popupCertifyUid->GetUidsToSign();
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<GpgME::UserID> uidsToRevoke
= m_popupCertifyUid->GetUidsToRevokeCertification();
e = gpgWorker.RevokeKeyCertifications(signingKey.toUTF8().c_str(),
keyToSign.toUTF8().c_str(),
uidsToRevoke,
m_popupCertifyUid->GetPassphrase());
}
if (e.code() != 0)
{
m_owner->m_tmwMessage->SetText(TR("CertificationFailure"));
m_popupUid->ShowPassphrase(true);
m_owner->m_tmwMessage->SetText(e.asString());
m_popupCertifyUid->ShowPassphrase(true);
LGE(e);
return;
}
if (m_popupCertifyUid->WhatToDo() == PopupCertifyUserId::CertifyUid)
m_owner->m_tmwMessage->SetText(TR("CertificationSuccess"));
m_popupUid->ShowPassphrase(false);
else
m_owner->m_tmwMessage->SetText(TR("RevocationSuccess"));
m_popupCertifyUid->ShowPassphrase(false);
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)
{
@@ -179,21 +199,29 @@ 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 = gpgWorker.SetExpiryTime(m_expiryEditedKeyFpr.toUTF8().c_str(),
GpgME::Error e;
const WString keyFpr = m_popupExpiryTime->GetKeyFpr();
WString subkeyFpr = m_popupExpiryTime->GetSubkeyFpr();
if (keyFpr == subkeyFpr)
subkeyFpr = WString::Empty;
e = gpgWorker.SetKeyExpiryTime(keyFpr.toUTF8().c_str(),
subkeyFpr.toUTF8().c_str(),
m_popupExpiryTime->GetPassphrase(),
m_popupExpiryTime->GetExpiryTime());
m_popupExpiryTime->GetExpiry());
if (e.code() != 0)
{
m_owner->m_tmwMessage->SetText(TR("SetExpirationTimeFailure"));
m_popupExpiryTime->ShowPassphrase(true);
LGE(e);
return;
}
m_owner->m_tmwMessage->SetText(TR("SetExpirationTimeSuccess"));
@@ -250,6 +278,7 @@ void KeyEdit::AddOrRevokeUid()
{
m_popupAddUid->ShowPassphrase(true);
m_owner->m_tmwMessage->SetText(e.asString());
LGE(e);
}
else
{

View File

@@ -39,7 +39,7 @@ private:
virtual ~KeyEdit();
K7Main * m_owner;
PopupCertifyUserId * m_popupUid;
PopupCertifyUserId * m_popupCertifyUid;
WString m_targetUidValidityKeyFpr;
PopupExpiryTime * m_popupExpiryTime;
@@ -57,8 +57,8 @@ private:
* @param keyHasSecret
*/
void FillOwnerTrustCombo(WComboBox * cmb, bool keyHasSecret);
void CertifyKey();
void SetExpiryTime();
void EditUidValidity();
void SetKeyExpiryTime();
void AddOrRevokeUid();
/**
@@ -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);

View File

@@ -13,6 +13,7 @@
#include "Tools.h"
#include <Wt/WLink.h>
#include <strstream>
#include "GpgMELogger.h"
using namespace std;
@@ -81,6 +82,7 @@ void KeyringIO::DoImportKey()
if (e.code() != 0)
{
m_tmwMessage->SetText(e.asString());
LGE(e);
return;
}
if (fpr.empty())
@@ -93,6 +95,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()));
@@ -111,6 +114,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
@@ -168,6 +172,7 @@ void KeyringIO::DoDeleteKey()
if (e.code() != 0)
{
m_tmwMessage->SetText(e.asString());
LGE(e);
return;
}
// Delete the key using the C API
@@ -234,6 +239,7 @@ void KeyringIO::DoCreateKey()
if (e.code() != 0)
{
m_tmwMessage->SetText(e.asString());
LGE(e);
}
else
{
@@ -337,8 +343,6 @@ void ExportKeyStreamResource::handleRequest(const Http::Request& request,
*/
string buffer;
if (!request.continuation()) // Needed for WStreamResource ?
{
Error e;
GpgMEWorker gpgw;
e = m_isSecret
@@ -348,10 +352,10 @@ 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);
}
istrstream bufStream(buffer.c_str());
handleRequestPiecewise(request, response, bufStream);

View File

@@ -12,7 +12,10 @@
#include "Tools.h"
#include <Wt/WStandardItem.h>
#include <Wt/WStandardItemModel.h>
#include <Wt/WRadioButton.h>
#include <iostream>
#include <algorithm>
#include "GpgMELogger.h"
using namespace std;
@@ -26,6 +29,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;
@@ -63,10 +67,10 @@ void PopupCertifyUserId::Create(vector<WString>& privateKeys,
m_hblPreferences->addLayout(unique_ptr<WVBoxLayout> (m_vblEmail));
PresentEmail();
// Column 1
m_gbOptions = new WGroupBox(TR("Options"));
WVBoxLayout * vblOptions = new WVBoxLayout();
m_hblPreferences->addLayout(unique_ptr<WVBoxLayout> (vblOptions));
WText * lblOptions = new WText(TR("Options"));
vblOptions->addWidget(unique_ptr<WText> (lblOptions));
m_gbOptions->setLayout(unique_ptr<WVBoxLayout> (vblOptions));
m_hblPreferences->addWidget(unique_ptr<WGroupBox> (m_gbOptions));
m_cbOptionExportable = new WCheckBox(TR("ExportableCertification"));
m_cbOptionExportable->setToolTip(TR("OneWayHint"));
vblOptions->addWidget(unique_ptr<WCheckBox> (m_cbOptionExportable));
@@ -74,7 +78,7 @@ void PopupCertifyUserId::Create(vector<WString>& privateKeys,
m_cbOptionNonRevocable->setToolTip(TR("OneWayHint"));
vblOptions->addWidget(unique_ptr<WCheckBox> (m_cbOptionNonRevocable));
/*m_cbOptionTrust = new WCheckBox(TR("TrustCertification"));
vblOptions->addWidget(unique_ptr<WCheckBox> (m_cbOptionTrust));*/
gbOptions->addWidget(unique_ptr<WCheckBox> (m_cbOptionTrust));*/
WHBoxLayout * hblPassphrase = new WHBoxLayout();
m_lblPassphrase = new WText(TR("Passphrase"));
@@ -84,6 +88,21 @@ void PopupCertifyUserId::Create(vector<WString>& privateKeys,
hblPassphrase->addWidget(unique_ptr<WLineEdit> (m_lePassphrase), 1);
vblMain->addLayout(unique_ptr<WHBoxLayout> (hblPassphrase));
WHBoxLayout * hblWhat = new WHBoxLayout();
WRadioButton * rbCertifyUid = new WRadioButton(TR("CertifyUid"));
hblWhat->addWidget(unique_ptr<WRadioButton> (rbCertifyUid));
WRadioButton * rbRevokeCertification =
new WRadioButton(TR("RevokeUidCertification"));
hblWhat->addWidget(unique_ptr<WRadioButton> (rbRevokeCertification));
m_bgWhat = make_shared<WButtonGroup>();
m_bgWhat->addButton(rbCertifyUid, What::CertifyUid);
m_bgWhat->addButton(rbRevokeCertification, What::RevokeUidCertification);
m_bgWhat->setCheckedButton(rbCertifyUid);
vblMain->addLayout(unique_ptr<WHBoxLayout> (hblWhat));
m_cbConfirm = new WCheckBox(TR("Confirm"));
vblMain->addWidget(unique_ptr<WCheckBox> (m_cbConfirm));
WHBoxLayout * hblButtons = new WHBoxLayout();
WPushButton * btnClose = new WPushButton(TR("Close"));
hblButtons->addWidget(unique_ptr<WPushButton> (btnClose));
@@ -99,6 +118,7 @@ void PopupCertifyUserId::Create(vector<WString>& 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<WString>& privateKeys)
@@ -116,6 +136,7 @@ void PopupCertifyUserId::FillPrivateKeyComboBox(vector<WString>& privateKeys)
if (e.code() != 0)
{
m_tmwMessage->SetText(e.asString());
LGE(e);
return;
}
/*
@@ -158,6 +179,7 @@ void PopupCertifyUserId::PresentEmail()
if (e.code() != 0)
{
m_tmwMessage->SetText(e.asString());
LGE(e);
return;
}
if (lst.size() != 1)
@@ -175,8 +197,8 @@ void PopupCertifyUserId::PresentEmail()
WCheckBox * cbEmail = new WCheckBox(it->email());
m_vblEmail->addWidget(unique_ptr<WCheckBox> (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 +242,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<uint>::iterator it;
for (it = m_uidsToSign.begin(); it != m_uidsToSign.end(); it++)
{
if ((*it) == id)
{
vector<uint>::iterator it =
std::find(m_uidsToSign.begin(), m_uidsToSign.end(), id);
if (it != m_uidsToSign.end())
m_uidsToSign.erase(it);
return;
vector<GpgME::UserID>::iterator uit;
for (uit = m_uidsToRevokeCertification.begin();
uit != m_uidsToRevokeCertification.end(); uit++)
{
if ((*uit).uidhash() == uid.uidhash())
{
m_uidsToRevokeCertification.erase(uit);
break;
}
}
}
void PopupCertifyUserId::OnCertifyOptionChecked(int id)
@@ -250,3 +279,27 @@ void PopupCertifyUserId::OnCertifyOptionUnChecked(int id)
{
m_certifyOptions -= id;
}
void PopupCertifyUserId::OnButtonGroupWhat(WRadioButton* btn)
{
m_gbOptions->setDisabled(m_bgWhat->checkedId()
== 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;
}

View File

@@ -19,28 +19,32 @@
#include <Wt/WLineEdit.h>
#include <Wt/WVBoxLayout.h>
#include <Wt/WHBoxLayout.h>
#include <Wt/WGroupBox.h>
#include <Wt/WButtonGroup.h>
#include <vector>
#include <gpgme++/key.h>
#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 :
* <ul>
* <li>Signer's private keys</li>
* <li>Target key user identities (email)</li>
* <li>Signing options : Exportable and non-revocable</li>
* <li>Passphrase for selected private key</li>
* </ul>
* 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<GpgME::UserID>& 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
* <ul>
@@ -124,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;
@@ -138,10 +168,14 @@ private:
// WCheckBox * m_cbOptionTrust; // Always fails
WText * m_lblPassphrase;
WLineEdit * m_lePassphrase;
WCheckBox * m_cbConfirm;
WPushButton * m_btnApply;
vector<uint> m_uidsToSign;
vector<GpgME::UserID> m_uidsToRevokeCertification;
int m_certifyOptions;
WGroupBox * m_gbOptions;
shared_ptr<WButtonGroup> m_bgWhat;
/**
* Available private fingerprints in a combobox. The selected item is the
* signing key.
@@ -154,15 +188,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 +209,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 */

View File

@@ -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

View File

@@ -26,6 +26,21 @@ public:
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
@@ -63,6 +85,7 @@ private:
WPushButton * m_btnApply;
WText * m_lblPassphrase;
WString m_keyFpr;
WString m_subkeyFpr;
};

View File

@@ -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<WLineEdit> (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<TreeTableNodeText> (m_text));
m_node->setColumnWidget(m_colIndex, std::move(m_sourceWidget));
}
void TreeTableNodeLineEdit::HoldSourceWidget(std::unique_ptr<WWidget> sourceWidget)
{
m_sourceWidget.swap(sourceWidget);
}

View File

@@ -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<WWidget> sourceWidget);
private:
WTreeTableNode * m_node;
uint m_colIndex;
TreeTableNodeText * m_text;
std::unique_ptr<WWidget> m_sourceWidget;
void Init(WTreeTableNode * node, uint colIndex);
};

View File

@@ -10,6 +10,7 @@
#include "Tools.h"
#include <sstream>
#include <iostream>
#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());

View File

@@ -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;
}

View File

@@ -83,8 +83,10 @@
<message id='NoUidSelected'>No email address is selected</message>
<message id='Apply'>Apply</message>
<message id='CertificationSuccess'>Key succesfully certified</message>
<message id='CertificationFailure'>Key certification failed</message>
<message id='RevocationSuccess'>Identity certification succesfully revoked</message>
<message id='OneWayHint'>Once set, this option cannot be removed. Use gpg cli for further edit.</message>
<message id='CertifyUid'>Certify identities</message>
<message id='RevokeUidCertification'>Revoke certifications</message>
<message id='PrepareCopy'>Click to be able to copy next</message>

View File

@@ -83,8 +83,10 @@
<message id='NoUidSelected'>Aucun courriel n'est sélectionné</message>
<message id='Apply'>Appliquer</message>
<message id='CertificationSuccess'>Succès de certification de la clé</message>
<message id='CertificationFailure'>Échec de certification de la clé</message>
<message id='RevocationSuccess'>Succès de révocation de l'identité</message>
<message id='OneWayHint'>Une fois appliquée, cette option ne pourra plus être enlevée. Utilisez gpg en ligne de commande pour édition ultérieure.</message>
<message id='CertifyUid'>Certifier les identités</message>
<message id='RevokeUidCertification'>Revoquer les certifications</message>
<message id='PrepareCopy'>Cliquez pour pouvoir ensuite copier</message>

View File

@@ -6,6 +6,7 @@
projectFiles="true">
<itemPath>AppConfig.h</itemPath>
<itemPath>GpgMECWorker.h</itemPath>
<itemPath>GpgMELogger.h</itemPath>
<itemPath>GpgMEWorker.h</itemPath>
<itemPath>K7Main.h</itemPath>
<itemPath>KeyEdit.h</itemPath>
@@ -104,6 +105,8 @@
</item>
<item path="GpgMECWorker.h" ex="false" tool="3" flavor2="0">
</item>
<item path="GpgMELogger.h" ex="false" tool="3" flavor2="0">
</item>
<item path="GpgMEWorker.cpp" ex="false" tool="1" flavor2="0">
</item>
<item path="GpgMEWorker.h" ex="false" tool="3" flavor2="0">
@@ -224,6 +227,8 @@
</item>
<item path="GpgMECWorker.h" ex="false" tool="3" flavor2="0">
</item>
<item path="GpgMELogger.h" ex="false" tool="3" flavor2="0">
</item>
<item path="GpgMEWorker.cpp" ex="false" tool="1" flavor2="0">
</item>
<item path="GpgMEWorker.h" ex="false" tool="3" flavor2="0">
@@ -348,6 +353,8 @@
</item>
<item path="GpgMECWorker.h" ex="false" tool="3" flavor2="0">
</item>
<item path="GpgMELogger.h" ex="false" tool="3" flavor2="0">
</item>
<item path="GpgMEWorker.cpp" ex="false" tool="1" flavor2="0">
</item>
<item path="GpgMEWorker.h" ex="false" tool="3" flavor2="0">

View File

@@ -13,8 +13,6 @@
<gdb_interceptlist>
<gdbinterceptoptions gdb_all="false" gdb_unhandled="true" gdb_unexpected="true"/>
</gdb_interceptlist>
<gdb_signals>
</gdb_signals>
<gdb_options>
<DebugOptions>
</DebugOptions>