From 8053187731ae8e3eb368d8360989cf5fd6eed9f7 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 05:32:49 +0200 Subject: Adding upstream version 0.17.0. Signed-off-by: Daniel Baumann --- src/tests/cli.cpp | 857 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 857 insertions(+) create mode 100644 src/tests/cli.cpp (limited to 'src/tests/cli.cpp') 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 +#else +#ifndef WIFEXITED +#define WIFEXITED(stat) (((*((int *) &(stat))) & 0xC0000000) == 0) +#endif + +#ifndef WEXITSTATUS +#define WEXITSTATUS(stat) (*((int *) &(stat))) +#endif +#endif + +#ifdef _WIN32 +#include +#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"); + } +} -- cgit v1.2.3