/** * 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 "GpgKeyOpera.h" #include #include #include #include #include #include #include #include "core/GpgConstants.h" #include "core/GpgGenKeyInfo.h" #include "GpgCommandExecutor.h" #include "GpgKeyGetter.h" GpgFrontend::GpgKeyOpera::GpgKeyOpera( int channel) : SingletonFunctionObject(channel) {} /** * 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; LOG(INFO) << "ctx version" << ctx_.GetInfo().GnupgVersion; 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)); }