/**
* Copyright (C) 2021 Saturneric
*
* This file is part of GpgFrontend.
*
* GpgFrontend is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GpgFrontend 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GpgFrontend. If not, see .
*
* The initial version of the source code is inherited from
* the gpg4usb project, which is under GPL-3.0-or-later.
*
* All the source code of GpgFrontend was modified and released by
* Saturneric starting on May 12, 2021.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#include "core/function/GpgKeyOpera.h"
#include
#include
#include
#include
#include
#include
#include
#include "core/GpgConstants.h"
#include "core/GpgGenKeyInfo.h"
#include "core/function/GpgCommandExecutor.h"
#include "core/function/GpgKeyGetter.h"
/**
* Delete keys
* @param uidList key ids
*/
void GpgFrontend::GpgKeyOpera::DeleteKeys(
GpgFrontend::KeyIdArgsListPtr key_ids) {
GpgError err;
for (const auto& tmp : *key_ids) {
auto key = GpgKeyGetter::GetInstance().GetKey(tmp);
if (key.IsGood()) {
err = check_gpg_error(
gpgme_op_delete_ext(ctx_, gpgme_key_t(key),
GPGME_DELETE_ALLOW_SECRET | GPGME_DELETE_FORCE));
assert(gpg_err_code(err) == GPG_ERR_NO_ERROR);
} else {
LOG(WARNING) << "GpgKeyOpera DeleteKeys get key failed" << tmp;
}
}
}
/**
* Set the expire date and time of a key pair(actually the primary key) or
* subkey
* @param key target key pair
* @param subkey null if primary key
* @param expires date and time
* @return if successful
*/
GpgFrontend::GpgError GpgFrontend::GpgKeyOpera::SetExpire(
const GpgKey& key, const SubkeyId& subkey_fpr,
std::unique_ptr& expires) {
unsigned long expires_time = 0;
if (expires != nullptr) {
using namespace boost::posix_time;
using namespace std::chrono;
expires_time =
to_time_t(*expires) - system_clock::to_time_t(system_clock::now());
}
LOG(INFO) << key.GetId() << subkey_fpr << expires_time;
GpgError err;
if (key.GetFingerprint() == subkey_fpr || subkey_fpr.empty())
err = gpgme_op_setexpire(ctx_, gpgme_key_t(key), expires_time, nullptr, 0);
else
err = gpgme_op_setexpire(ctx_, gpgme_key_t(key), expires_time,
subkey_fpr.c_str(), 0);
return err;
}
/**
* Generate revoke cert of a key pair
* @param key target key pair
* @param outputFileName out file name(path)
* @return the process doing this job
*/
void GpgFrontend::GpgKeyOpera::GenerateRevokeCert(
const GpgKey& key, const std::string& output_file_name) {
auto args = std::vector{"--no-tty",
"--command-fd",
"0",
"--status-fd",
"1",
"-o",
output_file_name,
"--gen-revoke",
key.GetFingerprint()};
using boost::asio::async_write;
using boost::process::async_pipe;
#ifndef WINDOWS
GpgCommandExecutor::GetInstance().Execute(
args, [](async_pipe& in, async_pipe& out) -> void {
// boost::asio::streambuf buff;
// boost::asio::read_until(in, buff, '\n');
//
// std::istream is(&buff);
//
// while (!is.eof()) {
// std::string line;
// is >> line;
// LOG(INFO) << "line" << line;
// boost::algorithm::trim(line);
// if (line == std::string("[GNUPG:] GET_BOOL
// gen_revoke.okay")) {
//
// } else if (line ==
// std::string(
// "[GNUPG:] GET_LINE
// ask_revocation_reason.code")) {
//
// } else if (line ==
// std::string(
// "[GNUPG:] GET_LINE
// ask_revocation_reason.text")) {
//
// } else if (line ==
// std::string("[GNUPG:] GET_BOOL
// openfile.overwrite.okay")) {
//
// } else if (line ==
// std::string(
// "[GNUPG:] GET_BOOL
// ask_revocation_reason.okay")) {
//
// }
// }
});
#endif
}
/**
* Generate a new key pair
* @param params key generation args
* @return error information
*/
GpgFrontend::GpgError GpgFrontend::GpgKeyOpera::GenerateKey(
const std::unique_ptr& params, GpgGenKeyResult& result) {
auto userid_utf8 = params->GetUserid();
const char* userid = userid_utf8.c_str();
auto algo_utf8 = params->GetAlgo() + params->GetKeySizeStr();
LOG(INFO) << "params" << params->GetAlgo() << params->GetKeySizeStr();
const char* algo = algo_utf8.c_str();
unsigned long expires = 0;
{
using namespace boost::posix_time;
using namespace std::chrono;
expires = to_time_t(ptime(params->GetExpireTime())) -
system_clock::to_time_t(system_clock::now());
}
GpgError err;
if (ctx_.GetInfo().GnupgVersion >= "2.1.0") {
unsigned int flags = 0;
if (!params->IsSubKey()) flags |= GPGME_CREATE_CERT;
if (params->IsAllowEncryption()) flags |= GPGME_CREATE_ENCR;
if (params->IsAllowSigning()) flags |= GPGME_CREATE_SIGN;
if (params->IsAllowAuthentication()) flags |= GPGME_CREATE_AUTH;
if (params->IsNonExpired()) flags |= GPGME_CREATE_NOEXPIRE;
if (params->IsNoPassPhrase()) flags |= GPGME_CREATE_NOPASSWD;
LOG(INFO) << "args: " << userid << algo << expires << flags;
err = gpgme_op_createkey(ctx_, userid, algo, 0, expires, nullptr, flags);
} else {
std::stringstream ss;
auto param_format =
boost::format{
"\n"
"Key-Type: %1%\n"
"Key-Usage: sign\n"
"Key-Length: %2%\n"
"Name-Real: %3%\n"
"Name-Comment: %4%\n"
"Name-Email: %5%\n"} %
params->GetAlgo() % params->GetKeyLength() % params->GetName() %
params->GetComment() % params->GetEmail();
ss << param_format;
if (!params->IsNonExpired()) {
auto date = params->GetExpireTime().date();
ss << boost::format{"Expire-Date: %1%\n"} % to_iso_string(date);
} else
ss << boost::format{"Expire-Date: 0\n"};
if (!params->IsNoPassPhrase())
ss << boost::format{"Passphrase: %1%\n"} % params->GetPassPhrase();
ss << "";
DLOG(INFO) << "params" << std::endl << ss.str();
err = gpgme_op_genkey(ctx_, ss.str().c_str(), nullptr, nullptr);
}
if (check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR) {
auto temp_result = _new_result(gpgme_op_genkey_result(ctx_));
std::swap(temp_result, result);
}
return check_gpg_error(err);
}
/**
* Generate a new subkey of a certain key pair
* @param key target key pair
* @param params opera args
* @return error info
*/
GpgFrontend::GpgError GpgFrontend::GpgKeyOpera::GenerateSubkey(
const GpgKey& key, const std::unique_ptr& params) {
if (!params->IsSubKey()) return GPG_ERR_CANCELED;
auto algo_utf8 = (params->GetAlgo() + params->GetKeySizeStr());
const char* algo = algo_utf8.c_str();
unsigned long expires = 0;
{
using namespace boost::posix_time;
using namespace std::chrono;
expires = to_time_t(ptime(params->GetExpireTime())) -
system_clock::to_time_t(system_clock::now());
}
unsigned int flags = 0;
if (!params->IsSubKey()) flags |= GPGME_CREATE_CERT;
if (params->IsAllowEncryption()) flags |= GPGME_CREATE_ENCR;
if (params->IsAllowSigning()) flags |= GPGME_CREATE_SIGN;
if (params->IsAllowAuthentication()) flags |= GPGME_CREATE_AUTH;
if (params->IsNonExpired()) flags |= GPGME_CREATE_NOEXPIRE;
flags |= GPGME_CREATE_NOPASSWD;
LOG(INFO) << "GpgFrontend::GpgKeyOpera::GenerateSubkey Args: " << key.GetId()
<< algo << expires << flags;
auto err =
gpgme_op_createsubkey(ctx_, gpgme_key_t(key), algo, 0, expires, flags);
return check_gpg_error(err);
}
GpgFrontend::GpgError GpgFrontend::GpgKeyOpera::ModifyPassword(
const GpgFrontend::GpgKey& key) {
if (ctx_.GetInfo().GnupgVersion < "2.0.15") {
LOG(ERROR) << _("operator not support");
return GPG_ERR_NOT_SUPPORTED;
}
auto err = gpgme_op_passwd(ctx_, gpgme_key_t(key), 0);
return check_gpg_error(err);
}
GpgFrontend::GpgError GpgFrontend::GpgKeyOpera::ModifyTOFUPolicy(
const GpgFrontend::GpgKey& key, gpgme_tofu_policy_t tofu_policy) {
if (ctx_.GetInfo().GnupgVersion < "2.1.10") {
LOG(ERROR) << _("operator not support");
return GPG_ERR_NOT_SUPPORTED;
}
auto err = gpgme_op_tofu_policy(ctx_, gpgme_key_t(key), tofu_policy);
return check_gpg_error(err);
}
void GpgFrontend::GpgKeyOpera::DeleteKey(const GpgFrontend::KeyId& key_id) {
auto keys = std::make_unique();
keys->push_back(key_id);
DeleteKeys(std::move(keys));
}