cpp: Add interactor to revoke a key

* lang/cpp/src/global.h (enum class RevocationReason): New.
* lang/cpp/src/gpgrevokekeyeditinteractor.cpp,
lang/cpp/src/gpgrevokekeyeditinteractor.h: New.
* lang/cpp/src/Makefile.am: Add new files.
--

GnuPG-bug-id: 5904
This commit is contained in:
Ingo Klöcker 2022-03-29 15:19:27 +02:00
parent 7317139ef9
commit d96e8a7a6b
5 changed files with 290 additions and 0 deletions

7
NEWS
View File

@ -5,6 +5,13 @@ Noteworthy changes in version 1.17.2 (unreleased)
* cpp, qt: Do not export internal symbols anymore. [T5906] * cpp, qt: Do not export internal symbols anymore. [T5906]
* cpp: Support revocation of own OpenPGP keys. [#5904]
* Interface changes relative to the 1.17.1 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cpp: RevocationReason NEW.
cpp: GpgRevokeKeyEditInteractor NEW.
Noteworthy changes in version 1.17.1 (2022-03-06) Noteworthy changes in version 1.17.1 (2022-03-06)
------------------------------------------------- -------------------------------------------------

View File

@ -34,6 +34,7 @@ main_sources = \
gpgsetownertrusteditinteractor.cpp gpgsignkeyeditinteractor.cpp \ gpgsetownertrusteditinteractor.cpp gpgsignkeyeditinteractor.cpp \
gpgadduserideditinteractor.cpp gpggencardkeyinteractor.cpp \ gpgadduserideditinteractor.cpp gpggencardkeyinteractor.cpp \
gpgaddexistingsubkeyeditinteractor.cpp \ gpgaddexistingsubkeyeditinteractor.cpp \
gpgrevokekeyeditinteractor.cpp \
defaultassuantransaction.cpp \ defaultassuantransaction.cpp \
scdgetinfoassuantransaction.cpp gpgagentgetinfoassuantransaction.cpp \ scdgetinfoassuantransaction.cpp gpgagentgetinfoassuantransaction.cpp \
statusconsumerassuantransaction.cpp \ statusconsumerassuantransaction.cpp \
@ -49,6 +50,7 @@ gpgmepp_headers = \
gpgsetownertrusteditinteractor.h gpgsignkeyeditinteractor.h \ gpgsetownertrusteditinteractor.h gpgsignkeyeditinteractor.h \
gpggencardkeyinteractor.h \ gpggencardkeyinteractor.h \
gpgaddexistingsubkeyeditinteractor.h \ gpgaddexistingsubkeyeditinteractor.h \
gpgrevokekeyeditinteractor.h \
importresult.h keygenerationresult.h key.h keylistresult.h \ importresult.h keygenerationresult.h key.h keylistresult.h \
notation.h result.h scdgetinfoassuantransaction.h signingresult.h \ notation.h result.h scdgetinfoassuantransaction.h signingresult.h \
statusconsumerassuantransaction.h \ statusconsumerassuantransaction.h \

View File

@ -72,6 +72,13 @@ enum KeyListMode {
enum SignatureMode { NormalSignatureMode, Detached, Clearsigned }; enum SignatureMode { NormalSignatureMode, Detached, Clearsigned };
enum class RevocationReason {
Unspecified = 0,
Compromised = 1,
Superseded = 2,
NoLongerUsed = 3
};
GPGMEPP_EXPORT std::ostream &operator<<(std::ostream &os, Protocol proto); GPGMEPP_EXPORT std::ostream &operator<<(std::ostream &os, Protocol proto);
GPGMEPP_EXPORT std::ostream &operator<<(std::ostream &os, Engine eng); GPGMEPP_EXPORT std::ostream &operator<<(std::ostream &os, Engine eng);
GPGMEPP_EXPORT std::ostream &operator<<(std::ostream &os, KeyListMode mode); GPGMEPP_EXPORT std::ostream &operator<<(std::ostream &os, KeyListMode mode);

View File

@ -0,0 +1,212 @@
/*
gpgrevokekeyeditinteractor.cpp - Edit Interactor to revoke own OpenPGP keys
Copyright (c) 2022 g10 Code GmbH
Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
This file is part of GPGME++.
GPGME++ is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
GPGME++ is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with GPGME++; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gpgrevokekeyeditinteractor.h"
#include "error.h"
#include <gpgme.h>
#include <sstream>
#include <vector>
// avoid conflict (msvc)
#ifdef ERROR
# undef ERROR
#endif
using namespace GpgME;
class GpgRevokeKeyEditInteractor::Private
{
enum {
START = EditInteractor::StartState,
COMMAND,
CONFIRM_REVOKING_ENTIRE_KEY,
REASON_CODE,
REASON_TEXT,
// all these free slots belong to REASON_TEXT, too; we increase state()
// by one for each line of text, so that action() is called
REASON_TEXT_DONE = REASON_TEXT + 1000,
CONFIRM_REASON,
QUIT,
CONFIRM_SAVE,
ERROR = EditInteractor::ErrorState
};
GpgRevokeKeyEditInteractor *const q = nullptr;
public:
Private(GpgRevokeKeyEditInteractor *q)
: q{q}
, reasonCode{"0"}
{
}
const char *action(Error &err) const;
unsigned int nextState(unsigned int statusCode, const char *args, Error &err);
std::string reasonCode;
std::vector<std::string> reasonLines;
int nextLine = -1;
};
const char *GpgRevokeKeyEditInteractor::Private::action(Error &err) const
{
switch (const auto state = q->state()) {
case COMMAND:
return "revkey";
case CONFIRM_REVOKING_ENTIRE_KEY:
return "Y";
case REASON_CODE:
return reasonCode.c_str();
case REASON_TEXT_DONE:
return "";
case CONFIRM_REASON:
return "Y";
case QUIT:
return "quit";
case CONFIRM_SAVE:
return "Y";
case START:
return nullptr;
default:
if (state >= REASON_TEXT && state < REASON_TEXT_DONE) {
return reasonLines[nextLine].c_str();
}
// fall through
case ERROR:
err = Error::fromCode(GPG_ERR_GENERAL);
return nullptr;
}
}
unsigned int GpgRevokeKeyEditInteractor::Private::nextState(unsigned int status, const char *args, Error &err)
{
using std::strcmp;
static const Error GENERAL_ERROR = Error::fromCode(GPG_ERR_GENERAL);
if (q->needsNoResponse(status)) {
return q->state();
}
switch (const auto state = q->state()) {
case START:
if (status == GPGME_STATUS_GET_LINE &&
strcmp(args, "keyedit.prompt") == 0) {
return COMMAND;
}
err = GENERAL_ERROR;
return ERROR;
case COMMAND:
if (status == GPGME_STATUS_GET_BOOL &&
strcmp(args, "keyedit.revoke.subkey.okay") == 0) {
return CONFIRM_REVOKING_ENTIRE_KEY;
}
err = GENERAL_ERROR;
return ERROR;
case CONFIRM_REVOKING_ENTIRE_KEY:
if (status == GPGME_STATUS_GET_LINE &&
strcmp(args, "ask_revocation_reason.code") == 0) {
return REASON_CODE;
}
err = GENERAL_ERROR;
return ERROR;
case REASON_CODE:
if (status == GPGME_STATUS_GET_LINE &&
strcmp(args, "ask_revocation_reason.text") == 0) {
nextLine++;
return nextLine < reasonLines.size() ? REASON_TEXT : REASON_TEXT_DONE;
}
err = GENERAL_ERROR;
return ERROR;
default:
if (state >= REASON_TEXT && state < REASON_TEXT_DONE) {
if (status == GPGME_STATUS_GET_LINE &&
strcmp(args, "ask_revocation_reason.text") == 0) {
nextLine++;
return nextLine < reasonLines.size() ? state + 1 : REASON_TEXT_DONE;
}
}
err = GENERAL_ERROR;
return ERROR;
case REASON_TEXT_DONE:
if (status == GPGME_STATUS_GET_BOOL &&
strcmp(args, "ask_revocation_reason.okay") == 0) {
return CONFIRM_REASON;
}
err = GENERAL_ERROR;
return ERROR;
case CONFIRM_REASON:
if (status == GPGME_STATUS_GET_LINE &&
strcmp(args, "keyedit.prompt") == 0) {
return QUIT;
}
err = GENERAL_ERROR;
return ERROR;
case QUIT:
if (status == GPGME_STATUS_GET_BOOL &&
strcmp(args, "keyedit.save.okay") == 0) {
return CONFIRM_SAVE;
}
err = GENERAL_ERROR;
return ERROR;
case ERROR:
if (status == GPGME_STATUS_GET_LINE &&
strcmp(args, "keyedit.prompt") == 0) {
return QUIT;
}
err = q->lastError();
return ERROR;
}
}
GpgRevokeKeyEditInteractor::GpgRevokeKeyEditInteractor()
: EditInteractor{}
, d{new Private{this}}
{
}
GpgRevokeKeyEditInteractor::~GpgRevokeKeyEditInteractor() = default;
void GpgRevokeKeyEditInteractor::setReason(RevocationReason reason, const std::vector<std::string> &description)
{
d->reasonCode = std::to_string(static_cast<int>(reason));
d->reasonLines = description;
}
const char *GpgRevokeKeyEditInteractor::action(Error &err) const
{
return d->action(err);
}
unsigned int GpgRevokeKeyEditInteractor::nextState(unsigned int status, const char *args, Error &err) const
{
return d->nextState(status, args, err);
}

View File

@ -0,0 +1,62 @@
/*
gpgrevokekeyeditinteractor.h - Edit Interactor to revoke own OpenPGP keys
Copyright (c) 2022 g10 Code GmbH
Software engineering by Ingo Klöcker <dev@ingo-kloecker.de>
This file is part of GPGME++.
GPGME++ is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
GPGME++ is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with GPGME++; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef __GPGMEPP_GPGREVOKEKEYEDITINTERACTOR_H__
#define __GPGMEPP_GPGREVOKEKEYEDITINTERACTOR_H__
#include "editinteractor.h"
#include "global.h"
#include <memory>
#include <vector>
namespace GpgME
{
/** Edit interactor to revoke the key a key edit operation is working on.
* Supports revocation of own keys only. */
class GPGMEPP_EXPORT GpgRevokeKeyEditInteractor : public EditInteractor
{
public:
GpgRevokeKeyEditInteractor();
~GpgRevokeKeyEditInteractor() override;
/** Sets the reason for the revocation. The reason defaults to \c Unspecified.
* \a description can be used for adding a comment for the revocation. The
* individual elements of \a description must be non-empty strings and they
* must not contain any endline characters.
*/
void setReason(RevocationReason reason, const std::vector<std::string> &description = {});
private:
const char *action(Error &err) const override;
unsigned int nextState(unsigned int statusCode, const char *args, Error &err) const override;
private:
class Private;
const std::unique_ptr<Private> d;
};
} // namespace GpgME
#endif // __GPGMEPP_GPGREVOKEKEYEDITINTERACTOR_H__