summaryrefslogtreecommitdiffstats
path: root/src/tests/cli.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests/cli.cpp')
-rw-r--r--src/tests/cli.cpp857
1 files changed, 857 insertions, 0 deletions
diff --git a/src/tests/cli.cpp b/src/tests/cli.cpp
new file mode 100644
index 0000000..a96115c
--- /dev/null
+++ b/src/tests/cli.cpp
@@ -0,0 +1,857 @@
+/*
+ * Copyright (c) 2018-2019 [Ribose Inc](https://www.ribose.com).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "rnp_tests.h"
+#include "support.h"
+#include "time-utils.h"
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#else
+#ifndef WIFEXITED
+#define WIFEXITED(stat) (((*((int *) &(stat))) & 0xC0000000) == 0)
+#endif
+
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat) (*((int *) &(stat)))
+#endif
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#include "str-utils.h"
+#endif
+
+int rnp_main(int argc, char **argv);
+int rnpkeys_main(int argc, char **argv);
+
+static int
+call_rnp(const char *cmd, ...)
+{
+ int argc = 0;
+ int res;
+ char ** argv = (char **) calloc(32, sizeof(char *));
+ va_list args;
+
+ if (!argv) {
+ return -1;
+ }
+
+ va_start(args, cmd);
+ while (cmd) {
+ argv[argc++] = (char *) cmd;
+ cmd = va_arg(args, char *);
+ }
+ va_end(args);
+ /* reset state of getopt_long used in rnp */
+ optind = 1;
+
+ if (!strcmp(argv[0], "rnp")) {
+ res = rnp_main(argc, argv);
+ } else if (!strcmp(argv[0], "rnpkeys")) {
+ res = rnpkeys_main(argc, argv);
+ } else {
+ res = -1;
+ }
+ free(argv);
+
+ return res;
+}
+
+#define KEYS "data/keyrings"
+#define GENKEYS "data/keyrings_genkey_tmp"
+#define MKEYS "data/test_stream_key_merge/"
+#define FILES "data/test_cli"
+#define G10KEYS "data/test_stream_key_load/g10"
+
+TEST_F(rnp_tests, test_cli_rnp_keyfile)
+{
+ int ret;
+
+ /* sign with keyfile, using default key */
+ ret = call_rnp("rnp",
+ "--keyfile",
+ MKEYS "key-sec.asc",
+ "--password",
+ "password",
+ "-s",
+ FILES "/hello.txt",
+ NULL);
+ assert_int_equal(ret, 0);
+ assert_true(rnp_file_exists(FILES "/hello.txt.pgp"));
+ /* verify signed file */
+ ret =
+ call_rnp("rnp", "--keyfile", MKEYS "key-pub.asc", "-v", FILES "/hello.txt.pgp", NULL);
+ assert_int_equal(ret, 0);
+ assert_int_equal(rnp_unlink(FILES "/hello.txt.pgp"), 0);
+
+ /* sign with keyfile, using user id */
+ ret = call_rnp("rnp",
+ "-f",
+ MKEYS "key-sec.asc",
+ "-u",
+ "key-merge-uid-2",
+ "--password",
+ "password",
+ "--armor",
+ "-s",
+ FILES "/hello.txt",
+ NULL);
+ assert_int_equal(ret, 0);
+ assert_true(rnp_file_exists(FILES "/hello.txt.asc"));
+ /* verify signed file */
+ ret = call_rnp("rnp", "-f", MKEYS "key-pub.asc", "-v", FILES "/hello.txt.asc", NULL);
+ assert_int_equal(ret, 0);
+ /* verify with key without self-signature - should fail */
+ ret =
+ call_rnp("rnp", "-f", MKEYS "key-pub-just-key.pgp", "-v", FILES "/hello.txt.asc", NULL);
+ assert_int_not_equal(ret, 0);
+ assert_int_equal(rnp_unlink(FILES "/hello.txt.asc"), 0);
+
+ /* encrypt with keyfile, using default key */
+ ret = call_rnp("rnp", "--keyfile", MKEYS "key-pub.asc", "-e", FILES "/hello.txt", NULL);
+ assert_int_equal(ret, 0);
+ assert_true(rnp_file_exists(FILES "/hello.txt.pgp"));
+ /* decrypt it with raw seckey, without userids and sigs */
+ ret = call_rnp("rnp",
+ "--keyfile",
+ MKEYS "key-sec-no-uid-no-sigs.pgp",
+ "--password",
+ "password",
+ "-d",
+ FILES "/hello.txt.pgp",
+ "--output",
+ "-",
+ NULL);
+ assert_int_equal(ret, 0);
+ assert_int_equal(rnp_unlink(FILES "/hello.txt.pgp"), 0);
+
+ /* try to encrypt with keyfile, using the signing subkey */
+ ret = call_rnp("rnp",
+ "--keyfile",
+ MKEYS "key-pub.asc",
+ "-r",
+ "16CD16F267CCDD4F",
+ "--armor",
+ "-e",
+ FILES "/hello.txt",
+ NULL);
+ assert_int_not_equal(ret, 0);
+ assert_false(rnp_file_exists(FILES "/hello.txt.asc"));
+ /* now encrypt with keyfile, using the encrypting subkey */
+ ret = call_rnp("rnp",
+ "--keyfile",
+ MKEYS "key-pub.asc",
+ "-r",
+ "AF1114A47F5F5B28",
+ "--armor",
+ "-e",
+ FILES "/hello.txt",
+ NULL);
+ assert_int_equal(ret, 0);
+ assert_true(rnp_file_exists(FILES "/hello.txt.asc"));
+ /* fail to decrypt it with pubkey */
+ ret = call_rnp("rnp",
+ "--keyfile",
+ MKEYS "key-pub-subkey-1.pgp",
+ "--password",
+ "password",
+ "-d",
+ FILES "/hello.txt.asc",
+ "--output",
+ "-",
+ NULL);
+ assert_int_not_equal(ret, 0);
+ /* decrypt correctly with seckey + subkeys */
+ ret = call_rnp("rnp",
+ "--keyfile",
+ MKEYS "key-sec.pgp",
+ "--password",
+ "password",
+ "-d",
+ FILES "/hello.txt.asc",
+ "--output",
+ "-",
+ NULL);
+ assert_int_equal(ret, 0);
+ assert_int_equal(rnp_unlink(FILES "/hello.txt.asc"), 0);
+}
+
+static bool
+test_cli_g10_key_sign(const char *userid)
+{
+ /* create signature */
+ int ret = call_rnp("rnp",
+ "--homedir",
+ G10KEYS,
+ "--password",
+ "password",
+ "-u",
+ userid,
+ "-s",
+ FILES "/hello.txt",
+ NULL);
+ if (ret) {
+ rnp_unlink(FILES "/hello.txt.pgp");
+ return false;
+ }
+
+ /* verify back */
+ ret = call_rnp(
+ "rnp", "--homedir", G10KEYS, "-v", FILES "/hello.txt.pgp", "--output", "-", NULL);
+ rnp_unlink(FILES "/hello.txt.pgp");
+ return !ret;
+}
+
+static bool
+test_cli_g10_key_encrypt(const char *userid)
+{
+ /* encrypt */
+ int ret =
+ call_rnp("rnp", "--homedir", G10KEYS, "-r", userid, "-e", FILES "/hello.txt", NULL);
+ if (ret) {
+ rnp_unlink(FILES "/hello.txt.pgp");
+ return false;
+ }
+
+ /* decrypt it back */
+ ret = call_rnp("rnp",
+ "--homedir",
+ G10KEYS,
+ "--password",
+ "password",
+ "-d",
+ FILES "/hello.txt.pgp",
+ "--output",
+ "-",
+ NULL);
+ rnp_unlink(FILES "/hello.txt.pgp");
+ return !ret;
+}
+
+TEST_F(rnp_tests, test_cli_g10_operations)
+{
+ int ret;
+
+ /* sign with default g10 key */
+ ret = call_rnp(
+ "rnp", "--homedir", G10KEYS, "--password", "password", "-s", FILES "/hello.txt", NULL);
+ assert_int_equal(ret, 0);
+
+ /* verify back */
+ ret = call_rnp("rnp", "--homedir", G10KEYS, "-v", FILES "/hello.txt.pgp", NULL);
+ assert_int_equal(ret, 0);
+ assert_int_equal(rnp_unlink(FILES "/hello.txt.pgp"), 0);
+
+ /* encrypt with default g10 key */
+ ret = call_rnp("rnp", "--homedir", G10KEYS, "-e", FILES "/hello.txt", NULL);
+ assert_int_equal(ret, 0);
+
+ /* decrypt it back */
+ ret = call_rnp("rnp",
+ "--homedir",
+ G10KEYS,
+ "--password",
+ "password",
+ "-d",
+ FILES "/hello.txt.pgp",
+ "--output",
+ "-",
+ NULL);
+ assert_int_equal(ret, 0);
+ assert_int_equal(rnp_unlink(FILES "/hello.txt.pgp"), 0);
+
+ /* check dsa/eg key */
+ assert_true(test_cli_g10_key_sign("c8a10a7d78273e10")); // signing key
+ assert_true(test_cli_g10_key_encrypt("c8a10a7d78273e10")); // will find subkey
+ assert_false(test_cli_g10_key_sign("02a5715c3537717e")); // fail - encrypting subkey
+ assert_true(test_cli_g10_key_encrypt("02a5715c3537717e")); // success
+
+ /* check rsa/rsa key, key is SC while subkey is E. Must succeed till year 2024 */
+ assert_true(test_cli_g10_key_sign("2fb9179118898e8b"));
+ assert_true(test_cli_g10_key_encrypt("2fb9179118898e8b"));
+ assert_false(test_cli_g10_key_sign("6e2f73008f8b8d6e"));
+ assert_true(test_cli_g10_key_encrypt("6e2f73008f8b8d6e"));
+
+#ifdef CRYPTO_BACKEND_BOTAN
+ /* GnuPG extended key format requires AEAD support that is available for BOTAN backend
+ only https://github.com/rnpgp/rnp/issues/1642 (???)
+ */
+ /* check new rsa/rsa key, key is SC while subkey is E. */
+ assert_true(test_cli_g10_key_sign("bd860a52d1899c0f"));
+ assert_true(test_cli_g10_key_encrypt("bd860a52d1899c0f"));
+ assert_false(test_cli_g10_key_sign("8e08d46a37414996"));
+ assert_true(test_cli_g10_key_encrypt("8e08d46a37414996"));
+#endif
+
+ /* check ed25519 key */
+ assert_true(test_cli_g10_key_sign("cc786278981b0728"));
+ assert_false(test_cli_g10_key_encrypt("cc786278981b0728"));
+
+ /* check ed25519/x25519 key */
+ assert_true(test_cli_g10_key_sign("941822a0fc1b30a5"));
+ assert_true(test_cli_g10_key_encrypt("941822a0fc1b30a5"));
+ assert_false(test_cli_g10_key_sign("c711187e594376af"));
+ assert_true(test_cli_g10_key_encrypt("c711187e594376af"));
+
+ /* check p256 key */
+ assert_true(test_cli_g10_key_sign("23674f21b2441527"));
+ assert_true(test_cli_g10_key_encrypt("23674f21b2441527"));
+ assert_false(test_cli_g10_key_sign("37e285e9e9851491"));
+ assert_true(test_cli_g10_key_encrypt("37e285e9e9851491"));
+
+ /* check p384 key */
+ assert_true(test_cli_g10_key_sign("242a3aa5ea85f44a"));
+ assert_true(test_cli_g10_key_encrypt("242a3aa5ea85f44a"));
+ assert_false(test_cli_g10_key_sign("e210e3d554a4fad9"));
+ assert_true(test_cli_g10_key_encrypt("e210e3d554a4fad9"));
+
+ /* check p521 key */
+ assert_true(test_cli_g10_key_sign("2092ca8324263b6a"));
+ assert_true(test_cli_g10_key_encrypt("2092ca8324263b6a"));
+ assert_false(test_cli_g10_key_sign("9853df2f6d297442"));
+ assert_true(test_cli_g10_key_encrypt("9853df2f6d297442"));
+
+ /* check bp256 key */
+ assert_true(test_cli_g10_key_sign("d0c8a3daf9e0634a") == brainpool_enabled());
+ assert_true(test_cli_g10_key_encrypt("d0c8a3daf9e0634a") == brainpool_enabled());
+ assert_false(test_cli_g10_key_sign("2edabb94d3055f76"));
+ assert_true(test_cli_g10_key_encrypt("2edabb94d3055f76") == brainpool_enabled());
+
+ /* check bp384 key */
+ assert_true(test_cli_g10_key_sign("6cf2dce85599ada2") == brainpool_enabled());
+ assert_true(test_cli_g10_key_encrypt("6cf2dce85599ada2") == brainpool_enabled());
+ assert_false(test_cli_g10_key_sign("cff1bb6f16d28191"));
+ assert_true(test_cli_g10_key_encrypt("cff1bb6f16d28191") == brainpool_enabled());
+
+ /* check bp512 key */
+ assert_true(test_cli_g10_key_sign("aa5c58d14f7b8f48") == brainpool_enabled());
+ assert_true(test_cli_g10_key_encrypt("aa5c58d14f7b8f48") == brainpool_enabled());
+ assert_false(test_cli_g10_key_sign("20cdaa1482ba79ce"));
+ assert_true(test_cli_g10_key_encrypt("20cdaa1482ba79ce") == brainpool_enabled());
+
+ /* check secp256k1 key */
+ assert_true(test_cli_g10_key_sign("3ea5bb6f9692c1a0"));
+ assert_true(test_cli_g10_key_encrypt("3ea5bb6f9692c1a0"));
+ assert_false(test_cli_g10_key_sign("7635401f90d3e533"));
+ assert_true(test_cli_g10_key_encrypt("7635401f90d3e533"));
+}
+
+TEST_F(rnp_tests, test_cli_rnpkeys_unicode)
+{
+#ifdef _WIN32
+ std::string uid_acp = "\x80@a.com";
+ std::wstring uid2_wide =
+ L"\x03C9\x0410@b.com"; // some Greek and Cyrillic for CreateProcessW test
+ std::string homedir_s = std::string(m_dir) + "/unicode";
+ rnp_mkdir(homedir_s.c_str());
+ std::string path_s = rnp::path::append(original_dir(), "../rnpkeys/rnpkeys.exe");
+ std::string cmdline_s = path_s + " --numbits 2048 --homedir " + homedir_s +
+ " --password password --userid " + uid_acp + " --generate-key";
+ UINT acp = GetACP();
+ STARTUPINFOA si;
+ ZeroMemory(&si, sizeof si);
+ PROCESS_INFORMATION pi;
+ ZeroMemory(&pi, sizeof pi);
+ BOOL res = CreateProcessA(NULL, // (LPSTR) path_s.c_str(), // Module name
+ (LPSTR) cmdline_s.c_str(), // Command line
+ NULL, // Process handle not inheritable
+ NULL, // Thread handle not inheritable
+ FALSE, // Handle inheritance
+ 0, // Creation flags
+ NULL, // Use parent's environment block
+ NULL, // Use parent's starting directory
+ &si, // Pointer to STARTUPINFO structure
+ &pi); // Pointer to PROCESS_INFORMATION structure
+ assert_true(res);
+ assert_true(WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_OBJECT_0);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+
+ std::wstring homedir_ws = wstr_from_utf8(homedir_s);
+ std::wstring path_ws = wstr_from_utf8(path_s);
+ std::wstring cmdline_ws = path_ws + L" --numbits 2048 --homedir " + homedir_ws +
+ L" --password password --userid " + uid2_wide +
+ L" --generate-key";
+ STARTUPINFOW siw;
+ ZeroMemory(&siw, sizeof siw);
+ ZeroMemory(&pi, sizeof pi);
+ res = CreateProcessW(NULL,
+ (LPWSTR) cmdline_ws.c_str(), // Command line
+ NULL, // Process handle not inheritable
+ NULL, // Thread handle not inheritable
+ FALSE, // Handle inheritance
+ 0, // Creation flags
+ NULL, // Use parent's environment block
+ NULL, // Use parent's starting directory
+ &siw, // Pointer to STARTUPINFO structure
+ &pi); // Pointer to PROCESS_INFORMATION structure
+ assert_true(res);
+ assert_true(WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_OBJECT_0);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ // Load the keyring and check what was actually written
+ rnp_ffi_t ffi;
+ assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));
+ rnp_input_t input = NULL;
+ assert_rnp_success(rnp_input_from_path(&input, "unicode/pubring.gpg"));
+ assert_rnp_success(rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_PUBLIC_KEYS));
+ rnp_input_destroy(input);
+
+ // convert from ACP to wide char via Windows native mechanism
+ int convertResult = MultiByteToWideChar(acp, 0, uid_acp.c_str(), uid_acp.size(), NULL, 0);
+ assert_true(convertResult > 0);
+ std::wstring uid_wide;
+ uid_wide.resize(convertResult);
+ convertResult = MultiByteToWideChar(
+ acp, 0, uid_acp.c_str(), uid_acp.size(), &uid_wide[0], (int) uid_wide.size());
+ assert_true(convertResult > 0);
+
+ // we expect to find UID in UTF-8
+ std::string uid_utf8 = wstr_to_utf8(uid_wide);
+ rnp_key_handle_t key = NULL;
+ assert_rnp_success(rnp_locate_key(ffi, "userid", uid_utf8.c_str(), &key));
+ assert_non_null(key);
+
+ size_t uids = 0;
+ assert_rnp_success(rnp_key_get_uid_count(key, &uids));
+ assert_int_equal(uids, 1);
+
+ rnp_uid_handle_t uid = NULL;
+ assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid));
+ assert_non_null(uid);
+
+ size_t size = 0;
+ char * data = NULL;
+ assert_rnp_success(rnp_uid_get_data(uid, (void **) &data, &size));
+ std::string uid_read(data, data + size);
+ assert_int_equal(0, uid_read.compare(uid_utf8));
+ rnp_buffer_destroy(data);
+ rnp_uid_handle_destroy(uid);
+ rnp_key_handle_destroy(key);
+
+ uid_utf8 = wstr_to_utf8(uid2_wide);
+ key = NULL;
+ assert_rnp_success(rnp_locate_key(ffi, "userid", uid_utf8.c_str(), &key));
+ assert_non_null(key);
+
+ uids = 0;
+ assert_rnp_success(rnp_key_get_uid_count(key, &uids));
+ assert_int_equal(uids, 1);
+
+ uid = NULL;
+ assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid));
+ assert_non_null(uid);
+
+ size = 0;
+ data = NULL;
+ assert_rnp_success(rnp_uid_get_data(uid, (void **) &data, &size));
+ std::string uid2_read(data, data + size);
+ assert_int_equal(0, uid2_read.compare(uid_utf8));
+ rnp_buffer_destroy(data);
+ rnp_uid_handle_destroy(uid);
+ rnp_key_handle_destroy(key);
+ rnp_ffi_destroy(ffi);
+#endif
+}
+
+TEST_F(rnp_tests, test_cli_rnp)
+{
+ int ret;
+ assert_int_equal(0, call_rnp("rnp", "--version", NULL));
+
+ /* sign with default key */
+ ret = call_rnp("rnp",
+ "--homedir",
+ KEYS "/1",
+ "--password",
+ "password",
+ "--sign",
+ FILES "/hello.txt",
+ NULL);
+ assert_int_equal(ret, 0);
+
+ /* encrypt with default key */
+ ret = call_rnp(
+ "rnp", "--homedir", KEYS "/1", "--encrypt", FILES "/hello.txt", "--overwrite", NULL);
+ assert_int_equal(ret, 0);
+
+ /* sign and verify back with g10 key */
+ ret = call_rnp("rnp",
+ "--homedir",
+ KEYS "/3",
+ "-u",
+ "4BE147BB22DF1E60",
+ "--password",
+ "password",
+ "--sign",
+ FILES "/hello.txt",
+ "--overwrite",
+ NULL);
+ assert_int_equal(ret, 0);
+ ret = call_rnp("rnp", "--homedir", KEYS "/3", "--verify", FILES "/hello.txt.pgp", NULL);
+ assert_int_equal(ret, 0);
+
+ /* encrypt and decrypt back with g10 key */
+ ret = call_rnp("rnp",
+ "--homedir",
+ KEYS "/3",
+ "-r",
+ "4BE147BB22DF1E60",
+ "--encrypt",
+ FILES "/hello.txt",
+ "--overwrite",
+ NULL);
+ assert_int_equal(ret, 0);
+ ret = call_rnp("rnp",
+ "--homedir",
+ KEYS "/3",
+ "--password",
+ "password",
+ "--decrypt",
+ FILES "/hello.txt.pgp",
+ "--output",
+ "-",
+ NULL);
+ assert_int_equal(ret, 0);
+}
+
+TEST_F(rnp_tests, test_cli_examples)
+{
+ auto examples_path = rnp::path::append(original_dir(), "../examples");
+ /* key generation example */
+ auto example_path = rnp::path::append(examples_path, "generate");
+ assert_false(example_path.empty());
+ assert_int_equal(system(example_path.c_str()), 0);
+
+ /* encryption sample */
+ example_path = rnp::path::append(examples_path, "encrypt");
+ assert_false(example_path.empty());
+ assert_int_equal(system(example_path.c_str()), 0);
+
+ /* decryption sample */
+ example_path = rnp::path::append(examples_path, "decrypt");
+ assert_false(example_path.empty());
+ assert_int_equal(system(example_path.c_str()), 0);
+
+ /* signing sample */
+ example_path = rnp::path::append(examples_path, "sign");
+ assert_false(example_path.empty());
+ assert_int_equal(system(example_path.c_str()), 0);
+
+ /* verification sample */
+ example_path = rnp::path::append(examples_path, "verify");
+ assert_false(example_path.empty());
+ assert_int_equal(system(example_path.c_str()), 0);
+}
+
+TEST_F(rnp_tests, test_cli_rnpkeys)
+{
+ int ret;
+ assert_int_equal(0, call_rnp("rnpkeys", "--version", NULL));
+
+ /* test keys listing */
+ ret = call_rnp("rnpkeys", "--homedir", KEYS "/1", "--list-keys", NULL);
+ assert_int_equal(ret, 0);
+
+ ret = call_rnp("rnpkeys", "--homedir", KEYS "/1", "--list-keys", "--with-sigs", NULL);
+ assert_int_equal(ret, 0);
+
+ ret = call_rnp("rnpkeys", "--homedir", KEYS "/2", "--list-keys", NULL);
+ assert_int_equal(ret, 0);
+
+ ret = call_rnp("rnpkeys", "--homedir", KEYS "/2", "--list-keys", "--with-sigs", NULL);
+ assert_int_equal(ret, 0);
+
+ ret = call_rnp("rnpkeys", "--homedir", KEYS "/3", "--list-keys", NULL);
+ assert_int_equal(ret, 0);
+
+ ret = call_rnp("rnpkeys", "--homedir", KEYS "/3", "--list-keys", "--with-sigs", NULL);
+ assert_int_equal(ret, 0);
+
+ ret = call_rnp("rnpkeys", "--homedir", KEYS "/5", "--list-keys", NULL);
+ assert_int_equal(ret, 0);
+
+ ret = call_rnp("rnpkeys", "--homedir", KEYS "/5", "--list-keys", "--with-sigs", NULL);
+ assert_int_equal(ret, 0);
+
+ ret = call_rnp("rnpkeys", "--homedir", G10KEYS, "--list-keys", NULL);
+ assert_int_equal(ret, 0);
+
+ ret = call_rnp("rnpkeys", "--homedir", G10KEYS, "--list-keys", "--with-sigs", NULL);
+ assert_int_equal(ret, 0);
+
+ /* test single key listing command */
+ ret = call_rnp("rnpkeys", "--homedir", KEYS "/1", "--list-keys", "2fcadf05ffa501bb", NULL);
+ assert_int_equal(ret, 0);
+
+ ret = call_rnp("rnpkeys", "--homedir", KEYS "/1", "--list-keys", "00000000", NULL);
+ assert_int_not_equal(ret, 0);
+
+ ret = call_rnp("rnpkeys", "--homedir", KEYS "/1", "--list-keys", "zzzzzzzz", NULL);
+ assert_int_not_equal(ret, 0);
+}
+
+// check both primary key and subkey for the given userid
+static int
+key_expiration_check(rnp_key_store_t *keystore,
+ const char * userid,
+ uint32_t expectedExpiration)
+{
+ int res = -1; // not found
+ for (auto &key : keystore->keys) {
+ pgp_key_t *pk;
+ if (key.is_primary()) {
+ pk = &key;
+ } else {
+ if (!key.has_primary_fp()) {
+ return 0;
+ }
+ pk = rnp_key_store_get_key_by_fpr(keystore, key.primary_fp());
+ }
+ if (pk->uid_count() != 1) {
+ return 0;
+ }
+ auto uid = pk->get_uid(0).str;
+ if (uid != userid) {
+ continue;
+ }
+ auto expiration = key.expiration();
+ if (uid == "expiration_absolute@rnp" || uid == "expiration_beyond2038_absolute@rnp") {
+ auto diff = expectedExpiration < expiration ? expiration - expectedExpiration :
+ expectedExpiration - expiration;
+ // allow 10 minutes diff
+ if (diff < 600) {
+ res = 1;
+ } else {
+ return 0;
+ }
+ } else {
+ if (expectedExpiration == expiration) {
+ res = 1;
+ } else {
+ RNP_LOG(
+ "key_expiration_check error: userid=%s expectedExpiration=%u expiration=%u",
+ userid,
+ expectedExpiration,
+ expiration);
+ return 0;
+ }
+ }
+ }
+ return res;
+}
+
+static int
+key_generate(const char *homedir, const char *userid, const char *expiration)
+{
+ int ret = call_rnp("rnpkeys",
+ "--password",
+ "1234",
+ "--homedir",
+ homedir,
+ "--generate-key",
+ "--expiration",
+ expiration,
+ "--userid",
+ userid,
+ "--s2k-iterations",
+ "65536",
+ "--numbits",
+ "1024",
+ NULL);
+ return ret;
+}
+
+TEST_F(rnp_tests, test_cli_rnpkeys_genkey)
+{
+ assert_false(RNP_MKDIR(GENKEYS, S_IRWXU));
+ time_t basetime = time(NULL);
+ time_t rawtime = basetime + 604800;
+ time_t y2k38time = INT32_MAX;
+ uint32_t expected_diff_beyond2038_absolute;
+ if (rnp_y2k38_warning(y2k38time)) {
+ // we're on the system that doesn't support dates beyond y2k38
+ auto diff_to_y2k38 = y2k38time - basetime;
+ expected_diff_beyond2038_absolute = diff_to_y2k38;
+ } else {
+ struct tm tm2100;
+ rnp_localtime(time(NULL), tm2100);
+ tm2100.tm_hour = 0;
+ tm2100.tm_min = 0;
+ tm2100.tm_sec = 0;
+ tm2100.tm_mday = 1;
+ tm2100.tm_mon = 0;
+ tm2100.tm_year = 200;
+ /* line below is required to correctly handle DST changes */
+ tm2100.tm_isdst = -1;
+ expected_diff_beyond2038_absolute = mktime(&tm2100) - basetime;
+ }
+ struct tm timeinfo;
+ rnp_localtime(rawtime, timeinfo);
+ // clear hours, minutes and seconds
+ timeinfo.tm_hour = 0;
+ timeinfo.tm_min = 0;
+ timeinfo.tm_sec = 0;
+ rawtime = mktime(&timeinfo);
+ auto exp =
+ fmt("%d-%02d-%02d", timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday);
+
+ // these should fail and not go to the keystore
+ assert_int_not_equal(key_generate(GENKEYS, "expiration_negative@rnp", "-1"), 0);
+ assert_int_not_equal(key_generate(GENKEYS, "expiration_unrecognized_1@rnp", "1z"), 0);
+ assert_int_not_equal(key_generate(GENKEYS, "expiration_unrecognized_2@rnp", "now"), 0);
+ assert_int_not_equal(key_generate(GENKEYS, "expiration_unrecognized_3@rnp", "00000-01-01"),
+ 0);
+ assert_int_not_equal(
+ key_generate(GENKEYS, "expiration_integer_overflow@rnp", "1234567890123456789"), 0);
+ assert_int_not_equal(key_generate(GENKEYS, "expiration_32bit_overflow@rnp", "4294967296"),
+ 0);
+ assert_int_not_equal(key_generate(GENKEYS, "expiration_overflow_day@rnp", "2037-02-29"),
+ 0);
+ assert_int_not_equal(key_generate(GENKEYS, "expiration_overflow_month@rnp", "2037-13-01"),
+ 0);
+ if (!rnp_y2k38_warning(y2k38time)) {
+ assert_int_not_equal(
+ key_generate(GENKEYS, "expiration_overflow_year@rnp", "2337-01-01"), 0);
+ }
+ assert_int_not_equal(key_generate(GENKEYS, "expiration_underflow_day@rnp", "2037-02-00"),
+ 0);
+ assert_int_not_equal(key_generate(GENKEYS, "expiration_underflow_month@rnp", "2037-00-01"),
+ 0);
+ assert_int_not_equal(key_generate(GENKEYS, "expiration_underflow_year@rnp", "1800-01-01"),
+ 0);
+ assert_int_not_equal(key_generate(GENKEYS, "expiration_overflow@rnp", "200y"), 0);
+ assert_int_not_equal(key_generate(GENKEYS, "expiration_past@rnp", "2021-01-01"), 0);
+
+ // these should pass and go to the keystore -- 17 primary keys and 17 subkeys
+ assert_int_equal(key_generate(GENKEYS, "expiration_beyond2038_relative@rnp", "20y"), 0);
+ assert_int_equal(key_generate(GENKEYS, "expiration_beyond2038_absolute@rnp", "2100-01-01"),
+ 0);
+ assert_int_equal(key_generate(GENKEYS, "expiration_absolute@rnp", exp.c_str()), 0);
+ assert_int_equal(key_generate(GENKEYS, "expiration_max_32bit@rnp", "4294967295"), 0);
+ assert_int_equal(key_generate(GENKEYS, "expiration_max_32bit_h@rnp", "1193046h"), 0);
+ assert_int_equal(key_generate(GENKEYS, "expiration_1sec@rnp", "1"), 0);
+ assert_int_equal(key_generate(GENKEYS, "expiration_1hour@rnp", "1h"), 0);
+ assert_int_equal(key_generate(GENKEYS, "expiration_1day@rnp", "1d"), 0);
+ assert_int_equal(key_generate(GENKEYS, "expiration_1week@rnp", "1w"), 0);
+ assert_int_equal(key_generate(GENKEYS, "expiration_1month@rnp", "1m"), 0);
+ assert_int_equal(key_generate(GENKEYS, "expiration_1year@rnp", "1y"), 0);
+ assert_int_equal(key_generate(GENKEYS, "expiration_2sec@rnp", "2"), 0);
+ assert_int_equal(key_generate(GENKEYS, "expiration_2hours@rnp", "2h"), 0);
+ assert_int_equal(key_generate(GENKEYS, "expiration_2days@rnp", "2d"), 0);
+ assert_int_equal(key_generate(GENKEYS, "expiration_2weeks@rnp", "2w"), 0);
+ assert_int_equal(key_generate(GENKEYS, "expiration_2months@rnp", "2m"), 0);
+ assert_int_equal(key_generate(GENKEYS, "expiration_2years@rnp", "2y"), 0);
+
+ auto keystore = new rnp_key_store_t(PGP_KEY_STORE_GPG, "", global_ctx);
+ pgp_source_t src = {};
+ assert_rnp_success(init_file_src(&src, GENKEYS "/pubring.gpg"));
+ assert_true(rnp_key_store_load_from_src(keystore, &src, NULL));
+ assert_int_equal(rnp_key_store_get_key_count(keystore), 34);
+ src_close(&src);
+ assert_int_equal(key_expiration_check(keystore, "expiration_max_32bit@rnp", 4294967295),
+ 1);
+ assert_int_equal(key_expiration_check(keystore, "expiration_max_32bit_h@rnp", 4294965600),
+ 1);
+ assert_int_equal(key_expiration_check(keystore, "expiration_1sec@rnp", 1), 1);
+ assert_int_equal(key_expiration_check(keystore, "expiration_1hour@rnp", 3600), 1);
+ assert_int_equal(key_expiration_check(keystore, "expiration_1day@rnp", 86400), 1);
+ assert_int_equal(key_expiration_check(keystore, "expiration_1week@rnp", 604800), 1);
+ assert_int_equal(key_expiration_check(keystore, "expiration_1month@rnp", 2678400), 1);
+ assert_int_equal(key_expiration_check(keystore, "expiration_1year@rnp", 31536000), 1);
+ assert_int_equal(key_expiration_check(keystore, "expiration_2sec@rnp", 2), 1);
+ assert_int_equal(key_expiration_check(keystore, "expiration_2hours@rnp", 7200), 1);
+ assert_int_equal(key_expiration_check(keystore, "expiration_2days@rnp", 172800), 1);
+ assert_int_equal(key_expiration_check(keystore, "expiration_2weeks@rnp", 1209600), 1);
+ assert_int_equal(key_expiration_check(keystore, "expiration_2months@rnp", 5356800), 1);
+ assert_int_equal(key_expiration_check(keystore, "expiration_2years@rnp", 63072000), 1);
+ assert_int_equal(
+ key_expiration_check(keystore, "expiration_absolute@rnp", rawtime - basetime), 1);
+ assert_int_equal(key_expiration_check(keystore,
+ "expiration_beyond2038_absolute@rnp",
+ expected_diff_beyond2038_absolute),
+ 1);
+ assert_int_equal(
+ key_expiration_check(keystore, "expiration_beyond2038_relative@rnp", 630720000), 1);
+
+ delete keystore;
+ delete_recursively(GENKEYS);
+}
+
+TEST_F(rnp_tests, test_cli_dump)
+{
+ auto dump_path = rnp::path::append(original_dir(), "../examples/dump");
+ char cmd[512] = {0};
+ int chnum;
+ int status;
+ /* call dump's help */
+ chnum = snprintf(cmd, sizeof(cmd), "%s -h", dump_path.c_str());
+ assert_true(chnum < (int) sizeof(cmd));
+ status = system(cmd);
+ assert_true(WIFEXITED(status));
+ assert_int_equal(WEXITSTATUS(status), 1);
+ /* run dump on some data */
+ chnum = snprintf(cmd, sizeof(cmd), "%s \"%s\"", dump_path.c_str(), KEYS "/1/pubring.gpg");
+ assert_true(chnum < (int) sizeof(cmd));
+ status = system(cmd);
+ assert_true(WIFEXITED(status));
+ assert_int_equal(WEXITSTATUS(status), 0);
+ /* run dump on some data with json output */
+ chnum =
+ snprintf(cmd, sizeof(cmd), "%s -j \"%s\"", dump_path.c_str(), KEYS "/1/pubring.gpg");
+ assert_true(chnum < (int) sizeof(cmd));
+ status = system(cmd);
+ assert_true(WIFEXITED(status));
+ assert_int_equal(WEXITSTATUS(status), 0);
+ /* run dump on directory - must fail but not crash */
+ chnum = snprintf(cmd, sizeof(cmd), "%s \"%s\"", dump_path.c_str(), KEYS "/1/");
+ assert_true(chnum < (int) sizeof(cmd));
+ status = system(cmd);
+ assert_true(WIFEXITED(status));
+ assert_int_not_equal(WEXITSTATUS(status), 0);
+}
+
+TEST_F(rnp_tests, test_cli_logname)
+{
+ char * logname = getenv("LOGNAME");
+ char * user = getenv("USER");
+ std::string testname(user ? user : "user");
+ testname.append("-test-user");
+
+ setenv("LOGNAME", testname.c_str(), 1);
+ assert_string_equal(getenv_logname(), testname.c_str());
+ if (user) {
+ unsetenv("LOGNAME");
+ assert_string_equal(getenv_logname(), user);
+ }
+
+ if (logname) {
+ setenv("LOGNAME", logname, 1);
+ } else {
+ unsetenv("LOGNAME");
+ }
+}