diff options
Diffstat (limited to '')
-rw-r--r-- | src/gpg/GpgContext.cpp | 189 | ||||
-rw-r--r-- | src/gpg/GpgContext.h | 35 | ||||
-rw-r--r-- | src/gpg/GpgInfo.h | 4 | ||||
-rw-r--r-- | src/gpg/function/BasicOperator.cpp | 6 | ||||
-rw-r--r-- | src/gpg/function/GpgKeyOpera.cpp | 19 | ||||
-rw-r--r-- | test/GpgCoreTestKeygen.cpp | 76 | ||||
-rw-r--r-- | test/GpgFrontendTest.h | 23 |
7 files changed, 245 insertions, 107 deletions
diff --git a/src/gpg/GpgContext.cpp b/src/gpg/GpgContext.cpp index a39918e2..ff483637 100644 --- a/src/gpg/GpgContext.cpp +++ b/src/gpg/GpgContext.cpp @@ -43,7 +43,7 @@ namespace GpgFrontend { * Constructor * Set up gpgme-context, set paths to app-run path */ -GpgContext::GpgContext(const GpgContextInitArgs& args) { +GpgContext::GpgContext(const GpgContextInitArgs &args) : args_(args) { static bool _first = true; if (_first) { @@ -62,94 +62,153 @@ GpgContext::GpgContext(const GpgContextInitArgs& args) { _ctx_ref = CtxRefHandler(_p_ctx); if (args.gpg_alone) { - info.AppPath = args.gpg_path; + info_.AppPath = args.gpg_path; auto err = gpgme_ctx_set_engine_info(_ctx_ref.get(), GPGME_PROTOCOL_OpenPGP, - info.AppPath.c_str(), - info.DatabasePath.c_str()); + info_.AppPath.c_str(), + info_.DatabasePath.c_str()); assert(check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR); } auto engine_info = gpgme_ctx_get_engine_info(*this); - // Check ENV before running - bool check_pass = false, find_openpgp = false, find_gpgconf = false, - find_assuan = false, find_cms = false; + bool check_passed = false, find_openpgp = false, find_gpgconf = false, + find_cms = false; + while (engine_info != nullptr) { - if (engine_info->protocol == GPGME_PROTOCOL_GPGCONF && - strcmp(engine_info->version, "1.0.0") != 0) - find_gpgconf = true; - if (engine_info->protocol == GPGME_PROTOCOL_OpenPGP && - strcmp(engine_info->version, "1.0.0") != 0) { - find_openpgp = true; - info.AppPath = engine_info->file_name; - info.DatabasePath = "default", info.GnupgVersion = engine_info->version; - DLOG(INFO) << "OpenPGP" - << std::string(engine_info->file_name == nullptr - ? "null" - : engine_info->file_name) - << std::string(engine_info->home_dir == nullptr - ? "null" - : engine_info->home_dir); + if (!strcmp(engine_info->version, "1.0.0")) { + engine_info = engine_info->next; + continue; } - if (engine_info->protocol == GPGME_PROTOCOL_CMS && - strcmp(engine_info->version, "1.0.0") != 0) - find_cms = true; - if (engine_info->protocol == GPGME_PROTOCOL_ASSUAN) find_assuan = true; + DLOG(INFO) << gpgme_get_protocol_name(engine_info->protocol) + << std::string(engine_info->file_name == nullptr + ? "null" + : engine_info->file_name) + << std::string(engine_info->home_dir == nullptr + ? "null" + : engine_info->home_dir); + + switch (engine_info->protocol) { + case GPGME_PROTOCOL_OpenPGP: + find_openpgp = true; + info_.AppPath = engine_info->file_name; + info_.GnupgVersion = engine_info->version; + break; + case GPGME_PROTOCOL_CMS: + find_cms = true; + info_.CMSPath = engine_info->file_name; + break; + case GPGME_PROTOCOL_GPGCONF: + find_gpgconf = true; + info_.GpgConfPath = engine_info->file_name; + break; + case GPGME_PROTOCOL_ASSUAN: + break; + case GPGME_PROTOCOL_G13: + break; + case GPGME_PROTOCOL_UISERVER: + break; + case GPGME_PROTOCOL_SPAWN: + break; + case GPGME_PROTOCOL_DEFAULT: + break; + case GPGME_PROTOCOL_UNKNOWN: + break; + } engine_info = engine_info->next; } - if (find_gpgconf && find_openpgp && find_cms && find_assuan) - check_pass = true; + // conditional check + if ((info_.GnupgVersion >= "2.0.0" && find_gpgconf && find_openpgp && + find_cms) || + (info_.GnupgVersion > "1.0.0" && find_gpgconf)) + check_passed = true; - if (!check_pass) { + if (!check_passed) { this->good_ = false; + LOG(ERROR) << "Env check failed"; return; } else { - LOG(INFO) << "GnuPG version" << info.GnupgVersion; - - // Set Independent Database - if (args.independent_database) { - info.DatabasePath = args.db_path; - LOG(INFO) << "gpg custom database path" << info.DatabasePath; - auto err = gpgme_ctx_set_engine_info( - _ctx_ref.get(), GPGME_PROTOCOL_OpenPGP, info.AppPath.c_str(), - info.DatabasePath.c_str()); - assert(check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR); - } - - /** Setting the output type must be done at the beginning */ - /** think this means ascii-armor --> ? */ - gpgme_set_armor(*this, 1); - // Speed up loading process - gpgme_set_offline(*this, 1); - - if (info.GnupgVersion >= "2.0.0") { - LOG(INFO) << "Using GnuPG 2.x"; - check_gpg_error(gpgme_set_keylist_mode( - *this, GPGME_KEYLIST_MODE_LOCAL | GPGME_KEYLIST_MODE_WITH_SECRET | - GPGME_KEYLIST_MODE_SIGS | - GPGME_KEYLIST_MODE_SIG_NOTATIONS | - GPGME_KEYLIST_MODE_WITH_TOFU)); - } else { - LOG(INFO) << "Using GnuPG 1.x"; - check_gpg_error(gpgme_set_keylist_mode( - *this, GPGME_KEYLIST_MODE_LOCAL | GPGME_KEYLIST_MODE_SIGS | - GPGME_KEYLIST_MODE_SIG_NOTATIONS | - GPGME_KEYLIST_MODE_WITH_TOFU)); - } + DLOG(INFO) << "gnupg version" << info_.GnupgVersion; + init_ctx(); good_ = true; } } +void GpgContext::init_ctx() { + // Set Independent Database + if (info_.GnupgVersion <= "2.0.0" && args_.independent_database) { + info_.DatabasePath = args_.db_path; + DLOG(INFO) << "custom key db path" << info_.DatabasePath; + auto err = gpgme_ctx_set_engine_info(_ctx_ref.get(), GPGME_PROTOCOL_OpenPGP, + info_.AppPath.c_str(), + info_.DatabasePath.c_str()); + assert(check_gpg_error_2_err_code(err) == GPG_ERR_NO_ERROR); + } else { + info_.DatabasePath = "default"; + } + + /** Setting the output type must be done at the beginning */ + /** think this means ascii-armor --> ? */ + gpgme_set_armor(*this, 1); + // Speed up loading process + gpgme_set_offline(*this, 1); + + if (info_.GnupgVersion >= "2.0.0") { + check_gpg_error(gpgme_set_keylist_mode( + *this, GPGME_KEYLIST_MODE_LOCAL | GPGME_KEYLIST_MODE_WITH_SECRET | + GPGME_KEYLIST_MODE_SIGS | GPGME_KEYLIST_MODE_SIG_NOTATIONS | + GPGME_KEYLIST_MODE_WITH_TOFU)); + } else { + check_gpg_error(gpgme_set_keylist_mode( + *this, GPGME_KEYLIST_MODE_LOCAL | GPGME_KEYLIST_MODE_SIGS | + GPGME_KEYLIST_MODE_SIG_NOTATIONS | + GPGME_KEYLIST_MODE_WITH_TOFU)); + } + + // for unit test + if (args_.test_mode) { + LOG(INFO) << "test mode"; + if (info_.GnupgVersion >= "2.1.0") SetPassphraseCb(test_passphrase_cb); + gpgme_set_status_cb(*this, test_status_cb, nullptr); + } +} + bool GpgContext::good() const { return good_; } -void GpgContext::SetPassphraseCb(decltype(test_passphrase_cb) cb) const { - gpgme_set_passphrase_cb(*this, cb, nullptr); +void GpgContext::SetPassphraseCb(gpgme_passphrase_cb_t cb) const { + if (info_.GnupgVersion >= "2.1.0") { + if (gpgme_get_pinentry_mode(*this) != GPGME_PINENTRY_MODE_LOOPBACK) { + gpgme_set_pinentry_mode(*this, GPGME_PINENTRY_MODE_LOOPBACK); + } + gpgme_set_passphrase_cb(*this, cb, nullptr); + } else { + LOG(ERROR) << "Not supported for gnupg version" << info_.GnupgVersion; + } +} + +gpgme_error_t GpgContext::test_passphrase_cb(void *opaque, const char *uid_hint, + const char *passphrase_info, + int last_was_bad, int fd) { + size_t res; + std::string pass = "abcdefg\n"; + auto pass_len = pass.size(); + + size_t off = 0; + + do { + res = gpgme_io_write(fd, &pass[off], pass_len - off); + if (res > 0) off += res; + } while (res > 0 && off != pass_len); + + return off == pass_len ? 0 : gpgme_error_from_errno(errno); + return 0; } -std::string GpgContext::getGpgmeVersion() { - return {gpgme_check_version(nullptr)}; +gpgme_error_t GpgContext::test_status_cb(void *hook, const char *keyword, + const char *args) { + LOG(INFO) << "keyword" << keyword; + return 0; } } // namespace GpgFrontend
\ No newline at end of file diff --git a/src/gpg/GpgContext.h b/src/gpg/GpgContext.h index 887bb7a6..753e1090 100644 --- a/src/gpg/GpgContext.h +++ b/src/gpg/GpgContext.h @@ -33,10 +33,12 @@ namespace GpgFrontend { struct GpgContextInitArgs { + // make no sense for gpg2 bool independent_database = false; std::string db_path = {}; bool gpg_alone = false; std::string gpg_path = {}; + bool test_mode = false; GpgContextInitArgs() = default; }; @@ -55,14 +57,15 @@ class GpgContext : public SingletonFunctionObject<GpgContext> { [[nodiscard]] bool good() const; - [[nodiscard]] const GpgInfo& GetInfo() const { return info; } - - static std::string getGpgmeVersion(); + [[nodiscard]] const GpgInfo& GetInfo() const { return info_; } operator gpgme_ctx_t() const { return _ctx_ref.get(); } private: - GpgInfo info; + GpgInfo info_; + GpgContextInitArgs args_; + + void init_ctx(); struct _ctx_ref_deleter { void operator()(gpgme_ctx_t _ctx) { @@ -78,28 +81,12 @@ class GpgContext : public SingletonFunctionObject<GpgContext> { public: static gpgme_error_t test_passphrase_cb(void* opaque, const char* uid_hint, const char* passphrase_info, - int last_was_bad, int fd) { - LOG(INFO) << "test_passphrase_cb Called"; - size_t res; - std::string pass = "abcdefg\n"; - auto pass_len = pass.size(); - - size_t off = 0; - - (void)opaque; - (void)uid_hint; - (void)passphrase_info; - (void)last_was_bad; - - do { - res = gpgme_io_write(fd, &pass[off], pass_len - off); - if (res > 0) off += res; - } while (res > 0 && off != pass_len); + int last_was_bad, int fd); - return off == pass_len ? 0 : gpgme_error_from_errno(errno); - } + static gpgme_error_t test_status_cb(void* hook, const char* keyword, + const char* args); - void SetPassphraseCb(decltype(test_passphrase_cb) func) const; + void SetPassphraseCb(gpgme_passphrase_cb_t func) const; }; } // namespace GpgFrontend diff --git a/src/gpg/GpgInfo.h b/src/gpg/GpgInfo.h index 6ecb9b92..d651b2f6 100644 --- a/src/gpg/GpgInfo.h +++ b/src/gpg/GpgInfo.h @@ -40,6 +40,10 @@ class GpgInfo { std::string DatabasePath; std::string GnupgVersion; + + std::string GpgConfPath; + + std::string CMSPath; }; #endif // GPGFRONTEND_ZH_CN_TS_GPGINFO_H diff --git a/src/gpg/function/BasicOperator.cpp b/src/gpg/function/BasicOperator.cpp index 56b7ca54..e52c091c 100644 --- a/src/gpg/function/BasicOperator.cpp +++ b/src/gpg/function/BasicOperator.cpp @@ -187,9 +187,11 @@ gpgme_error_t GpgFrontend::BasicOperator::EncryptSign( void GpgFrontend::BasicOperator::SetSigners(KeyArgsList& keys) { gpgme_signers_clear(ctx); for (const GpgKey& key : keys) { + DLOG(INFO) << "key" << key.fpr(); if (key.CanSignActual()) { - auto gpgmeError = gpgme_signers_add(ctx, gpgme_key_t(key)); - check_gpg_error(gpgmeError); + DLOG(INFO) << "signer"; + auto error = gpgme_signers_add(ctx, gpgme_key_t(key)); + check_gpg_error(error); } } if (keys.size() != gpgme_signers_count(ctx)) diff --git a/src/gpg/function/GpgKeyOpera.cpp b/src/gpg/function/GpgKeyOpera.cpp index 4df06875..4cbc1d0a 100644 --- a/src/gpg/function/GpgKeyOpera.cpp +++ b/src/gpg/function/GpgKeyOpera.cpp @@ -47,11 +47,13 @@ void GpgFrontend::GpgKeyOpera::DeleteKeys( for (const auto& tmp : *key_ids) { auto key = GpgKeyGetter::GetInstance().GetKey(tmp); if (key.good()) { - LOG(INFO) << "GpgKeyOpera DeleteKeys Get Key Good"; - err = check_gpg_error(gpgme_op_delete(ctx, gpgme_key_t(key), 1)); + 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" << key.fpr(); + } else { + LOG(WARNING) << "GpgKeyOpera DeleteKeys get key failed" << tmp; + } } } @@ -152,8 +154,7 @@ GpgFrontend::GpgError GpgFrontend::GpgKeyOpera::GenerateKey( const char* userid = userid_utf8.c_str(); auto algo_utf8 = params->getAlgo() + params->getKeySizeStr(); - LOG(INFO) << "GpgFrontend::GpgKeyOpera::GenerateKey Params" - << params->getAlgo() << params->getKeySizeStr(); + LOG(INFO) << "params" << params->getAlgo() << params->getKeySizeStr(); const char* algo = algo_utf8.c_str(); unsigned long expires = 0; @@ -176,14 +177,12 @@ GpgFrontend::GpgError GpgFrontend::GpgKeyOpera::GenerateKey( if (params->isNonExpired()) flags |= GPGME_CREATE_NOEXPIRE; if (params->isNoPassPhrase()) flags |= GPGME_CREATE_NOPASSWD; - LOG(INFO) << "GpgFrontend::GpgKeyOpera::GenerateKey Args: " << userid - << algo << expires << flags; + 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{ "<GnupgKeyParms format=\"internal\">\n" @@ -203,6 +202,8 @@ GpgFrontend::GpgError GpgFrontend::GpgKeyOpera::GenerateKey( ss << "</GnupgKeyParms>"; + DLOG(INFO) << "params" << std::endl << ss.str(); + err = gpgme_op_genkey(ctx, ss.str().c_str(), nullptr, nullptr); } diff --git a/test/GpgCoreTestKeygen.cpp b/test/GpgCoreTestKeygen.cpp index 05d77af1..b703ee40 100644 --- a/test/GpgCoreTestKeygen.cpp +++ b/test/GpgCoreTestKeygen.cpp @@ -33,6 +33,7 @@ TEST_F(GpgCoreTest, GenerateKeyTest) { keygen_info->setName("foo"); keygen_info->setEmail("[email protected]"); keygen_info->setComment(""); + keygen_info->setKeySize(1024); keygen_info->setAlgo("rsa"); keygen_info->setNonExpired(true); keygen_info->setNonPassPhrase(true); @@ -50,3 +51,78 @@ TEST_F(GpgCoreTest, GenerateKeyTest) { ASSERT_TRUE(key.good()); key_opera.DeleteKey(fpr); } + +TEST_F(GpgCoreTest, GenerateKeyTest_1) { + auto& key_opera = GpgFrontend::GpgKeyOpera::GetInstance(default_channel); + auto keygen_info = std::make_unique<GpgFrontend::GenKeyInfo>(); + keygen_info->setName("foo"); + keygen_info->setEmail("[email protected]"); + keygen_info->setComment("hello gpgfrontend"); + keygen_info->setAlgo("rsa"); + keygen_info->setKeySize(4096); + keygen_info->setNonExpired(false); + keygen_info->setExpired(boost::posix_time::second_clock::local_time() + + boost::posix_time::hours(24)); + keygen_info->setNonPassPhrase(false); + + GpgFrontend::GpgGenKeyResult result = nullptr; + auto err = GpgFrontend::check_gpg_error_2_err_code( + key_opera.GenerateKey(keygen_info, result)); + ASSERT_EQ(err, GPG_ERR_NO_ERROR); + + auto fpr = result->fpr; + ASSERT_FALSE(fpr == nullptr); + + auto key = + GpgFrontend::GpgKeyGetter::GetInstance(default_channel).GetKey(fpr); + ASSERT_TRUE(key.good()); + key_opera.DeleteKey(fpr); +} + +TEST_F(GpgCoreTest, GenerateKeyTest_4) { + auto& key_opera = GpgFrontend::GpgKeyOpera::GetInstance(default_channel); + auto keygen_info = std::make_unique<GpgFrontend::GenKeyInfo>(); + keygen_info->setName("foo"); + keygen_info->setEmail("[email protected]"); + keygen_info->setComment(""); + keygen_info->setAlgo("dsa"); + keygen_info->setNonExpired(true); + keygen_info->setNonPassPhrase(false); + + GpgFrontend::GpgGenKeyResult result = nullptr; + auto err = GpgFrontend::check_gpg_error_2_err_code( + key_opera.GenerateKey(keygen_info, result)); + ASSERT_EQ(err, GPG_ERR_NO_ERROR); + + auto fpr = result->fpr; + ASSERT_FALSE(fpr == nullptr); + + auto key = + GpgFrontend::GpgKeyGetter::GetInstance(default_channel).GetKey(fpr); + ASSERT_TRUE(key.good()); + key_opera.DeleteKey(fpr); +} + +TEST_F(GpgCoreTest, GenerateKeyTest_5) { + auto& key_opera = GpgFrontend::GpgKeyOpera::GetInstance(default_channel); + auto keygen_info = std::make_unique<GpgFrontend::GenKeyInfo>(); + keygen_info->setName("foo"); + keygen_info->setEmail("[email protected]"); + keygen_info->setComment(""); + keygen_info->setAlgo("ed25519"); + keygen_info->setNonExpired(true); + keygen_info->setNonPassPhrase(false); + + GpgFrontend::GpgGenKeyResult result = nullptr; + auto err = GpgFrontend::check_gpg_error_2_err_code( + key_opera.GenerateKey(keygen_info, result)); + ASSERT_EQ(err, GPG_ERR_NO_ERROR); + + auto fpr = result->fpr; + ASSERT_FALSE(fpr == nullptr); + + auto key = + GpgFrontend::GpgKeyGetter::GetInstance(default_channel).GetKey(fpr); + ASSERT_TRUE(key.good()); + key_opera.DeleteKey(fpr); +} diff --git a/test/GpgFrontendTest.h b/test/GpgFrontendTest.h index 83778ee6..84476106 100644 --- a/test/GpgFrontendTest.h +++ b/test/GpgFrontendTest.h @@ -40,6 +40,7 @@ #include "gpg/GpgConstants.h" #include "gpg/function/GpgKeyImportExporter.h" +#include "gpg/function/GpgKeyOpera.h" class GpgCoreTest : public ::testing::Test { protected: @@ -70,6 +71,10 @@ class GpgCoreTest : public ::testing::Test { defaultConf.setToDefault(); el::Loggers::reconfigureLogger("default", defaultConf); + defaultConf.setGlobally(el::ConfigurationType::Format, + "%datetime %level %func %msg"); + el::Loggers::reconfigureLogger("default", defaultConf); + using namespace libconfig; Config cfg; ASSERT_NO_THROW(cfg.readFile(config_path.c_str())); @@ -90,12 +95,18 @@ class GpgCoreTest : public ::testing::Test { import_data_alone(); } - void TearDown() override {} + void TearDown() override { + auto key_ids = std::make_unique<GpgFrontend::KeyIdArgsList>(); + key_ids->push_back("81704859182661FB"); + key_ids->push_back("06F1C7E7240C94E8"); + key_ids->push_back("8465C55B25C9B7D1"); + key_ids->push_back("021D89771B680FFB"); + GpgFrontend::GpgKeyOpera::GetInstance(default_channel) + .DeleteKeys(std::move(key_ids)); + } private: void import_data() { - GpgFrontend::GpgContext::GetInstance(default_channel) - .SetPassphraseCb(GpgFrontend::GpgContext::test_passphrase_cb); for (const auto& secret_key : secret_keys_) { auto secret_key_copy = std::make_unique<GpgFrontend::ByteArray>(*secret_key); @@ -105,8 +116,6 @@ class GpgCoreTest : public ::testing::Test { } void import_data_alone() { - GpgFrontend::GpgContext::GetInstance(gpg_alone_channel) - .SetPassphraseCb(GpgFrontend::GpgContext::test_passphrase_cb); for (auto& secret_key : secret_keys_) { auto secret_key_copy = std::make_unique<GpgFrontend::ByteArray>(*secret_key); @@ -157,6 +166,7 @@ class GpgCoreTest : public ::testing::Test { args.independent_database = true; args.db_path = db_path.string(); args.gpg_path = gpg_path.string(); + args.test_mode = true; return std::make_unique<GpgFrontend::GpgContext>(args); }); } @@ -181,8 +191,7 @@ class GpgCoreTest : public ::testing::Test { GpgFrontend::GpgContext::CreateInstance( default_channel, [&]() -> std::unique_ptr<GpgFrontend::GpgContext> { GpgFrontend::GpgContextInitArgs args; - args.independent_database = true; - args.db_path = db_path.string(); + args.test_mode = true; return std::make_unique<GpgFrontend::GpgContext>(args); }); } |