diff options
Diffstat (limited to 'src/tests/ffi.cpp')
-rw-r--r-- | src/tests/ffi.cpp | 6025 |
1 files changed, 6025 insertions, 0 deletions
diff --git a/src/tests/ffi.cpp b/src/tests/ffi.cpp new file mode 100644 index 0000000..1e75871 --- /dev/null +++ b/src/tests/ffi.cpp @@ -0,0 +1,6025 @@ +/* + * Copyright (c) 2017-2020 [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 <fstream> +#include <vector> +#include <string> +#include <set> +#include <utility> + +#include <rnp/rnp.h> +#include "rnp_tests.h" +#include "support.h" +#include "librepgp/stream-common.h" +#include "librepgp/stream-packet.h" +#include "librepgp/stream-sig.h" +#include <json.h> +#include "file-utils.h" +#include "str-utils.h" +#include <librepgp/stream-ctx.h> +#include "pgp-key.h" +#include "ffi-priv-types.h" + +TEST_F(rnp_tests, test_ffi_homedir) +{ + rnp_ffi_t ffi = NULL; + char * pub_format = NULL; + char * pub_path = NULL; + char * sec_format = NULL; + char * sec_path = NULL; + + // get the default homedir (not a very thorough test) + { + assert_rnp_failure(rnp_get_default_homedir(NULL)); + char *homedir = NULL; + assert_rnp_success(rnp_get_default_homedir(&homedir)); + assert_non_null(homedir); + rnp_buffer_destroy(homedir); + } + + // check NULL params + assert_rnp_failure( + rnp_detect_homedir_info(NULL, &pub_format, &pub_path, &sec_format, &sec_path)); + assert_rnp_failure( + rnp_detect_homedir_info("data/keyrings/1", NULL, &pub_path, &sec_format, &sec_path)); + assert_rnp_failure( + rnp_detect_homedir_info("data/keyrings/1", &pub_format, NULL, &sec_format, &sec_path)); + assert_rnp_failure( + rnp_detect_homedir_info("data/keyrings/1", &pub_format, &pub_path, NULL, &sec_path)); + assert_rnp_failure( + rnp_detect_homedir_info("data/keyrings/1", &pub_format, &pub_path, &sec_format, NULL)); + // detect the formats+paths + assert_rnp_success(rnp_detect_homedir_info( + "data/keyrings/1", &pub_format, &pub_path, &sec_format, &sec_path)); + // check formats + assert_string_equal(pub_format, "GPG"); + assert_string_equal(sec_format, "GPG"); + // check paths + assert_string_equal(pub_path, "data/keyrings/1/pubring.gpg"); + assert_string_equal(sec_path, "data/keyrings/1/secring.gpg"); + rnp_buffer_destroy(pub_format); + rnp_buffer_destroy(pub_path); + rnp_buffer_destroy(sec_format); + rnp_buffer_destroy(sec_path); +// detect windows-style slashes +#ifdef _WIN32 + assert_rnp_success(rnp_detect_homedir_info( + "data\\keyrings\\1", &pub_format, &pub_path, &sec_format, &sec_path)); + // check formats + assert_string_equal(pub_format, "GPG"); + assert_string_equal(sec_format, "GPG"); + // check paths + assert_string_equal(pub_path, "data\\keyrings\\1\\pubring.gpg"); + assert_string_equal(sec_path, "data\\keyrings\\1\\secring.gpg"); + rnp_buffer_destroy(pub_format); + rnp_buffer_destroy(pub_path); + rnp_buffer_destroy(sec_format); + rnp_buffer_destroy(sec_path); +#endif + // detect with the trailing slash + assert_rnp_success(rnp_detect_homedir_info( + "data/keyrings/1/", &pub_format, &pub_path, &sec_format, &sec_path)); + // check formats + assert_string_equal(pub_format, "GPG"); + assert_string_equal(sec_format, "GPG"); + // check paths + assert_string_equal(pub_path, "data/keyrings/1/pubring.gpg"); + assert_string_equal(sec_path, "data/keyrings/1/secring.gpg"); + // setup FFI with wrong parameters + assert_rnp_failure(rnp_ffi_create(NULL, "GPG", "GPG")); + assert_rnp_failure(rnp_ffi_create(&ffi, "GPG", NULL)); + assert_rnp_failure(rnp_ffi_create(&ffi, NULL, "GPG")); + assert_rnp_failure(rnp_ffi_create(&ffi, "ZZG", "GPG")); + assert_rnp_failure(rnp_ffi_create(&ffi, "GPG", "ZZG")); + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, pub_format, sec_format)); + // load our keyrings + assert_true(load_keys_gpg(ffi, pub_path, sec_path)); + // free formats+paths + rnp_buffer_destroy(pub_format); + pub_format = NULL; + rnp_buffer_destroy(pub_path); + pub_path = NULL; + rnp_buffer_destroy(sec_format); + sec_format = NULL; + rnp_buffer_destroy(sec_path); + sec_path = NULL; + // check key counts + size_t count = 0; + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(7, count); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(7, count); + // cleanup + rnp_ffi_destroy(ffi); + ffi = NULL; + + // detect the formats+paths + assert_rnp_success(rnp_detect_homedir_info( + "data/keyrings/3", &pub_format, &pub_path, &sec_format, &sec_path)); + // check formats + assert_string_equal(pub_format, "KBX"); + assert_string_equal(sec_format, "G10"); + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, pub_format, sec_format)); + // load our keyrings + assert_true(load_keys_kbx_g10(ffi, pub_path, sec_path)); + // free formats+paths + rnp_buffer_destroy(pub_format); + pub_format = NULL; + rnp_buffer_destroy(pub_path); + pub_path = NULL; + rnp_buffer_destroy(sec_format); + sec_format = NULL; + rnp_buffer_destroy(sec_path); + sec_path = NULL; + // check key counts + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(2, count); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(2, count); + // check grip (1) + rnp_key_handle_t key = NULL; + assert_int_equal( + RNP_SUCCESS, + rnp_locate_key(ffi, "grip", "63E59092E4B1AE9F8E675B2F98AA2B8BD9F4EA59", &key)); + assert_non_null(key); + char *grip = NULL; + assert_rnp_success(rnp_key_get_grip(key, &grip)); + assert_non_null(grip); + assert_string_equal(grip, "63E59092E4B1AE9F8E675B2F98AA2B8BD9F4EA59"); + rnp_buffer_destroy(grip); + grip = NULL; + rnp_key_handle_destroy(key); + key = NULL; + // check grip (2) + assert_int_equal( + RNP_SUCCESS, + rnp_locate_key(ffi, "grip", "7EAB41A2F46257C36F2892696F5A2F0432499AD3", &key)); + assert_non_null(key); + grip = NULL; + assert_rnp_success(rnp_key_get_grip(key, &grip)); + assert_non_null(grip); + assert_string_equal(grip, "7EAB41A2F46257C36F2892696F5A2F0432499AD3"); + rnp_buffer_destroy(grip); + grip = NULL; + assert_rnp_success(rnp_key_handle_destroy(key)); + key = NULL; + // cleanup + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_detect_key_format) +{ + char *format = NULL; + + // Wrong parameters + auto data = file_to_vec("data/keyrings/1/pubring.gpg"); + assert_rnp_failure(rnp_detect_key_format(NULL, data.size(), &format)); + assert_rnp_failure(rnp_detect_key_format(data.data(), 0, &format)); + assert_rnp_failure(rnp_detect_key_format(data.data(), data.size(), NULL)); + free(format); + + // GPG + format = NULL; + data = file_to_vec("data/keyrings/1/pubring.gpg"); + assert_rnp_success(rnp_detect_key_format(data.data(), data.size(), &format)); + assert_string_equal(format, "GPG"); + free(format); + format = NULL; + + // GPG + data = file_to_vec("data/keyrings/1/secring.gpg"); + assert_rnp_success(rnp_detect_key_format(data.data(), data.size(), &format)); + assert_string_equal(format, "GPG"); + free(format); + format = NULL; + + // GPG (armored) + data = file_to_vec("data/keyrings/4/rsav3-p.asc"); + assert_rnp_success(rnp_detect_key_format(data.data(), data.size(), &format)); + assert_string_equal(format, "GPG"); + free(format); + format = NULL; + + // KBX + data = file_to_vec("data/keyrings/3/pubring.kbx"); + assert_rnp_success(rnp_detect_key_format(data.data(), data.size(), &format)); + assert_string_equal(format, "KBX"); + free(format); + format = NULL; + + // G10 + data = file_to_vec( + "data/keyrings/3/private-keys-v1.d/63E59092E4B1AE9F8E675B2F98AA2B8BD9F4EA59.key"); + assert_rnp_success(rnp_detect_key_format(data.data(), data.size(), &format)); + assert_string_equal(format, "G10"); + free(format); + format = NULL; + + // invalid + assert_rnp_success(rnp_detect_key_format((uint8_t *) "ABC", 3, &format)); + assert_null(format); +} + +TEST_F(rnp_tests, test_ffi_load_keys) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + size_t count; + + /* load public keys from pubring */ + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + // load pubring + assert_true(load_keys_gpg(ffi, "data/keyrings/1/pubring.gpg")); + // again + assert_true(load_keys_gpg(ffi, "data/keyrings/1/pubring.gpg")); + // check counts + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(7, count); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(0, count); + // cleanup + rnp_ffi_destroy(ffi); + ffi = NULL; + + /* load public keys from secring */ + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + // load + assert_true(load_keys_gpg(ffi, "data/keyrings/1/secring.gpg")); + // check counts + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(7, count); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(0, count); + // cleanup + rnp_ffi_destroy(ffi); + ffi = NULL; + + /* load secret keys from secring */ + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + // load secring + assert_true(load_keys_gpg(ffi, "", "data/keyrings/1/secring.gpg")); + // again + assert_true(load_keys_gpg(ffi, "", "data/keyrings/1/secring.gpg")); + // check counts + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(7, count); + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(0, count); + // cleanup + rnp_ffi_destroy(ffi); + ffi = NULL; + + /* load secret keys from pubring */ + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + // load pubring + assert_true(load_keys_gpg(ffi, "", "data/keyrings/1/pubring.gpg")); + // check counts + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(0, count); + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(0, count); + // cleanup + rnp_input_destroy(input); + input = NULL; + rnp_ffi_destroy(ffi); + ffi = NULL; + + /* concatenate the pubring and secrings into a single buffer */ + auto pub_buf = file_to_vec("data/keyrings/1/pubring.gpg"); + auto sec_buf = file_to_vec("data/keyrings/1/secring.gpg"); + auto buf = pub_buf; + buf.reserve(buf.size() + sec_buf.size()); + buf.insert(buf.end(), sec_buf.begin(), sec_buf.end()); + + /* load secret keys from pubring */ + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + // load + assert_rnp_success(rnp_input_from_memory(&input, buf.data(), buf.size(), true)); + assert_non_null(input); + // try wrong parameters + assert_rnp_failure(rnp_load_keys(NULL, "GPG", input, RNP_LOAD_SAVE_SECRET_KEYS)); + assert_rnp_failure(rnp_load_keys(ffi, NULL, input, RNP_LOAD_SAVE_SECRET_KEYS)); + assert_rnp_failure(rnp_load_keys(ffi, "GPG", NULL, RNP_LOAD_SAVE_SECRET_KEYS)); + assert_rnp_failure(rnp_load_keys(ffi, "WRONG", input, RNP_LOAD_SAVE_SECRET_KEYS)); + assert_rnp_failure(rnp_load_keys(ffi, "WRONG", input, 0)); + assert_rnp_success(rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_SECRET_KEYS)); + rnp_input_destroy(input); + input = NULL; + // again + assert_rnp_success(rnp_input_from_memory(&input, buf.data(), buf.size(), true)); + assert_non_null(input); + assert_rnp_success(rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_SECRET_KEYS)); + rnp_input_destroy(input); + input = NULL; + // check counts + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(7, count); + // cleanup + rnp_input_destroy(input); + input = NULL; + rnp_ffi_destroy(ffi); + ffi = NULL; +} + +TEST_F(rnp_tests, test_ffi_clear_keys) +{ + rnp_ffi_t ffi = NULL; + size_t pub_count; + size_t sec_count; + + // setup FFI + test_ffi_init(&ffi); + // check counts + assert_rnp_success(rnp_get_public_key_count(ffi, &pub_count)); + assert_int_equal(7, pub_count); + assert_rnp_success(rnp_get_secret_key_count(ffi, &sec_count)); + assert_int_equal(7, sec_count); + // clear public keys + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC)); + assert_rnp_success(rnp_get_public_key_count(ffi, &pub_count)); + assert_int_equal(pub_count, 0); + assert_rnp_success(rnp_get_secret_key_count(ffi, &sec_count)); + assert_int_equal(sec_count, 7); + // clear secret keys + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_SECRET)); + assert_rnp_success(rnp_get_public_key_count(ffi, &pub_count)); + assert_int_equal(pub_count, 0); + assert_rnp_success(rnp_get_secret_key_count(ffi, &sec_count)); + assert_int_equal(sec_count, 0); + // load public and clear secret keys + assert_true(load_keys_gpg(ffi, "data/keyrings/1/pubring.gpg")); + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_SECRET)); + assert_rnp_success(rnp_get_public_key_count(ffi, &pub_count)); + assert_int_equal(pub_count, 7); + assert_rnp_success(rnp_get_secret_key_count(ffi, &sec_count)); + assert_int_equal(sec_count, 0); + // load secret keys and clear all + assert_true(load_keys_gpg(ffi, "", "data/keyrings/1/secring.gpg")); + assert_rnp_success(rnp_get_public_key_count(ffi, &pub_count)); + assert_int_equal(7, pub_count); + assert_rnp_success(rnp_get_secret_key_count(ffi, &sec_count)); + assert_int_equal(7, sec_count); + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET)); + assert_rnp_success(rnp_get_public_key_count(ffi, &pub_count)); + assert_int_equal(pub_count, 0); + assert_rnp_success(rnp_get_secret_key_count(ffi, &sec_count)); + assert_int_equal(sec_count, 0); + // attempt to clear NULL ffi + assert_rnp_failure(rnp_unload_keys(NULL, RNP_KEY_UNLOAD_SECRET)); + // attempt to pass wrong flags + assert_rnp_failure(rnp_unload_keys(ffi, 255)); + // cleanup + rnp_ffi_destroy(ffi); + ffi = NULL; +} + +TEST_F(rnp_tests, test_ffi_save_keys) +{ + rnp_ffi_t ffi = NULL; + // setup FFI + test_ffi_init(&ffi); + char *temp_dir = make_temp_dir(); + // save pubring + auto pub_path = rnp::path::append(temp_dir, "pubring.gpg"); + assert_false(rnp::path::exists(pub_path)); + rnp_output_t output = NULL; + assert_rnp_success(rnp_output_to_path(&output, pub_path.c_str())); + assert_rnp_failure(rnp_save_keys(NULL, "GPG", output, RNP_LOAD_SAVE_PUBLIC_KEYS)); + assert_rnp_failure(rnp_save_keys(ffi, NULL, output, RNP_LOAD_SAVE_PUBLIC_KEYS)); + assert_rnp_failure(rnp_save_keys(ffi, "GPG", NULL, RNP_LOAD_SAVE_PUBLIC_KEYS)); + assert_rnp_failure(rnp_save_keys(ffi, "WRONG", output, RNP_LOAD_SAVE_PUBLIC_KEYS)); + assert_rnp_failure(rnp_save_keys(ffi, "GPG", output, 0)); + assert_rnp_success(rnp_save_keys(ffi, "GPG", output, RNP_LOAD_SAVE_PUBLIC_KEYS)); + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_true(rnp::path::exists(pub_path)); + // save secring + auto sec_path = rnp::path::append(temp_dir, "secring.gpg"); + assert_false(rnp::path::exists(sec_path)); + assert_rnp_success(rnp_output_to_path(&output, sec_path.c_str())); + assert_rnp_success(rnp_save_keys(ffi, "GPG", output, RNP_LOAD_SAVE_SECRET_KEYS)); + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_true(rnp::path::exists(sec_path)); + // save pubring && secring + auto both_path = rnp::path::append(temp_dir, "bothring.gpg"); + assert_false(rnp::path::exists(both_path)); + assert_rnp_success(rnp_output_to_path(&output, both_path.c_str())); + assert_int_equal( + RNP_SUCCESS, + rnp_save_keys( + ffi, "GPG", output, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS)); + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_true(rnp::path::exists(both_path)); + // cleanup + rnp_ffi_destroy(ffi); + ffi = NULL; + // start over (read from the saved locations) + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + // load pubring & secring + assert_true(load_keys_gpg(ffi, pub_path, sec_path)); + // check the counts + size_t count = 0; + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(7, count); + count = 0; + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(7, count); + // cleanup + rnp_ffi_destroy(ffi); + ffi = NULL; + // load both keyrings from the single file + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + // load pubring + rnp_input_t input = NULL; + assert_rnp_success(rnp_input_from_path(&input, both_path.c_str())); + assert_non_null(input); + assert_int_equal( + RNP_SUCCESS, + rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS)); + rnp_input_destroy(input); + input = NULL; + // check the counts. We should get both secret and public keys, since public keys are + // extracted from the secret ones. + count = 0; + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(7, count); + count = 0; + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(7, count); + // cleanup + rnp_ffi_destroy(ffi); + ffi = NULL; + + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "KBX", "G10")); + // load pubring & secring + assert_true(load_keys_kbx_g10( + ffi, "data/keyrings/3/pubring.kbx", "data/keyrings/3/private-keys-v1.d")); + // save pubring + pub_path = rnp::path::append(temp_dir, "pubring.kbx"); + assert_rnp_success(rnp_output_to_path(&output, pub_path.c_str())); + assert_rnp_success(rnp_save_keys(ffi, "KBX", output, RNP_LOAD_SAVE_PUBLIC_KEYS)); + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_true(rnp::path::exists(pub_path)); + // save secring to file - will fail for G10 + sec_path = rnp::path::append(temp_dir, "secring.file"); + assert_rnp_success(rnp_output_to_path(&output, sec_path.c_str())); + assert_rnp_failure(rnp_save_keys(ffi, "G10", output, RNP_LOAD_SAVE_SECRET_KEYS)); + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + // save secring + sec_path = rnp::path::append(temp_dir, "private-keys-v1.d"); + assert_false(rnp::path::exists(sec_path, true)); + assert_int_equal(0, RNP_MKDIR(sec_path.c_str(), S_IRWXU)); + assert_rnp_success(rnp_output_to_path(&output, sec_path.c_str())); + assert_rnp_success(rnp_save_keys(ffi, "G10", output, RNP_LOAD_SAVE_SECRET_KEYS)); + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_true(rnp::path::exists(sec_path, true)); + // cleanup + rnp_ffi_destroy(ffi); + ffi = NULL; + // start over (read from the saved locations) + assert_rnp_success(rnp_ffi_create(&ffi, "KBX", "G10")); + // load pubring & secring + assert_true(load_keys_kbx_g10(ffi, pub_path, sec_path)); + // check the counts + count = 0; + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(2, count); + count = 0; + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(2, count); + // cleanup + rnp_ffi_destroy(ffi); + + // final cleanup + clean_temp_dir(temp_dir); + free(temp_dir); +} + +TEST_F(rnp_tests, test_ffi_load_save_keys_to_utf8_path) +{ + const char kbx_pubring_utf8_filename[] = "pubring_\xC2\xA2.kbx"; + const char g10_secring_utf8_dirname[] = "private-keys-\xC2\xA2.d"; + const char utf8_filename[] = "bothring_\xC2\xA2.gpg"; + + // setup FFI + rnp_ffi_t ffi = NULL; + test_ffi_init(&ffi); + auto temp_dir = make_temp_dir(); + // save pubring && secring + auto both_path = rnp::path::append(temp_dir, utf8_filename); + assert_false(rnp::path::exists(both_path)); + rnp_output_t output = NULL; + assert_rnp_success(rnp_output_to_path(&output, both_path.c_str())); + assert_rnp_success(rnp_save_keys( + ffi, "GPG", output, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS)); + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_true(rnp::path::exists(both_path)); + // cleanup + rnp_ffi_destroy(ffi); + ffi = NULL; + // start over (read from the saved locations) + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + // load both keyrings from the single file + rnp_input_t input = NULL; + assert_rnp_success(rnp_input_from_path(&input, both_path.c_str())); + assert_non_null(input); + assert_rnp_success( + rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS)); + rnp_input_destroy(input); + input = NULL; + // check the counts. We should get both secret and public keys, since public keys are + // extracted from the secret ones. + size_t count = 0; + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(7, count); + count = 0; + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(7, count); + // cleanup + rnp_ffi_destroy(ffi); + ffi = NULL; + + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "KBX", "G10")); + // load pubring + assert_true(load_keys_kbx_g10( + ffi, "data/keyrings/3/pubring.kbx", "data/keyrings/3/private-keys-v1.d")); + // save pubring + auto pub_path = rnp::path::append(temp_dir, kbx_pubring_utf8_filename); + assert_rnp_success(rnp_output_to_path(&output, pub_path.c_str())); + assert_rnp_success(rnp_save_keys(ffi, "KBX", output, RNP_LOAD_SAVE_PUBLIC_KEYS)); + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_true(rnp::path::exists(pub_path)); + // save secring + auto sec_path = rnp::path::append(temp_dir, g10_secring_utf8_dirname); + assert_false(rnp::path::exists(sec_path, true)); + assert_int_equal(0, RNP_MKDIR(sec_path.c_str(), S_IRWXU)); + assert_rnp_success(rnp_output_to_path(&output, sec_path.c_str())); + assert_rnp_success(rnp_save_keys(ffi, "G10", output, RNP_LOAD_SAVE_SECRET_KEYS)); + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_true(rnp::path::exists(sec_path, true)); + // cleanup + rnp_ffi_destroy(ffi); + ffi = NULL; + // start over (read from the saved locations) + assert_rnp_success(rnp_ffi_create(&ffi, "KBX", "G10")); + // load pubring & secring + assert_true(load_keys_kbx_g10(ffi, pub_path, sec_path)); + // check the counts + count = 0; + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(2, count); + count = 0; + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(2, count); + // cleanup + rnp_ffi_destroy(ffi); + + // final cleanup + clean_temp_dir(temp_dir); + free(temp_dir); +} + +static size_t +get_longest_line_length(const std::string &str, const std::set<std::string> lines_to_skip) +{ + // eol could be \n or \r\n + size_t index = 0; + size_t max_len = 0; + for (;;) { + auto new_index = str.find('\n', index); + if (new_index == std::string::npos) { + break; + } + size_t line_length = new_index - index; + if (str[new_index - 1] == '\r') { + line_length--; + } + if (line_length > max_len && + lines_to_skip.find(str.substr(index, line_length)) == lines_to_skip.end()) { + max_len = line_length; + } + index = new_index + 1; + } + return max_len; +} + +TEST_F(rnp_tests, test_ffi_add_userid) +{ + rnp_ffi_t ffi = NULL; + char * results = NULL; + size_t count = 0; + rnp_uid_handle_t uid; + rnp_signature_handle_t sig; + char * hash_alg_name = NULL; + + const char *new_userid = "my new userid <user@example.com>"; + const char *default_hash_userid = "default hash <user@example.com"; + const char *ripemd_hash_userid = "ripemd160 <user@example.com"; + + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_ffi_set_key_provider(ffi, unused_getkeycb, NULL)); + + // load our JSON + auto json = file_to_str("data/test_ffi_json/generate-primary.json"); + + // generate the keys + assert_rnp_success(rnp_generate_key_json(ffi, json.c_str(), &results)); + assert_non_null(results); + rnp_buffer_destroy(results); + results = NULL; + + // check the key counts + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(1, count); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(1, count); + + rnp_key_handle_t key_handle = NULL; + assert_rnp_success(rnp_locate_key(ffi, "userid", "test0", &key_handle)); + assert_non_null(key_handle); + + assert_rnp_success(rnp_key_get_uid_count(key_handle, &count)); + assert_int_equal(1, count); + + // protect+lock the key + if (!sm2_enabled()) { + assert_rnp_failure(rnp_key_protect(key_handle, "pass", "SM4", "CFB", "SM3", 999999)); + assert_rnp_success( + rnp_key_protect(key_handle, "pass", "AES128", "CFB", "SHA256", 999999)); + } else { + assert_rnp_success(rnp_key_protect(key_handle, "pass", "SM4", "CFB", "SM3", 999999)); + } + assert_rnp_success(rnp_key_lock(key_handle)); + + // add with NULL parameters + assert_rnp_failure(rnp_key_add_uid(NULL, new_userid, NULL, 2147317200, 0x00, false)); + assert_rnp_failure(rnp_key_add_uid(key_handle, NULL, NULL, 2147317200, 0x00, false)); + + // add the userid (no pass provider, should fail) + assert_int_equal( + RNP_ERROR_BAD_PASSWORD, + rnp_key_add_uid(key_handle, new_userid, "SHA256", 2147317200, 0x00, false)); + + // actually add the userid + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "pass")); + // attempt to add empty uid + assert_rnp_failure(rnp_key_add_uid(key_handle, "", NULL, 2147317200, 0, false)); + // add with default hash algorithm + assert_rnp_success( + rnp_key_add_uid(key_handle, default_hash_userid, NULL, 2147317200, 0, false)); + // check whether key was locked back + bool locked = false; + assert_rnp_success(rnp_key_is_locked(key_handle, &locked)); + assert_true(locked); + // check if default hash was used + assert_rnp_success(rnp_key_get_uid_handle_at(key_handle, 1, &uid)); + assert_rnp_success(rnp_uid_get_signature_at(uid, 0, &sig)); + assert_rnp_success(rnp_signature_get_hash_alg(sig, &hash_alg_name)); + assert_int_equal(strcasecmp(hash_alg_name, DEFAULT_HASH_ALG), 0); + rnp_buffer_destroy(hash_alg_name); + hash_alg_name = NULL; + assert_rnp_success(rnp_signature_handle_destroy(sig)); + assert_rnp_success(rnp_uid_handle_destroy(uid)); + + assert_int_equal( + RNP_SUCCESS, rnp_key_add_uid(key_handle, new_userid, "SHA256", 2147317200, 0x00, false)); + + int uid_count_expected = 3; + int res = + rnp_key_add_uid(key_handle, ripemd_hash_userid, "RIPEMD160", 2147317200, 0, false); + if (ripemd160_enabled()) { + assert_rnp_success(res); + uid_count_expected++; + } else { + assert_rnp_failure(res); + } + + assert_rnp_success(rnp_key_get_uid_count(key_handle, &count)); + assert_int_equal(uid_count_expected, count); + + rnp_key_handle_t key_handle2 = NULL; + assert_rnp_success(rnp_locate_key(ffi, "userid", new_userid, &key_handle2)); + assert_non_null(key_handle2); + + rnp_key_handle_destroy(key_handle); + rnp_key_handle_destroy(key_handle2); + rnp_ffi_destroy(ffi); +} + +static bool +file_equals(const char *filename, const void *data, size_t len) +{ + pgp_source_t msrc = {}; + bool res = false; + + if (file_to_mem_src(&msrc, filename)) { + return false; + } + + res = (msrc.size == len) && !memcmp(mem_src_get_memory(&msrc), data, len); + src_close(&msrc); + return res; +} + +static void +test_ffi_init_sign_file_input(rnp_input_t *input, rnp_output_t *output) +{ + const char *plaintext = "this is some data that will be signed"; + + // write out some data + str_to_file("plaintext", plaintext); + // create input+output + assert_rnp_success(rnp_input_from_path(input, "plaintext")); + assert_non_null(*input); + assert_rnp_success(rnp_output_to_path(output, "signed")); + assert_non_null(*output); +} + +static void +test_ffi_init_sign_memory_input(rnp_input_t *input, rnp_output_t *output) +{ + const char *plaintext = "this is some data that will be signed"; + + assert_rnp_success( + rnp_input_from_memory(input, (uint8_t *) plaintext, strlen(plaintext), true)); + assert_non_null(*input); + if (output) { + assert_rnp_success(rnp_output_to_memory(output, 0)); + assert_non_null(*output); + } +} + +static void +test_ffi_init_verify_file_input(rnp_input_t *input, rnp_output_t *output) +{ + // create input+output + assert_rnp_success(rnp_input_from_path(input, "signed")); + assert_non_null(*input); + assert_rnp_success(rnp_output_to_path(output, "recovered")); + assert_non_null(*output); +} + +static void +test_ffi_init_verify_detached_file_input(rnp_input_t *input, rnp_input_t *signature) +{ + assert_rnp_success(rnp_input_from_path(input, "plaintext")); + assert_non_null(*input); + assert_rnp_success(rnp_input_from_path(signature, "signed")); + assert_non_null(*signature); +} + +static void +test_ffi_init_verify_memory_input(rnp_input_t * input, + rnp_output_t *output, + uint8_t * signed_buf, + size_t signed_len) +{ + // create input+output + assert_rnp_success(rnp_input_from_memory(input, signed_buf, signed_len, false)); + assert_non_null(*input); + assert_rnp_success(rnp_output_to_memory(output, 0)); + assert_non_null(*output); +} + +static void +test_ffi_setup_signatures(rnp_ffi_t *ffi, rnp_op_sign_t *op) +{ + rnp_key_handle_t key = NULL; + rnp_op_sign_signature_t sig = NULL; + // set signature times + const uint32_t issued = 1516211899; // Unix epoch, nowish + const uint32_t expires = 1000000000; // expires later + const uint32_t issued2 = 1516211900; // Unix epoch, nowish + const uint32_t expires2 = 2000000000; // expires later + + assert_rnp_success(rnp_op_sign_set_armor(*op, true)); + assert_rnp_success(rnp_op_sign_set_hash(*op, "SHA256")); + assert_rnp_success(rnp_op_sign_set_creation_time(*op, issued)); + assert_rnp_success(rnp_op_sign_set_expiration_time(*op, expires)); + + // set pass provider + assert_rnp_success( + rnp_ffi_set_pass_provider(*ffi, ffi_string_password_provider, (void *) "password")); + + // set first signature key + assert_rnp_success(rnp_locate_key(*ffi, "userid", "key0-uid2", &key)); + assert_rnp_success(rnp_op_sign_add_signature(*op, key, NULL)); + assert_rnp_success(rnp_key_handle_destroy(key)); + key = NULL; + // set second signature key + assert_rnp_success(rnp_locate_key(*ffi, "userid", "key0-uid1", &key)); + assert_rnp_success(rnp_op_sign_add_signature(*op, key, &sig)); + assert_rnp_success(rnp_op_sign_signature_set_creation_time(sig, issued2)); + assert_rnp_success(rnp_op_sign_signature_set_expiration_time(sig, expires2)); + assert_rnp_success(rnp_op_sign_signature_set_hash(sig, "SHA512")); + assert_rnp_success(rnp_key_handle_destroy(key)); +} + +static void +test_ffi_check_signatures(rnp_op_verify_t *verify) +{ + rnp_op_verify_signature_t sig; + size_t sig_count; + uint32_t sig_create; + uint32_t sig_expires; + char * hname = NULL; + const uint32_t issued = 1516211899; // Unix epoch, nowish + const uint32_t expires = 1000000000; // expires later + const uint32_t issued2 = 1516211900; // Unix epoch, nowish + const uint32_t expires2 = 2000000000; // expires later + + assert_rnp_success(rnp_op_verify_get_signature_count(*verify, &sig_count)); + assert_int_equal(sig_count, 2); + // first signature + assert_rnp_success(rnp_op_verify_get_signature_at(*verify, 0, &sig)); + assert_rnp_success(rnp_op_verify_signature_get_status(sig)); + assert_rnp_success(rnp_op_verify_signature_get_times(sig, &sig_create, &sig_expires)); + assert_int_equal(sig_create, issued); + assert_int_equal(sig_expires, expires); + assert_rnp_success(rnp_op_verify_signature_get_hash(sig, &hname)); + assert_string_equal(hname, "SHA256"); + rnp_buffer_destroy(hname); + // second signature + assert_rnp_success(rnp_op_verify_get_signature_at(*verify, 1, &sig)); + assert_rnp_success(rnp_op_verify_signature_get_status(sig)); + assert_rnp_success(rnp_op_verify_signature_get_times(sig, &sig_create, &sig_expires)); + assert_int_equal(sig_create, issued2); + assert_int_equal(sig_expires, expires2); + assert_rnp_success(rnp_op_verify_signature_get_hash(sig, &hname)); + assert_string_equal(hname, "SHA512"); + rnp_buffer_destroy(hname); +} + +static bool +test_ffi_check_recovered() +{ + pgp_source_t msrc1 = {}; + pgp_source_t msrc2 = {}; + bool res = false; + + if (file_to_mem_src(&msrc1, "recovered")) { + return false; + } + + if (file_to_mem_src(&msrc2, "plaintext")) { + goto finish; + } + + res = (msrc1.size == msrc2.size) && + !memcmp(mem_src_get_memory(&msrc1), mem_src_get_memory(&msrc2), msrc1.size); +finish: + src_close(&msrc1); + src_close(&msrc2); + return res; +} + +TEST_F(rnp_tests, test_ffi_signatures_memory) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_op_sign_t op = NULL; + rnp_op_verify_t verify; + uint8_t * signed_buf; + size_t signed_len; + uint8_t * verified_buf; + size_t verified_len; + + // init ffi + test_ffi_init(&ffi); + // init input + test_ffi_init_sign_memory_input(&input, &output); + // create signature operation + assert_rnp_success(rnp_op_sign_create(&op, ffi, input, output)); + // setup signature(s) + test_ffi_setup_signatures(&ffi, &op); + // execute the operation + assert_rnp_success(rnp_op_sign_execute(op)); + // make sure the output file was created + assert_rnp_failure(rnp_output_memory_get_buf(NULL, &signed_buf, &signed_len, true)); + assert_rnp_failure(rnp_output_memory_get_buf(output, NULL, &signed_len, true)); + assert_rnp_failure(rnp_output_memory_get_buf(output, &signed_buf, NULL, true)); + assert_rnp_success(rnp_output_memory_get_buf(output, &signed_buf, &signed_len, true)); + assert_non_null(signed_buf); + assert_true(signed_len > 0); + + // cleanup + assert_rnp_success(rnp_input_destroy(input)); + input = NULL; + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_rnp_success(rnp_op_sign_destroy(op)); + op = NULL; + + /* now verify */ + // make sure it is correctly armored + assert_int_equal(memcmp(signed_buf, "-----BEGIN PGP MESSAGE-----", 27), 0); + // create input and output + test_ffi_init_verify_memory_input(&input, &output, signed_buf, signed_len); + // call verify + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + // check signatures + test_ffi_check_signatures(&verify); + // get output + assert_rnp_success(rnp_output_memory_get_buf(output, &verified_buf, &verified_len, true)); + assert_non_null(verified_buf); + assert_true(verified_len > 0); + // cleanup + assert_rnp_success(rnp_op_verify_destroy(verify)); + assert_rnp_success(rnp_input_destroy(input)); + input = NULL; + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_rnp_success(rnp_ffi_destroy(ffi)); + rnp_buffer_destroy(signed_buf); + rnp_buffer_destroy(verified_buf); +} + +TEST_F(rnp_tests, test_ffi_signatures) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_op_sign_t op = NULL; + rnp_op_verify_t verify; + + // init ffi + test_ffi_init(&ffi); + // init file input + test_ffi_init_sign_file_input(&input, &output); + // create signature operation + assert_rnp_success(rnp_op_sign_create(&op, ffi, input, output)); + // setup signature(s) + test_ffi_setup_signatures(&ffi, &op); + // execute the operation + assert_rnp_success(rnp_op_sign_execute(op)); + // make sure the output file was created + assert_true(rnp_file_exists("signed")); + + // cleanup + assert_rnp_success(rnp_input_destroy(input)); + input = NULL; + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_rnp_success(rnp_op_sign_destroy(op)); + op = NULL; + + /* now verify */ + + // create input and output + test_ffi_init_verify_file_input(&input, &output); + // call verify + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + // check signatures + test_ffi_check_signatures(&verify); + // cleanup + assert_rnp_success(rnp_op_verify_destroy(verify)); + assert_rnp_success(rnp_input_destroy(input)); + input = NULL; + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_rnp_success(rnp_ffi_destroy(ffi)); + // check output + assert_true(test_ffi_check_recovered()); +} + +TEST_F(rnp_tests, test_ffi_signatures_detached_memory) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_input_t signature = NULL; + rnp_output_t output = NULL; + rnp_op_sign_t op = NULL; + rnp_op_verify_t verify; + uint8_t * signed_buf; + size_t signed_len; + + // init ffi + test_ffi_init(&ffi); + // init input + test_ffi_init_sign_memory_input(&input, &output); + // create signature operation + assert_rnp_success(rnp_op_sign_detached_create(&op, ffi, input, output)); + // setup signature(s) + test_ffi_setup_signatures(&ffi, &op); + // execute the operation + assert_rnp_success(rnp_op_sign_execute(op)); + assert_rnp_success(rnp_output_memory_get_buf(output, &signed_buf, &signed_len, true)); + assert_non_null(signed_buf); + assert_true(signed_len > 0); + + // cleanup + assert_rnp_success(rnp_input_destroy(input)); + input = NULL; + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_rnp_success(rnp_op_sign_destroy(op)); + op = NULL; + + /* now verify */ + // make sure it is correctly armored + assert_int_equal(memcmp(signed_buf, "-----BEGIN PGP SIGNATURE-----", 29), 0); + // create input and output + test_ffi_init_sign_memory_input(&input, NULL); + assert_rnp_success(rnp_input_from_memory(&signature, signed_buf, signed_len, true)); + assert_non_null(signature); + // call verify + assert_rnp_success(rnp_op_verify_detached_create(&verify, ffi, input, signature)); + assert_rnp_success(rnp_op_verify_execute(verify)); + // check signatures + test_ffi_check_signatures(&verify); + // cleanup + rnp_buffer_destroy(signed_buf); + assert_rnp_success(rnp_op_verify_destroy(verify)); + assert_rnp_success(rnp_input_destroy(input)); + input = NULL; + assert_rnp_success(rnp_input_destroy(signature)); + signature = NULL; + assert_rnp_success(rnp_ffi_destroy(ffi)); +} + +TEST_F(rnp_tests, test_ffi_signatures_detached) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_input_t signature = NULL; + rnp_output_t output = NULL; + rnp_op_sign_t op = NULL; + rnp_op_verify_t verify; + + // init ffi + test_ffi_init(&ffi); + // init file input + test_ffi_init_sign_file_input(&input, &output); + // create signature operation + assert_rnp_success(rnp_op_sign_detached_create(&op, ffi, input, output)); + // setup signature(s) + test_ffi_setup_signatures(&ffi, &op); + // execute the operation + assert_rnp_success(rnp_op_sign_execute(op)); + // make sure the output file was created + assert_true(rnp_file_exists("signed")); + + // cleanup + assert_rnp_success(rnp_input_destroy(input)); + input = NULL; + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_rnp_success(rnp_op_sign_destroy(op)); + op = NULL; + + /* now verify */ + + // create input and output + test_ffi_init_verify_detached_file_input(&input, &signature); + // call verify + assert_rnp_success(rnp_op_verify_detached_create(&verify, ffi, input, signature)); + assert_rnp_success(rnp_op_verify_execute(verify)); + // check signatures + test_ffi_check_signatures(&verify); + // cleanup + assert_rnp_success(rnp_op_verify_destroy(verify)); + assert_rnp_success(rnp_input_destroy(input)); + input = NULL; + assert_rnp_success(rnp_input_destroy(signature)); + signature = NULL; + assert_rnp_success(rnp_ffi_destroy(ffi)); +} + +TEST_F(rnp_tests, test_ffi_signatures_dump) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_input_t signature = NULL; + rnp_op_verify_t verify; + + /* init ffi and inputs */ + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + load_keys_gpg(ffi, "data/test_stream_signatures/pub.asc"); + assert_rnp_success(rnp_input_from_path(&input, "data/test_stream_signatures/source.txt")); + assert_rnp_success( + rnp_input_from_path(&signature, "data/test_stream_signatures/source.txt.sig")); + /* call verify detached to obtain signatures */ + assert_rnp_success(rnp_op_verify_detached_create(&verify, ffi, input, signature)); + assert_rnp_success(rnp_op_verify_execute(verify)); + /* get signature and check it */ + rnp_op_verify_signature_t sig; + size_t sig_count; + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sig_count)); + assert_int_equal(sig_count, 1); + /* get signature handle */ + assert_rnp_success(rnp_op_verify_get_signature_at(verify, 0, &sig)); + assert_rnp_success(rnp_op_verify_signature_get_status(sig)); + rnp_signature_handle_t sighandle = NULL; + assert_rnp_success(rnp_op_verify_signature_get_handle(sig, &sighandle)); + assert_non_null(sighandle); + /* check signature type */ + char *sigtype = NULL; + assert_rnp_success(rnp_signature_get_type(sighandle, &sigtype)); + assert_string_equal(sigtype, "binary"); + rnp_buffer_destroy(sigtype); + /* attempt to validate it via wrong function */ + assert_int_equal(rnp_signature_is_valid(sighandle, 0), RNP_ERROR_BAD_PARAMETERS); + /* cleanup, making sure that sighandle doesn't depend on verify */ + assert_rnp_success(rnp_op_verify_destroy(verify)); + assert_rnp_success(rnp_input_destroy(input)); + assert_rnp_success(rnp_input_destroy(signature)); + /* check whether getters work on sighandle: algorithm */ + char *alg = NULL; + assert_rnp_success(rnp_signature_get_alg(sighandle, &alg)); + assert_non_null(alg); + assert_string_equal(alg, "RSA"); + rnp_buffer_destroy(alg); + /* keyid */ + char *keyid = NULL; + assert_rnp_success(rnp_signature_get_keyid(sighandle, &keyid)); + assert_non_null(keyid); + assert_string_equal(keyid, "5873BD738E575398"); + rnp_buffer_destroy(keyid); + /* creation time */ + uint32_t create = 0; + assert_rnp_success(rnp_signature_get_creation(sighandle, &create)); + assert_int_equal(create, 1522241943); + /* hash algorithm */ + assert_rnp_success(rnp_signature_get_hash_alg(sighandle, &alg)); + assert_non_null(alg); + assert_string_equal(alg, "SHA256"); + rnp_buffer_destroy(alg); + /* now dump signature packet to json */ + char *json = NULL; + assert_rnp_success(rnp_signature_packet_to_json(sighandle, 0, &json)); + json_object *jso = json_tokener_parse(json); + rnp_buffer_destroy(json); + assert_non_null(jso); + assert_true(json_object_is_type(jso, json_type_array)); + assert_int_equal(json_object_array_length(jso), 1); + /* check the signature packet dump */ + json_object *pkt = json_object_array_get_idx(jso, 0); + /* check helper functions */ + assert_false(check_json_field_int(pkt, "unknown", 4)); + assert_false(check_json_field_int(pkt, "version", 5)); + assert_true(check_json_field_int(pkt, "version", 4)); + assert_true(check_json_field_int(pkt, "type", 0)); + assert_true(check_json_field_str(pkt, "type.str", "Signature of a binary document")); + assert_true(check_json_field_int(pkt, "algorithm", 1)); + assert_true(check_json_field_str(pkt, "algorithm.str", "RSA (Encrypt or Sign)")); + assert_true(check_json_field_int(pkt, "hash algorithm", 8)); + assert_true(check_json_field_str(pkt, "hash algorithm.str", "SHA256")); + assert_true(check_json_field_str(pkt, "lbits", "816e")); + json_object *subpkts = NULL; + assert_true(json_object_object_get_ex(pkt, "subpackets", &subpkts)); + assert_non_null(subpkts); + assert_true(json_object_is_type(subpkts, json_type_array)); + assert_int_equal(json_object_array_length(subpkts), 3); + /* subpacket 0 */ + json_object *subpkt = json_object_array_get_idx(subpkts, 0); + assert_true(check_json_field_int(subpkt, "type", 33)); + assert_true(check_json_field_str(subpkt, "type.str", "issuer fingerprint")); + assert_true(check_json_field_int(subpkt, "length", 21)); + assert_true(check_json_field_bool(subpkt, "hashed", true)); + assert_true(check_json_field_bool(subpkt, "critical", false)); + assert_true( + check_json_field_str(subpkt, "fingerprint", "7a60e671179f9b920f6478a25873bd738e575398")); + /* subpacket 1 */ + subpkt = json_object_array_get_idx(subpkts, 1); + assert_true(check_json_field_int(subpkt, "type", 2)); + assert_true(check_json_field_str(subpkt, "type.str", "signature creation time")); + assert_true(check_json_field_int(subpkt, "length", 4)); + assert_true(check_json_field_bool(subpkt, "hashed", true)); + assert_true(check_json_field_bool(subpkt, "critical", false)); + assert_true(check_json_field_int(subpkt, "creation time", 1522241943)); + /* subpacket 2 */ + subpkt = json_object_array_get_idx(subpkts, 2); + assert_true(check_json_field_int(subpkt, "type", 16)); + assert_true(check_json_field_str(subpkt, "type.str", "issuer key ID")); + assert_true(check_json_field_int(subpkt, "length", 8)); + assert_true(check_json_field_bool(subpkt, "hashed", false)); + assert_true(check_json_field_bool(subpkt, "critical", false)); + assert_true(check_json_field_str(subpkt, "issuer keyid", "5873bd738e575398")); + json_object_put(jso); + rnp_signature_handle_destroy(sighandle); + /* check text-mode detached signature */ + assert_rnp_success(rnp_input_from_path(&input, "data/test_stream_signatures/source.txt")); + assert_rnp_success( + rnp_input_from_path(&signature, "data/test_stream_signatures/source.txt.text.sig")); + /* call verify detached to obtain signatures */ + assert_rnp_success(rnp_op_verify_detached_create(&verify, ffi, input, signature)); + assert_rnp_success(rnp_op_verify_execute(verify)); + /* get signature and check it */ + sig_count = 0; + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sig_count)); + assert_int_equal(sig_count, 1); + /* get signature handle */ + assert_rnp_success(rnp_op_verify_get_signature_at(verify, 0, &sig)); + assert_rnp_success(rnp_op_verify_signature_get_status(sig)); + assert_rnp_success(rnp_op_verify_signature_get_handle(sig, &sighandle)); + assert_non_null(sighandle); + /* check signature type */ + assert_rnp_success(rnp_signature_get_type(sighandle, &sigtype)); + assert_string_equal(sigtype, "text"); + rnp_buffer_destroy(sigtype); + /* attempt to validate it via wrong function */ + assert_int_equal(rnp_signature_is_valid(sighandle, 0), RNP_ERROR_BAD_PARAMETERS); + /* cleanup, making sure that sighandle doesn't depend on verify */ + assert_rnp_success(rnp_op_verify_destroy(verify)); + assert_rnp_success(rnp_input_destroy(input)); + assert_rnp_success(rnp_input_destroy(signature)); + /* check whether getters work on sighandle: algorithm */ + assert_rnp_success(rnp_signature_get_alg(sighandle, &alg)); + assert_non_null(alg); + assert_string_equal(alg, "RSA"); + rnp_buffer_destroy(alg); + /* keyid */ + assert_rnp_success(rnp_signature_get_keyid(sighandle, &keyid)); + assert_non_null(keyid); + assert_string_equal(keyid, "5873BD738E575398"); + rnp_buffer_destroy(keyid); + /* creation time */ + assert_rnp_success(rnp_signature_get_creation(sighandle, &create)); + assert_int_equal(create, 1608118321); + /* hash algorithm */ + assert_rnp_success(rnp_signature_get_hash_alg(sighandle, &alg)); + assert_non_null(alg); + assert_string_equal(alg, "SHA256"); + rnp_buffer_destroy(alg); + /* now dump signature packet to json */ + assert_rnp_success(rnp_signature_packet_to_json(sighandle, 0, &json)); + jso = json_tokener_parse(json); + rnp_buffer_destroy(json); + assert_non_null(jso); + assert_true(json_object_is_type(jso, json_type_array)); + assert_int_equal(json_object_array_length(jso), 1); + /* check the signature packet dump */ + pkt = json_object_array_get_idx(jso, 0); + /* check helper functions */ + assert_false(check_json_field_int(pkt, "unknown", 4)); + assert_false(check_json_field_int(pkt, "version", 5)); + assert_true(check_json_field_int(pkt, "version", 4)); + assert_true(check_json_field_int(pkt, "type", 1)); + assert_true( + check_json_field_str(pkt, "type.str", "Signature of a canonical text document")); + assert_true(check_json_field_int(pkt, "algorithm", 1)); + assert_true(check_json_field_str(pkt, "algorithm.str", "RSA (Encrypt or Sign)")); + assert_true(check_json_field_int(pkt, "hash algorithm", 8)); + assert_true(check_json_field_str(pkt, "hash algorithm.str", "SHA256")); + assert_true(check_json_field_str(pkt, "lbits", "1037")); + subpkts = NULL; + assert_true(json_object_object_get_ex(pkt, "subpackets", &subpkts)); + assert_non_null(subpkts); + assert_true(json_object_is_type(subpkts, json_type_array)); + assert_int_equal(json_object_array_length(subpkts), 3); + /* subpacket 0 */ + subpkt = json_object_array_get_idx(subpkts, 0); + assert_true(check_json_field_int(subpkt, "type", 33)); + assert_true(check_json_field_str(subpkt, "type.str", "issuer fingerprint")); + assert_true(check_json_field_int(subpkt, "length", 21)); + assert_true(check_json_field_bool(subpkt, "hashed", true)); + assert_true(check_json_field_bool(subpkt, "critical", false)); + assert_true( + check_json_field_str(subpkt, "fingerprint", "7a60e671179f9b920f6478a25873bd738e575398")); + /* subpacket 1 */ + subpkt = json_object_array_get_idx(subpkts, 1); + assert_true(check_json_field_int(subpkt, "type", 2)); + assert_true(check_json_field_str(subpkt, "type.str", "signature creation time")); + assert_true(check_json_field_int(subpkt, "length", 4)); + assert_true(check_json_field_bool(subpkt, "hashed", true)); + assert_true(check_json_field_bool(subpkt, "critical", false)); + assert_true(check_json_field_int(subpkt, "creation time", 1608118321)); + /* subpacket 2 */ + subpkt = json_object_array_get_idx(subpkts, 2); + assert_true(check_json_field_int(subpkt, "type", 16)); + assert_true(check_json_field_str(subpkt, "type.str", "issuer key ID")); + assert_true(check_json_field_int(subpkt, "length", 8)); + assert_true(check_json_field_bool(subpkt, "hashed", false)); + assert_true(check_json_field_bool(subpkt, "critical", false)); + assert_true(check_json_field_str(subpkt, "issuer keyid", "5873bd738e575398")); + json_object_put(jso); + rnp_signature_handle_destroy(sighandle); + + /* attempt to validate a timestamp signature instead of detached */ + assert_rnp_success(rnp_input_from_path(&input, "data/test_stream_signatures/source.txt")); + assert_rnp_success( + rnp_input_from_path(&signature, "data/test_stream_signatures/signature-timestamp.asc")); + /* call verify detached to obtain signatures */ + assert_rnp_success(rnp_op_verify_detached_create(&verify, ffi, input, signature)); + assert_int_equal(rnp_op_verify_execute(verify), RNP_ERROR_SIGNATURE_INVALID); + /* get signature and check it */ + sig_count = 0; + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sig_count)); + assert_int_equal(sig_count, 1); + /* get signature handle */ + assert_rnp_success(rnp_op_verify_get_signature_at(verify, 0, &sig)); + assert_int_equal(rnp_op_verify_signature_get_status(sig), RNP_ERROR_SIGNATURE_INVALID); + assert_rnp_success(rnp_op_verify_signature_get_handle(sig, &sighandle)); + assert_non_null(sighandle); + /* check signature type */ + assert_rnp_success(rnp_signature_get_type(sighandle, &sigtype)); + assert_string_equal(sigtype, "timestamp"); + rnp_buffer_destroy(sigtype); + /* attempt to validate it via wrong function */ + assert_int_equal(rnp_signature_is_valid(sighandle, 0), RNP_ERROR_BAD_PARAMETERS); + /* cleanup, making sure that sighandle doesn't depend on verify */ + assert_rnp_success(rnp_op_verify_destroy(verify)); + assert_rnp_success(rnp_input_destroy(input)); + assert_rnp_success(rnp_input_destroy(signature)); + /* check whether getters work on sighandle: algorithm */ + assert_rnp_success(rnp_signature_get_alg(sighandle, &alg)); + assert_non_null(alg); + assert_string_equal(alg, "DSA"); + rnp_buffer_destroy(alg); + /* keyid */ + assert_rnp_success(rnp_signature_get_keyid(sighandle, &keyid)); + assert_non_null(keyid); + assert_string_equal(keyid, "2D727CC768697734"); + rnp_buffer_destroy(keyid); + /* creation time */ + assert_rnp_success(rnp_signature_get_creation(sighandle, &create)); + assert_int_equal(create, 1535389094); + /* hash algorithm */ + assert_rnp_success(rnp_signature_get_hash_alg(sighandle, &alg)); + assert_non_null(alg); + assert_string_equal(alg, "SHA512"); + rnp_buffer_destroy(alg); + /* now dump signature packet to json */ + assert_rnp_success(rnp_signature_packet_to_json(sighandle, 0, &json)); + jso = json_tokener_parse(json); + rnp_buffer_destroy(json); + assert_non_null(jso); + assert_true(json_object_is_type(jso, json_type_array)); + assert_int_equal(json_object_array_length(jso), 1); + /* check the signature packet dump */ + pkt = json_object_array_get_idx(jso, 0); + /* check helper functions */ + assert_false(check_json_field_int(pkt, "unknown", 4)); + assert_false(check_json_field_int(pkt, "version", 5)); + assert_true(check_json_field_int(pkt, "version", 4)); + assert_true(check_json_field_int(pkt, "type", 0x40)); + assert_true(check_json_field_str(pkt, "type.str", "Timestamp signature")); + assert_true(check_json_field_int(pkt, "algorithm", 17)); + assert_true(check_json_field_str(pkt, "algorithm.str", "DSA")); + assert_true(check_json_field_int(pkt, "hash algorithm", 10)); + assert_true(check_json_field_str(pkt, "hash algorithm.str", "SHA512")); + assert_true(check_json_field_str(pkt, "lbits", "2727")); + subpkts = NULL; + assert_true(json_object_object_get_ex(pkt, "subpackets", &subpkts)); + assert_non_null(subpkts); + assert_true(json_object_is_type(subpkts, json_type_array)); + assert_int_equal(json_object_array_length(subpkts), 7); + /* subpacket 0 */ + subpkt = json_object_array_get_idx(subpkts, 0); + assert_true(check_json_field_int(subpkt, "type", 2)); + assert_true(check_json_field_str(subpkt, "type.str", "signature creation time")); + assert_true(check_json_field_int(subpkt, "length", 4)); + assert_true(check_json_field_bool(subpkt, "hashed", true)); + assert_true(check_json_field_bool(subpkt, "critical", true)); + assert_true(check_json_field_int(subpkt, "creation time", 1535389094)); + /* subpacket 1 */ + subpkt = json_object_array_get_idx(subpkts, 1); + assert_true(check_json_field_int(subpkt, "type", 7)); + assert_true(check_json_field_str(subpkt, "type.str", "revocable")); + assert_true(check_json_field_int(subpkt, "length", 1)); + assert_true(check_json_field_bool(subpkt, "hashed", true)); + assert_true(check_json_field_bool(subpkt, "critical", true)); + assert_true(check_json_field_bool(subpkt, "revocable", false)); + /* subpacket 2 */ + subpkt = json_object_array_get_idx(subpkts, 2); + assert_true(check_json_field_int(subpkt, "type", 16)); + assert_true(check_json_field_str(subpkt, "type.str", "issuer key ID")); + assert_true(check_json_field_int(subpkt, "length", 8)); + assert_true(check_json_field_bool(subpkt, "hashed", true)); + assert_true(check_json_field_bool(subpkt, "critical", true)); + assert_true(check_json_field_str(subpkt, "issuer keyid", "2d727cc768697734")); + /* subpacket 3 */ + subpkt = json_object_array_get_idx(subpkts, 3); + assert_true(check_json_field_int(subpkt, "type", 20)); + assert_true(check_json_field_str(subpkt, "type.str", "notation data")); + assert_true(check_json_field_int(subpkt, "length", 51)); + assert_true(check_json_field_bool(subpkt, "hashed", true)); + assert_true(check_json_field_bool(subpkt, "critical", false)); + assert_true(check_json_field_bool(subpkt, "human", true)); + assert_true(check_json_field_str(subpkt, "name", "serialnumber@dots.testdomain.test")); + assert_true(check_json_field_str(subpkt, "value", "TEST000001")); + /* subpacket 4 */ + subpkt = json_object_array_get_idx(subpkts, 4); + assert_true(check_json_field_int(subpkt, "type", 26)); + assert_true(check_json_field_str(subpkt, "type.str", "policy URI")); + assert_true(check_json_field_int(subpkt, "length", 44)); + assert_true(check_json_field_bool(subpkt, "hashed", true)); + assert_true(check_json_field_bool(subpkt, "critical", false)); + assert_true( + check_json_field_str(subpkt, "uri", "https://policy.testdomain.test/timestamping/")); + /* subpacket 5 */ + subpkt = json_object_array_get_idx(subpkts, 5); + assert_true(check_json_field_int(subpkt, "type", 32)); + assert_true(check_json_field_str(subpkt, "type.str", "embedded signature")); + assert_true(check_json_field_int(subpkt, "length", 105)); + assert_true(check_json_field_bool(subpkt, "hashed", true)); + assert_true(check_json_field_bool(subpkt, "critical", true)); + json_object *embsig = NULL; + assert_true(json_object_object_get_ex(subpkt, "signature", &embsig)); + assert_true(check_json_field_int(embsig, "version", 4)); + assert_true(check_json_field_int(embsig, "type", 0)); + assert_true(check_json_field_str(embsig, "type.str", "Signature of a binary document")); + assert_true(check_json_field_int(embsig, "algorithm", 17)); + assert_true(check_json_field_str(embsig, "algorithm.str", "DSA")); + assert_true(check_json_field_int(embsig, "hash algorithm", 10)); + assert_true(check_json_field_str(embsig, "hash algorithm.str", "SHA512")); + assert_true(check_json_field_str(embsig, "lbits", "a386")); + /* subpacket 6 */ + subpkt = json_object_array_get_idx(subpkts, 6); + assert_true(check_json_field_int(subpkt, "type", 33)); + assert_true(check_json_field_str(subpkt, "type.str", "issuer fingerprint")); + assert_true(check_json_field_int(subpkt, "length", 21)); + assert_true(check_json_field_bool(subpkt, "hashed", true)); + assert_true(check_json_field_bool(subpkt, "critical", false)); + assert_true( + check_json_field_str(subpkt, "fingerprint", "a0ff4590bb6122edef6e3c542d727cc768697734")); + json_object_put(jso); + rnp_signature_handle_destroy(sighandle); + + /* cleanup ffi */ + assert_rnp_success(rnp_ffi_destroy(ffi)); +} + +TEST_F(rnp_tests, test_ffi_locate_key) +{ + rnp_ffi_t ffi = NULL; + + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + // load our keyrings + assert_true(load_keys_gpg(ffi, "data/keyrings/1/pubring.gpg")); + + // keyid + { + static const char *ids[] = {"7BC6709B15C23A4A", + "1ED63EE56FADC34D", + "1D7E8A5393C997A8", + "8A05B89FAD5ADED1", + "2FCADF05FFA501BB", + "54505A936A4A970E", + "326EF111425D14A5"}; + for (size_t i = 0; i < ARRAY_SIZE(ids); i++) { + const char * id = ids[i]; + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", id, &key)); + assert_non_null(key); + rnp_key_handle_destroy(key); + } + // invalid - value did not change + { + rnp_key_handle_t key = (rnp_key_handle_t) 0x111; + assert_rnp_failure(rnp_locate_key(ffi, "keyid", "invalid-keyid", &key)); + assert_true(key == (rnp_key_handle_t) 0x111); + } + // valid but non-existent - null returned + { + rnp_key_handle_t key = (rnp_key_handle_t) 0x111; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "AAAAAAAAAAAAAAAA", &key)); + assert_null(key); + } + } + + // userid + { + static const char *ids[] = { + "key0-uid0", "key0-uid1", "key0-uid2", "key1-uid0", "key1-uid2", "key1-uid1"}; + for (size_t i = 0; i < ARRAY_SIZE(ids); i++) { + const char * id = ids[i]; + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "userid", id, &key)); + assert_non_null(key); + rnp_key_handle_destroy(key); + } + // valid but non-existent + { + rnp_key_handle_t key = (rnp_key_handle_t) 0x111; + assert_rnp_success(rnp_locate_key(ffi, "userid", "bad-userid", &key)); + assert_null(key); + } + } + + // fingerprint + { + static const char *ids[] = {"E95A3CBF583AA80A2CCC53AA7BC6709B15C23A4A", + "E332B27CAF4742A11BAA677F1ED63EE56FADC34D", + "C5B15209940A7816A7AF3FB51D7E8A5393C997A8", + "5CD46D2A0BD0B8CFE0B130AE8A05B89FAD5ADED1", + "BE1C4AB951F4C2F6B604C7F82FCADF05FFA501BB", + "A3E94DE61A8CB229413D348E54505A936A4A970E", + "57F8ED6E5C197DB63C60FFAF326EF111425D14A5"}; + for (size_t i = 0; i < ARRAY_SIZE(ids); i++) { + const char * id = ids[i]; + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "fingerprint", id, &key)); + assert_non_null(key); + rnp_key_handle_destroy(key); + } + // invalid + { + rnp_key_handle_t key = (rnp_key_handle_t) 0x111; + assert_rnp_failure(rnp_locate_key(ffi, "fingerprint", "invalid-fpr", &key)); + assert_true(key == (rnp_key_handle_t) 0x111); + } + // valid but non-existent + { + rnp_key_handle_t key = (rnp_key_handle_t) 0x111; + assert_rnp_success(rnp_locate_key( + ffi, "fingerprint", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", &key)); + assert_null(key); + } + } + + // grip + { + static const char *ids[] = {"66D6A0800A3FACDE0C0EB60B16B3669ED380FDFA", + "D9839D61EDAF0B3974E0A4A341D6E95F3479B9B7", + "B1CC352FEF9A6BD4E885B5351840EF9306D635F0", + "E7C8860B70DC727BED6DB64C633683B41221BB40", + "B2A7F6C34AA2C15484783E9380671869A977A187", + "43C01D6D96BE98C3C87FE0F175870ED92DE7BE45", + "8082FE753013923972632550838A5F13D81F43B9"}; + for (size_t i = 0; i < ARRAY_SIZE(ids); i++) { + const char * id = ids[i]; + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "grip", id, &key)); + assert_non_null(key); + rnp_key_handle_destroy(key); + } + // invalid + { + rnp_key_handle_t key = (rnp_key_handle_t) 0x111; + assert_rnp_failure(rnp_locate_key(ffi, "grip", "invalid-fpr", &key)); + assert_true(key == (rnp_key_handle_t) 0x111); + } + // valid but non-existent + { + rnp_key_handle_t key = (rnp_key_handle_t) 0x111; + assert_rnp_success( + rnp_locate_key(ffi, "grip", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", &key)); + assert_null(key); + } + } + + // cleanup + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_signatures_detached_memory_g10) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_input_t input_sig = NULL; + rnp_output_t output = NULL; + rnp_key_handle_t key = NULL; + rnp_op_sign_t opsign = NULL; + rnp_op_verify_t opverify = NULL; + const char * data = "my data"; + uint8_t * sig = NULL; + size_t sig_len = 0; + + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "KBX", "G10")); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + + // load our keyrings + assert_true(load_keys_kbx_g10( + ffi, "data/keyrings/3/pubring.kbx", "data/keyrings/3/private-keys-v1.d")); + + // find our signing key + assert_rnp_success(rnp_locate_key(ffi, "keyid", "4BE147BB22DF1E60", &key)); + assert_non_null(key); + + // create our input + assert_rnp_success(rnp_input_from_memory(&input, (uint8_t *) data, strlen(data), false)); + assert_non_null(input); + // create our output + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_non_null(output); + // create the signing operation + assert_rnp_success(rnp_op_sign_detached_create(&opsign, ffi, input, output)); + assert_non_null(opsign); + + // add the signer + assert_rnp_success(rnp_op_sign_add_signature(opsign, key, NULL)); + // execute the signing operation + assert_rnp_success(rnp_op_sign_execute(opsign)); + // get the resulting signature + assert_rnp_success(rnp_output_memory_get_buf(output, &sig, &sig_len, true)); + assert_non_null(sig); + assert_int_not_equal(0, sig_len); + // cleanup + rnp_op_sign_destroy(opsign); + opsign = NULL; + rnp_input_destroy(input); + input = NULL; + rnp_output_destroy(output); + output = NULL; + + // verify + // create our data input + assert_rnp_success(rnp_input_from_memory(&input, (uint8_t *) data, strlen(data), false)); + assert_non_null(input); + // create our signature input + assert_rnp_success(rnp_input_from_memory(&input_sig, sig, sig_len, true)); + assert_non_null(input_sig); + // create our operation + assert_rnp_success(rnp_op_verify_detached_create(&opverify, ffi, input, input_sig)); + assert_non_null(opverify); + // execute the verification + assert_rnp_success(rnp_op_verify_execute(opverify)); + // cleanup + rnp_op_verify_destroy(opverify); + opverify = NULL; + rnp_input_destroy(input); + input = NULL; + rnp_input_destroy(input_sig); + input_sig = NULL; + + // verify (tamper with signature) + // create our data input + assert_rnp_success(rnp_input_from_memory(&input, (uint8_t *) data, strlen(data), false)); + assert_non_null(input); + // create our signature input + sig[sig_len - 5] ^= 0xff; + assert_rnp_success(rnp_input_from_memory(&input_sig, sig, sig_len, true)); + assert_non_null(input_sig); + // create our operation + assert_rnp_success(rnp_op_verify_detached_create(&opverify, ffi, input, input_sig)); + assert_non_null(opverify); + // execute the verification + assert_rnp_failure(rnp_op_verify_execute(opverify)); + // cleanup + rnp_op_verify_destroy(opverify); + opverify = NULL; + rnp_input_destroy(input); + input = NULL; + rnp_input_destroy(input_sig); + input_sig = NULL; + + // cleanup + rnp_buffer_destroy(sig); + rnp_key_handle_destroy(key); + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_enarmor_dearmor) +{ + std::string data; + + // enarmor plain message + const std::string msg("this is a test"); + data.clear(); + { + uint8_t * buf = NULL; + size_t buf_size = 0; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + + assert_rnp_success( + rnp_input_from_memory(&input, (const uint8_t *) msg.data(), msg.size(), true)); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + + assert_rnp_success(rnp_enarmor(input, output, "message")); + + rnp_output_memory_get_buf(output, &buf, &buf_size, false); + data = std::string(buf, buf + buf_size); + assert_true(starts_with(data, "-----BEGIN PGP MESSAGE-----\r\n")); + assert_true(ends_with(data, "-----END PGP MESSAGE-----\r\n")); + + rnp_input_destroy(input); + rnp_output_destroy(output); + } + { + uint8_t * buf = NULL; + size_t buf_size = 0; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + + assert_rnp_success( + rnp_input_from_memory(&input, (const uint8_t *) data.data(), data.size(), true)); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + + assert_rnp_success(rnp_dearmor(input, output)); + + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &buf_size, false)); + std::string dearmored(buf, buf + buf_size); + assert_true(msg == dearmored); + + rnp_input_destroy(input); + rnp_output_destroy(output); + } + + // enarmor public key + data.clear(); + { + uint8_t * buf = NULL; + size_t buf_size = 0; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + + // enarmor + assert_rnp_success(rnp_input_from_path(&input, "data/keyrings/1/pubring.gpg")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + + assert_rnp_success(rnp_enarmor(input, output, NULL)); + + rnp_output_memory_get_buf(output, &buf, &buf_size, false); + data = std::string(buf, buf + buf_size); + assert_true(starts_with(data, "-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n")); + assert_true(ends_with(data, "-----END PGP PUBLIC KEY BLOCK-----\r\n")); + + rnp_input_destroy(input); + rnp_output_destroy(output); + } + // dearmor public key + { + uint8_t * buf = NULL; + size_t buf_size = 0; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + + assert_rnp_success( + rnp_input_from_memory(&input, (const uint8_t *) data.data(), data.size(), true)); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + + assert_rnp_success(rnp_dearmor(input, output)); + + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &buf_size, false)); + std::string dearmored(buf, buf + buf_size); + std::ifstream inf("data/keyrings/1/pubring.gpg", std::ios::binary | std::ios::ate); + std::string from_disk(inf.tellg(), ' '); + inf.seekg(0); + inf.read(&from_disk[0], from_disk.size()); + inf.close(); + assert_true(dearmored == from_disk); + + rnp_input_destroy(input); + rnp_output_destroy(output); + } + // test truncated armored data + { + std::ifstream keyf("data/test_stream_key_load/rsa-rsa-pub.asc", + std::ios::binary | std::ios::ate); + std::string keystr(keyf.tellg(), ' '); + keyf.seekg(0); + keyf.read(&keystr[0], keystr.size()); + keyf.close(); + for (size_t sz = keystr.size() - 2; sz > 0; sz--) { + rnp_input_t input = NULL; + rnp_output_t output = NULL; + + assert_rnp_success( + rnp_input_from_memory(&input, (const uint8_t *) keystr.data(), sz, true)); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_failure(rnp_dearmor(input, output)); + + rnp_input_destroy(input); + rnp_output_destroy(output); + } + } +} + +TEST_F(rnp_tests, test_ffi_dearmor_edge_cases) +{ + rnp_input_t input = NULL; + rnp_output_t output = NULL; + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_armor/long_header_line.asc")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_dearmor(input, output)); + uint8_t *buf = NULL; + size_t len = 0; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + assert_int_equal(len, 2226); + rnp_input_destroy(input); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_armor/empty_header_line.asc")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_dearmor(input, output)); + buf = NULL; + len = 0; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + assert_int_equal(len, 2226); + rnp_input_destroy(input); + rnp_output_destroy(output); + + assert_rnp_success(rnp_input_from_path( + &input, "data/test_stream_armor/64k_whitespace_before_armored_message.asc")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_failure(rnp_dearmor(input, output)); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* Armor header starts and fits in the first 1024 bytes of the input. Prepended by + * whitespaces. */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_armor/1024_peek_buf.asc")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_dearmor(input, output)); + buf = NULL; + len = 0; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + assert_int_equal(len, 2226); + rnp_input_destroy(input); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_armor/blank_line_with_whitespace.asc")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_dearmor(input, output)); + buf = NULL; + len = 0; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + assert_int_equal(len, 2226); + rnp_input_destroy(input); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_armor/duplicate_header_line.asc")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_dearmor(input, output)); + buf = NULL; + len = 0; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + assert_int_equal(len, 2226); + rnp_input_destroy(input); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_armor/long_header_line_1024.asc")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_dearmor(input, output)); + buf = NULL; + len = 0; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + assert_int_equal(len, 2226); + rnp_input_destroy(input); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_armor/long_header_line_64k.asc")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_dearmor(input, output)); + buf = NULL; + len = 0; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + assert_int_equal(len, 2226); + rnp_input_destroy(input); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_armor/long_header_nameline_64k.asc")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_dearmor(input, output)); + buf = NULL; + len = 0; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + assert_int_equal(len, 2226); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* Armored message encoded in a single >64k text line */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_armor/message_64k_oneline.asc")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_dearmor(input, output)); + buf = NULL; + len = 0; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + assert_int_equal(len, 68647); + rnp_input_destroy(input); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_armor/wrong_header_line.asc")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_dearmor(input, output)); + buf = NULL; + len = 0; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + assert_int_equal(len, 2226); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* invalid, > 127 (negative char), preceding the armor header - just warning */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_armor/wrong_chars_header.asc")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_dearmor(input, output)); + buf = NULL; + len = 0; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + assert_int_equal(len, 2226); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* invalid, > 127, base64 chars at positions 1..4 */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_armor/wrong_chars_base64_1.asc")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_failure(rnp_dearmor(input, output)); + rnp_input_destroy(input); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_armor/wrong_chars_base64_2.asc")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_failure(rnp_dearmor(input, output)); + rnp_input_destroy(input); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_armor/wrong_chars_base64_3.asc")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_failure(rnp_dearmor(input, output)); + rnp_input_destroy(input); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_armor/wrong_chars_base64_4.asc")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_failure(rnp_dearmor(input, output)); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* invalid, > 127 base64 char in the crc */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_armor/wrong_chars_crc.asc")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_failure(rnp_dearmor(input, output)); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* too short armor header */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_armor/too_short_header.asc")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_failure(rnp_dearmor(input, output)); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* wrong base64 padding */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_armor/wrong_b64_trailer.asc")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_failure(rnp_dearmor(input, output)); + rnp_input_destroy(input); + rnp_output_destroy(output); +} + +TEST_F(rnp_tests, test_ffi_customized_enarmor) +{ + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_output_t armor_layer = NULL; + const std::string msg("this is a test long enough to have more than 76 characters in " + "enarmored representation"); + std::set<std::string> lines_to_skip{"-----BEGIN PGP MESSAGE-----", + "-----END PGP MESSAGE-----"}; + + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_output_to_armor(output, &armor_layer, "message")); + // should fail when trying to set line length on non-armor output + assert_rnp_failure(rnp_output_armor_set_line_length(output, 64)); + // should fail when trying to set zero line length + assert_rnp_failure(rnp_output_armor_set_line_length(armor_layer, 0)); + // should fail when trying to set line length less than the minimum allowed 16 + assert_rnp_failure(rnp_output_armor_set_line_length(armor_layer, 15)); + assert_rnp_success(rnp_output_armor_set_line_length(armor_layer, 16)); + assert_rnp_success(rnp_output_armor_set_line_length(armor_layer, 76)); + // should fail when trying to set line length greater than the maximum allowed 76 + assert_rnp_failure(rnp_output_armor_set_line_length(armor_layer, 77)); + assert_rnp_success(rnp_output_destroy(armor_layer)); + assert_rnp_success(rnp_output_destroy(output)); + + for (size_t llen = 16; llen <= 76; llen++) { + std::string data; + uint8_t * buf = NULL; + size_t buf_size = 0; + + input = NULL; + output = NULL; + armor_layer = NULL; + assert_rnp_success( + rnp_input_from_memory(&input, (const uint8_t *) msg.data(), msg.size(), true)); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_output_to_armor(output, &armor_layer, "message")); + assert_rnp_success(rnp_output_armor_set_line_length(armor_layer, llen)); + assert_rnp_success(rnp_output_pipe(input, armor_layer)); + assert_rnp_success(rnp_output_finish(armor_layer)); + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &buf_size, false)); + data = std::string(buf, buf + buf_size); + auto effective_llen = get_longest_line_length(data, lines_to_skip); + assert_int_equal(llen / 4, effective_llen / 4); + assert_true(llen >= effective_llen); + assert_rnp_success(rnp_input_destroy(input)); + assert_rnp_success(rnp_output_destroy(armor_layer)); + assert_rnp_success(rnp_output_destroy(output)); + + // test that the dearmored message is correct + assert_rnp_success( + rnp_input_from_memory(&input, (const uint8_t *) data.data(), data.size(), true)); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + + assert_rnp_success(rnp_dearmor(input, output)); + + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &buf_size, false)); + std::string dearmored(buf, buf + buf_size); + assert_true(msg == dearmored); + + assert_rnp_success(rnp_input_destroy(input)); + assert_rnp_success(rnp_output_destroy(output)); + } +} + +TEST_F(rnp_tests, test_ffi_version) +{ + const uint32_t version = rnp_version(); + const uint32_t major = rnp_version_major(version); + const uint32_t minor = rnp_version_minor(version); + const uint32_t patch = rnp_version_patch(version); + + // reconstruct the version string + assert_string_equal(fmt("%d.%d.%d", major, minor, patch).c_str(), rnp_version_string()); + + // full version string should probably be at least as long as regular version string + assert_true(strlen(rnp_version_string_full()) >= strlen(rnp_version_string())); + + // reconstruct the version value + assert_int_equal(version, rnp_version_for(major, minor, patch)); + + // check out-of-range handling + assert_int_equal(0, rnp_version_for(1024, 0, 0)); + assert_int_equal(0, rnp_version_for(0, 1024, 0)); + assert_int_equal(0, rnp_version_for(0, 0, 1024)); + + // check component extraction again + assert_int_equal(rnp_version_major(rnp_version_for(5, 4, 3)), 5); + assert_int_equal(rnp_version_minor(rnp_version_for(5, 4, 3)), 4); + assert_int_equal(rnp_version_patch(rnp_version_for(5, 4, 3)), 3); + + // simple comparisons + assert_true(rnp_version_for(1, 0, 1) > rnp_version_for(1, 0, 0)); + assert_true(rnp_version_for(1, 1, 0) > rnp_version_for(1, 0, 1023)); + assert_true(rnp_version_for(2, 0, 0) > rnp_version_for(1, 1023, 1023)); + + // commit timestamp + const uint64_t timestamp = rnp_version_commit_timestamp(); + assert_true(!timestamp || (timestamp >= 1639439116)); +} + +TEST_F(rnp_tests, test_ffi_backend_version) +{ + assert_non_null(rnp_backend_string()); + assert_non_null(rnp_backend_version()); + + assert_true(strlen(rnp_backend_string()) > 0 && strlen(rnp_backend_string()) < 255); + assert_true(strlen(rnp_backend_version()) > 0 && strlen(rnp_backend_version()) < 255); +} + +void check_loaded_keys(const char * format, + bool armored, + uint8_t * buf, + size_t buf_len, + const char * id_type, + const std::vector<std::string> &expected_ids, + bool secret); + +TEST_F(rnp_tests, test_ffi_key_export_customized_enarmor) +{ + rnp_ffi_t ffi = NULL; + rnp_output_t output = NULL; + rnp_output_t armor_layer = NULL; + rnp_key_handle_t key = NULL; + uint8_t * buf = NULL; + size_t buf_len = 0; + std::set<std::string> lines_to_skip{"-----BEGIN PGP PUBLIC KEY BLOCK-----", + "-----END PGP PUBLIC KEY BLOCK-----", + "-----BEGIN PGP PRIVATE KEY BLOCK-----", + "-----END PGP PRIVATE KEY BLOCK-----"}; + // setup FFI + test_ffi_init(&ffi); + + for (size_t llen = 16; llen <= 76; llen++) { + // primary pub only + { + // locate key + key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "2FCADF05FFA501BB", &key)); + assert_non_null(key); + + // create output + output = NULL; + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_non_null(output); + assert_rnp_success(rnp_output_to_armor(output, &armor_layer, "public key")); + assert_non_null(armor_layer); + assert_rnp_success(rnp_output_armor_set_line_length(armor_layer, llen)); + + // export + assert_rnp_success(rnp_key_export(key, armor_layer, RNP_KEY_EXPORT_PUBLIC)); + assert_rnp_success(rnp_output_finish(armor_layer)); + // get output + buf = NULL; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &buf_len, false)); + assert_non_null(buf); + std::string data = std::string(buf, buf + buf_len); + auto effective_llen = get_longest_line_length(data, lines_to_skip); + assert_int_equal(llen / 4, effective_llen / 4); + assert_true(llen >= effective_llen); + + // check results + check_loaded_keys("GPG", true, buf, buf_len, "keyid", {"2FCADF05FFA501BB"}, false); + + // cleanup + rnp_output_destroy(armor_layer); + rnp_output_destroy(output); + rnp_key_handle_destroy(key); + } + + // primary sec only + { + // locate key + key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "2FCADF05FFA501BB", &key)); + assert_non_null(key); + + // create output + output = NULL; + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_non_null(output); + assert_rnp_success(rnp_output_to_armor(output, &armor_layer, "secret key")); + assert_non_null(armor_layer); + assert_rnp_success(rnp_output_armor_set_line_length(armor_layer, llen)); + + // export + assert_rnp_success(rnp_key_export(key, armor_layer, RNP_KEY_EXPORT_SECRET)); + assert_rnp_success(rnp_output_finish(armor_layer)); + + // get output + buf = NULL; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &buf_len, false)); + assert_non_null(buf); + std::string data = std::string(buf, buf + buf_len); + auto effective_llen = get_longest_line_length(data, lines_to_skip); + assert_int_equal(llen / 4, effective_llen / 4); + assert_true(llen >= effective_llen); + + // check results + check_loaded_keys("GPG", true, buf, buf_len, "keyid", {"2FCADF05FFA501BB"}, true); + + // cleanup + rnp_output_destroy(armor_layer); + rnp_output_destroy(output); + rnp_key_handle_destroy(key); + } + + // sub pub + { + // locate key + key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "54505A936A4A970E", &key)); + assert_non_null(key); + + // create output + output = NULL; + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_non_null(output); + assert_rnp_success(rnp_output_to_armor(output, &armor_layer, "public key")); + assert_non_null(armor_layer); + assert_rnp_success(rnp_output_armor_set_line_length(armor_layer, llen)); + + // export + assert_rnp_success(rnp_key_export(key, armor_layer, RNP_KEY_EXPORT_PUBLIC)); + assert_rnp_success(rnp_output_finish(armor_layer)); + + // get output + buf = NULL; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &buf_len, false)); + assert_non_null(buf); + std::string data = std::string(buf, buf + buf_len); + auto effective_llen = get_longest_line_length(data, lines_to_skip); + assert_int_equal(llen / 4, effective_llen / 4); + assert_true(llen >= effective_llen); + + // check results + check_loaded_keys("GPG", + true, + buf, + buf_len, + "keyid", + {"2FCADF05FFA501BB", "54505A936A4A970E"}, + false); + + // cleanup + rnp_output_destroy(armor_layer); + rnp_output_destroy(output); + rnp_key_handle_destroy(key); + } + + // sub sec + { + // locate key + key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "54505A936A4A970E", &key)); + assert_non_null(key); + + // create output + output = NULL; + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_non_null(output); + assert_rnp_success(rnp_output_to_armor(output, &armor_layer, "secret key")); + assert_non_null(armor_layer); + assert_rnp_success(rnp_output_armor_set_line_length(armor_layer, llen)); + + // export + assert_rnp_success(rnp_key_export(key, armor_layer, RNP_KEY_EXPORT_SECRET)); + assert_rnp_success(rnp_output_finish(armor_layer)); + + // get output + buf = NULL; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &buf_len, false)); + assert_non_null(buf); + std::string data = std::string(buf, buf + buf_len); + auto effective_llen = get_longest_line_length(data, lines_to_skip); + assert_int_equal(llen / 4, effective_llen / 4); + assert_true(llen >= effective_llen); + + // check results + check_loaded_keys("GPG", + true, + buf, + buf_len, + "keyid", + {"2FCADF05FFA501BB", "54505A936A4A970E"}, + true); + + // cleanup + rnp_output_destroy(armor_layer); + rnp_output_destroy(output); + rnp_key_handle_destroy(key); + } + } + // cleanup + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_key_dump) +{ + rnp_ffi_t ffi = NULL; + rnp_key_handle_t key = NULL; + char * json = NULL; + json_object * jso = NULL; + + // setup FFI + test_ffi_init(&ffi); + + // locate key + assert_rnp_success(rnp_locate_key(ffi, "keyid", "2FCADF05FFA501BB", &key)); + assert_non_null(key); + + // dump public key and check results + assert_rnp_success(rnp_key_packets_to_json( + key, false, RNP_JSON_DUMP_MPI | RNP_JSON_DUMP_RAW | RNP_JSON_DUMP_GRIP, &json)); + assert_non_null(json); + jso = json_tokener_parse(json); + assert_non_null(jso); + assert_true(json_object_is_type(jso, json_type_array)); + json_object_put(jso); + rnp_buffer_destroy(json); + + // dump secret key and check results + assert_rnp_success(rnp_key_packets_to_json( + key, true, RNP_JSON_DUMP_MPI | RNP_JSON_DUMP_RAW | RNP_JSON_DUMP_GRIP, &json)); + assert_non_null(json); + jso = json_tokener_parse(json); + assert_non_null(jso); + assert_true(json_object_is_type(jso, json_type_array)); + json_object_put(jso); + rnp_buffer_destroy(json); + + // cleanup + rnp_key_handle_destroy(key); + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_key_dump_edge_cases) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + + /* secret key, stored on gpg card, with too large card serial len */ + rnp_input_t input = NULL; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_edge_cases/alice-s2k-101-2-card-len.pgp")); + rnp_output_t output = NULL; + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_dump_packets_to_output(input, output, 0)); + rnp_input_destroy(input); + uint8_t *buf = NULL; + size_t len = 0; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + std::string dstr(buf, buf + len); + assert_true( + dstr.find("card serial number: 0x000102030405060708090a0b0c0d0e0f (16 bytes)") != + std::string::npos); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_edge_cases/alice-s2k-101-2-card-len.pgp")); + char *json = NULL; + assert_rnp_success(rnp_dump_packets_to_json(input, 0, &json)); + rnp_input_destroy(input); + dstr = json; + assert_true(dstr.find("\"card serial number\":\"000102030405060708090a0b0c0d0e0f\"") != + std::string::npos); + rnp_buffer_destroy(json); + + /* secret key, stored with unknown gpg s2k */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_edge_cases/alice-s2k-101-3.pgp")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_dump_packets_to_output(input, output, 0)); + rnp_input_destroy(input); + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + dstr = std::string(buf, buf + len); + assert_true(dstr.find("Unknown experimental s2k: 0x474e5503 (4 bytes)") != + std::string::npos); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_edge_cases/alice-s2k-101-3.pgp")); + assert_rnp_success(rnp_dump_packets_to_json(input, 0, &json)); + rnp_input_destroy(input); + dstr = json; + assert_true(dstr.find("\"unknown experimental\":\"474e5503\"") != std::string::npos); + rnp_buffer_destroy(json); + + /* secret key, stored with unknown s2k */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_edge_cases/alice-s2k-101-unknown.pgp")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_dump_packets_to_output(input, output, 0)); + rnp_input_destroy(input); + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + dstr = std::string(buf, buf + len); + assert_true(dstr.find("Unknown experimental s2k: 0x554e4b4e (4 bytes)") != + std::string::npos); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_edge_cases/alice-s2k-101-unknown.pgp")); + assert_rnp_success(rnp_dump_packets_to_json(input, 0, &json)); + rnp_input_destroy(input); + dstr = json; + assert_true(dstr.find("\"unknown experimental\":\"554e4b4e\"") != std::string::npos); + rnp_buffer_destroy(json); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_key_userid_dump_has_no_special_chars) +{ + rnp_ffi_t ffi = NULL; + char * json = NULL; + json_object *jso = NULL; + const char * trackers[] = { + "userid\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f@rnp", + "userid\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f@rnp"}; + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + + for (int i = 0; i < 2; i++) { + // generate RSA key + rnp_op_generate_t keygen = NULL; + assert_rnp_success(rnp_op_generate_create(&keygen, ffi, "RSA")); + assert_rnp_success(rnp_op_generate_set_bits(keygen, 1024)); + // user id + assert_rnp_success(rnp_op_generate_set_userid(keygen, trackers[0])); + // now execute keygen operation + assert_rnp_success(rnp_op_generate_execute(keygen)); + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_op_generate_get_key(keygen, &key)); + assert_non_null(key); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + keygen = NULL; + + // dump public key and check results + assert_rnp_success(rnp_key_packets_to_json( + key, false, RNP_JSON_DUMP_MPI | RNP_JSON_DUMP_RAW | RNP_JSON_DUMP_GRIP, &json)); + assert_non_null(json); + for (char c = 1; c < 0x20; c++) { + if (c != '\n') { + assert_null(strchr(json, c)); + } + } + jso = json_tokener_parse(json); + assert_non_null(jso); + assert_true(json_object_is_type(jso, json_type_array)); + json_object_put(jso); + rnp_buffer_destroy(json); + + // cleanup + rnp_key_handle_destroy(key); + } + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_pkt_dump) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + char * json = NULL; + json_object *jso = NULL; + + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + + // setup input + assert_rnp_success(rnp_input_from_path(&input, "data/keyrings/1/pubring.gpg")); + + // try with wrong parameters + assert_rnp_failure(rnp_dump_packets_to_json(input, 0, NULL)); + assert_rnp_failure(rnp_dump_packets_to_json(NULL, 0, &json)); + assert_rnp_failure(rnp_dump_packets_to_json(input, 117, &json)); + // dump + assert_rnp_success(rnp_dump_packets_to_json( + input, RNP_JSON_DUMP_MPI | RNP_JSON_DUMP_RAW | RNP_JSON_DUMP_GRIP, &json)); + rnp_input_destroy(input); + input = NULL; + assert_non_null(json); + + // check results + jso = json_tokener_parse(json); + assert_non_null(jso); + assert_true(json_object_is_type(jso, json_type_array)); + /* make sure that correct number of packets dumped */ + assert_int_equal(json_object_array_length(jso), 35); + json_object_put(jso); + rnp_buffer_destroy(json); + + // setup input and output + rnp_output_t output = NULL; + assert_rnp_success(rnp_input_from_path(&input, "data/keyrings/1/pubring.gpg")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + + // try with wrong parameters + assert_rnp_failure(rnp_dump_packets_to_output(input, NULL, 0)); + assert_rnp_failure(rnp_dump_packets_to_output(NULL, output, 0)); + assert_rnp_failure(rnp_dump_packets_to_output(input, output, 117)); + // dump + assert_rnp_success( + rnp_dump_packets_to_output(input, output, RNP_DUMP_MPI | RNP_DUMP_RAW | RNP_DUMP_GRIP)); + + uint8_t *buf = NULL; + size_t len = 0; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + /* make sure output is not cut */ + assert_true(len > 45000); + rnp_input_destroy(input); + rnp_output_destroy(output); + + // dump data with marker packet + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message.txt.marker")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success( + rnp_dump_packets_to_output(input, output, RNP_DUMP_MPI | RNP_DUMP_RAW | RNP_DUMP_GRIP)); + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + buf[len - 1] = '\0'; + assert_non_null(strstr((char *) buf, "contents: PGP")); + rnp_input_destroy(input); + rnp_output_destroy(output); + + // dump data with marker packet to json + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message.txt.marker")); + assert_rnp_success(rnp_dump_packets_to_json( + input, RNP_JSON_DUMP_MPI | RNP_JSON_DUMP_RAW | RNP_JSON_DUMP_GRIP, &json)); + assert_non_null(strstr(json, "\"contents\":\"PGP\"")); + rnp_buffer_destroy(json); + rnp_input_destroy(input); + + // dump data with malformed marker packet + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.marker.malf")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success( + rnp_dump_packets_to_output(input, output, RNP_DUMP_MPI | RNP_DUMP_RAW | RNP_DUMP_GRIP)); + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + buf[len - 1] = '\0'; + assert_non_null(strstr((char *) buf, "contents: invalid")); + rnp_input_destroy(input); + rnp_output_destroy(output); + + // dump data with malformed marker packet to json + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.marker.malf")); + assert_rnp_success(rnp_dump_packets_to_json( + input, RNP_JSON_DUMP_MPI | RNP_JSON_DUMP_RAW | RNP_JSON_DUMP_GRIP, &json)); + assert_non_null(strstr(json, "\"contents\":\"invalid\"")); + rnp_buffer_destroy(json); + rnp_input_destroy(input); + + // cleanup + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_rsa_v3_dump) +{ + rnp_input_t input = NULL; + char * json = NULL; + + /* dump rsav3 key to json via FFI */ + assert_rnp_success(rnp_input_from_path(&input, "data/keyrings/4/rsav3-p.asc")); + assert_rnp_success(rnp_dump_packets_to_json(input, RNP_JSON_DUMP_GRIP, &json)); + rnp_input_destroy(input); + /* parse dump */ + json_object *jso = json_tokener_parse(json); + rnp_buffer_destroy(json); + assert_non_null(jso); + assert_true(json_object_is_type(jso, json_type_array)); + json_object *rsapkt = json_object_array_get_idx(jso, 0); + assert_non_null(rsapkt); + assert_true(json_object_is_type(rsapkt, json_type_object)); + /* check algorithm string */ + json_object *fld = NULL; + assert_true(json_object_object_get_ex(rsapkt, "algorithm.str", &fld)); + assert_non_null(fld); + const char *str = json_object_get_string(fld); + assert_non_null(str); + assert_string_equal(str, "RSA (Encrypt or Sign)"); + /* check fingerprint */ + fld = NULL; + assert_true(json_object_object_get_ex(rsapkt, "fingerprint", &fld)); + assert_non_null(fld); + str = json_object_get_string(fld); + assert_non_null(str); + assert_string_equal(str, "06a044022bb5aa7991077466aeba2ce7"); + json_object_put(jso); +} + +TEST_F(rnp_tests, test_ffi_load_userattr) +{ + rnp_ffi_t ffi = NULL; + + // init ffi and load key + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_true(load_keys_gpg(ffi, "data/test_stream_key_load/ecc-25519-photo-pub.asc")); + // check userid 0 : ecc-25519 + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "cc786278981b0728", &key)); + assert_non_null(key); + size_t uid_count = 0; + assert_rnp_success(rnp_key_get_uid_count(key, &uid_count)); + assert_int_equal(uid_count, 2); + char *uid = NULL; + assert_rnp_success(rnp_key_get_uid_at(key, 0, &uid)); + assert_string_equal(uid, "ecc-25519"); + rnp_buffer_destroy(uid); + // check userattr 1, must be text instead of binary JPEG data + assert_rnp_success(rnp_key_get_uid_at(key, 1, &uid)); + assert_string_equal(uid, "(photo)"); + rnp_buffer_destroy(uid); + assert_rnp_success(rnp_key_handle_destroy(key)); + // cleanup + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_revocations) +{ + rnp_ffi_t ffi = NULL; + + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + // load key with revoked userid + assert_true(load_keys_gpg(ffi, "data/test_stream_key_load/ecc-p256-revoked-uid.asc")); + // check userid 0 : ecc-p256 + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "userid", "ecc-p256", &key)); + assert_non_null(key); + size_t uid_count = 0; + assert_rnp_success(rnp_key_get_uid_count(key, &uid_count)); + assert_int_equal(uid_count, 2); + char *uid = NULL; + assert_rnp_success(rnp_key_get_uid_at(key, 0, &uid)); + assert_string_equal(uid, "ecc-p256"); + rnp_buffer_destroy(uid); + rnp_uid_handle_t uid_handle = NULL; + assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid_handle)); + assert_non_null(uid_handle); + bool revoked = true; + assert_rnp_failure(rnp_uid_is_revoked(NULL, &revoked)); + assert_rnp_failure(rnp_uid_is_revoked(uid_handle, NULL)); + assert_rnp_success(rnp_uid_is_revoked(uid_handle, &revoked)); + assert_false(revoked); + rnp_signature_handle_t sig = (rnp_signature_handle_t) 0xdeadbeef; + assert_rnp_failure(rnp_uid_get_revocation_signature(NULL, &sig)); + assert_rnp_failure(rnp_uid_get_revocation_signature(uid_handle, NULL)); + assert_rnp_success(rnp_uid_get_revocation_signature(uid_handle, &sig)); + assert_null(sig); + assert_rnp_success(rnp_uid_handle_destroy(uid_handle)); + // check userid 1: ecc-p256-revoked + assert_rnp_success(rnp_key_get_uid_at(key, 1, &uid)); + assert_string_equal(uid, "ecc-p256-revoked"); + rnp_buffer_destroy(uid); + assert_rnp_success(rnp_key_get_uid_handle_at(key, 1, &uid_handle)); + assert_non_null(uid_handle); + assert_rnp_success(rnp_uid_is_revoked(uid_handle, &revoked)); + assert_true(revoked); + assert_rnp_success(rnp_uid_get_revocation_signature(uid_handle, &sig)); + assert_non_null(sig); + uint32_t creation = 0; + assert_rnp_success(rnp_signature_get_creation(sig, &creation)); + assert_int_equal(creation, 1556630215); + assert_rnp_success(rnp_signature_handle_destroy(sig)); + assert_rnp_success(rnp_uid_handle_destroy(uid_handle)); + assert_rnp_success(rnp_key_handle_destroy(key)); + + // load key with revoked subkey + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC)); + assert_true(load_keys_gpg(ffi, "data/test_stream_key_load/ecc-p256-revoked-sub.asc")); + // key is not revoked + assert_rnp_success(rnp_locate_key(ffi, "userid", "ecc-p256", &key)); + assert_rnp_success(rnp_key_is_revoked(key, &revoked)); + assert_false(revoked); + assert_rnp_failure(rnp_key_get_revocation_signature(NULL, &sig)); + assert_rnp_failure(rnp_key_get_revocation_signature(key, NULL)); + assert_rnp_success(rnp_key_get_revocation_signature(key, &sig)); + assert_null(sig); + bool valid = false; + assert_rnp_success(rnp_key_is_valid(key, &valid)); + assert_true(valid); + uint32_t till = 0; + assert_rnp_success(rnp_key_valid_till(key, &till)); + assert_int_equal(till, 0xFFFFFFFF); + assert_rnp_success(rnp_key_handle_destroy(key)); + // subkey is revoked + assert_rnp_success(rnp_locate_key(ffi, "keyid", "37E285E9E9851491", &key)); + assert_rnp_success(rnp_key_is_revoked(key, &revoked)); + assert_true(revoked); + char *reason = NULL; + assert_rnp_success(rnp_key_get_revocation_reason(key, &reason)); + assert_string_equal(reason, "Subkey revocation test."); + rnp_buffer_destroy(reason); + assert_rnp_success(rnp_key_is_superseded(key, &revoked)); + assert_false(revoked); + assert_rnp_success(rnp_key_is_compromised(key, &revoked)); + assert_true(revoked); + assert_rnp_success(rnp_key_is_retired(key, &revoked)); + assert_false(revoked); + assert_rnp_success(rnp_key_get_revocation_signature(key, &sig)); + assert_non_null(sig); + assert_rnp_success(rnp_signature_get_creation(sig, &creation)); + assert_int_equal(creation, 1556630749); + assert_rnp_success(rnp_signature_handle_destroy(sig)); + assert_rnp_success(rnp_key_is_valid(key, &valid)); + assert_false(valid); + assert_rnp_success(rnp_key_valid_till(key, &till)); + assert_int_equal(till, 0); + assert_rnp_success(rnp_key_handle_destroy(key)); + + // load revoked key + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC)); + assert_true(load_keys_gpg(ffi, "data/test_stream_key_load/ecc-p256-revoked-key.asc")); + // key is revoked + assert_rnp_success(rnp_locate_key(ffi, "userid", "ecc-p256", &key)); + assert_rnp_success(rnp_key_is_revoked(key, &revoked)); + assert_true(revoked); + reason = NULL; + assert_rnp_success(rnp_key_get_revocation_reason(key, &reason)); + assert_string_equal(reason, "Superseded key test."); + rnp_buffer_destroy(reason); + assert_rnp_success(rnp_key_is_superseded(key, &revoked)); + assert_true(revoked); + assert_rnp_success(rnp_key_is_compromised(key, &revoked)); + assert_false(revoked); + assert_rnp_success(rnp_key_is_retired(key, &revoked)); + assert_false(revoked); + assert_rnp_success(rnp_key_get_revocation_signature(key, &sig)); + assert_non_null(sig); + assert_rnp_success(rnp_signature_get_creation(sig, &creation)); + assert_int_equal(creation, 1556799806); + assert_rnp_success(rnp_signature_handle_destroy(sig)); + assert_rnp_success(rnp_key_is_valid(key, &valid)); + assert_false(valid); + assert_rnp_success(rnp_key_valid_till(key, &till)); + assert_int_equal(till, 1556799806); + uint64_t till64 = 0; + assert_rnp_success(rnp_key_valid_till64(key, &till64)); + assert_int_equal(till64, 1556799806); + assert_rnp_success(rnp_key_handle_destroy(key)); + + // cleanup + rnp_ffi_destroy(ffi); +} + +#define KEY_OUT_PATH "exported-key.asc" + +TEST_F(rnp_tests, test_ffi_file_output) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + // load two keys + assert_true(load_keys_gpg(ffi, "data/test_stream_key_load/ecc-p256-pub.asc")); + assert_true(load_keys_gpg(ffi, "data/test_stream_key_load/ecc-p521-pub.asc")); + + rnp_key_handle_t k256 = NULL; + rnp_key_handle_t k521 = NULL; + assert_rnp_success(rnp_locate_key(ffi, "userid", "ecc-p256", &k256)); + assert_rnp_success(rnp_locate_key(ffi, "userid", "ecc-p521", &k521)); + + rnp_output_t output = NULL; + // test output to path - must overwrite if exists + assert_rnp_success(rnp_output_to_path(&output, KEY_OUT_PATH)); + assert_rnp_success(rnp_key_export( + k256, output, RNP_KEY_EXPORT_PUBLIC | RNP_KEY_EXPORT_ARMORED | RNP_KEY_EXPORT_SUBKEYS)); + assert_rnp_success(rnp_output_destroy(output)); + assert_true(rnp_file_exists(KEY_OUT_PATH)); + off_t sz = file_size(KEY_OUT_PATH); + assert_rnp_success(rnp_output_to_path(&output, KEY_OUT_PATH)); + assert_rnp_success(rnp_key_export( + k521, output, RNP_KEY_EXPORT_PUBLIC | RNP_KEY_EXPORT_ARMORED | RNP_KEY_EXPORT_SUBKEYS)); + assert_rnp_success(rnp_output_destroy(output)); + assert_true(rnp_file_exists(KEY_OUT_PATH)); + assert_true(sz != file_size(KEY_OUT_PATH)); + sz = file_size(KEY_OUT_PATH); + // test output to file - will fail without overwrite + assert_rnp_failure(rnp_output_to_file(&output, KEY_OUT_PATH, 0)); + // fail with wrong flags + assert_rnp_failure(rnp_output_to_file(&output, KEY_OUT_PATH, 0x100)); + // test output to random file - will succeed on creation and export but fail on finish. + assert_rnp_success(rnp_output_to_file(&output, KEY_OUT_PATH, RNP_OUTPUT_FILE_RANDOM)); + assert_true(file_size(KEY_OUT_PATH) == sz); + assert_rnp_success( + rnp_key_export(k256, output, RNP_KEY_EXPORT_PUBLIC | RNP_KEY_EXPORT_SUBKEYS)); + assert_rnp_failure(rnp_output_finish(output)); + assert_rnp_success(rnp_output_destroy(output)); + // test output with random + overwrite - will succeed + assert_rnp_success(rnp_output_to_file( + &output, KEY_OUT_PATH, RNP_OUTPUT_FILE_RANDOM | RNP_OUTPUT_FILE_OVERWRITE)); + assert_true(file_size(KEY_OUT_PATH) == sz); + assert_rnp_success( + rnp_key_export(k256, output, RNP_KEY_EXPORT_PUBLIC | RNP_KEY_EXPORT_SUBKEYS)); + assert_rnp_success(rnp_output_finish(output)); + assert_rnp_success(rnp_output_destroy(output)); + assert_true(file_size(KEY_OUT_PATH) != sz); + sz = file_size(KEY_OUT_PATH); + // test output with just overwrite - will succeed + assert_rnp_success(rnp_output_to_file(&output, KEY_OUT_PATH, RNP_OUTPUT_FILE_OVERWRITE)); + assert_true(file_size(KEY_OUT_PATH) == 0); + assert_rnp_success( + rnp_key_export(k521, output, RNP_KEY_EXPORT_PUBLIC | RNP_KEY_EXPORT_SUBKEYS)); + assert_rnp_success(rnp_output_finish(output)); + assert_rnp_success(rnp_output_destroy(output)); + assert_true(file_size(KEY_OUT_PATH) != sz); + assert_int_equal(rnp_unlink(KEY_OUT_PATH), 0); + // cleanup + assert_rnp_success(rnp_key_handle_destroy(k256)); + assert_rnp_success(rnp_key_handle_destroy(k521)); + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_stdout_output) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + + assert_true(load_keys_gpg(ffi, "data/test_stream_key_load/ecc-p256-pub.asc")); + + rnp_key_handle_t k256 = NULL; + assert_rnp_success(rnp_locate_key(ffi, "userid", "ecc-p256", &k256)); + + rnp_output_t output = NULL; + assert_rnp_failure(rnp_output_to_stdout(NULL)); + assert_rnp_success(rnp_output_to_stdout(&output)); + assert_rnp_success(rnp_key_export( + k256, output, RNP_KEY_EXPORT_PUBLIC | RNP_KEY_EXPORT_ARMORED | RNP_KEY_EXPORT_SUBKEYS)); + assert_rnp_success(rnp_output_destroy(output)); + assert_rnp_success(rnp_key_handle_destroy(k256)); + rnp_ffi_destroy(ffi); +} + +/* shrink the length to 1 packet + * set packet length type as PGP_PTAG_OLD_LEN_1 and remove one octet from length header + */ +static std::vector<uint8_t> +shrink_len_2_to_1(const std::vector<uint8_t> &src) +{ + std::vector<uint8_t> dst = std::vector<uint8_t>(); + dst.reserve(src.size() - 1); + dst.insert(dst.end(), + PGP_PTAG_ALWAYS_SET | (PGP_PKT_PUBLIC_KEY << PGP_PTAG_OF_CONTENT_TAG_SHIFT) | + PGP_PTAG_OLD_LEN_1); + // make sure the most significant octet of 2-octet length is actually zero + if (src[1] != 0) { + throw std::invalid_argument("src"); + } + dst.insert(dst.end(), src[2]); + dst.insert(dst.end(), src.begin() + 3, src.end()); + return dst; +} + +/* + * fake a packet with len = 0xEEEE + */ +static std::vector<uint8_t> +fake_len_EEEE(const std::vector<uint8_t> &src) +{ + std::vector<uint8_t> dst = std::vector<uint8_t>(src); + dst[1] = 0xEE; + dst[2] = 0xEE; + return dst; +} + +/* + * fake a packet with len = 0x00 + */ +static std::vector<uint8_t> +fake_len_0(const std::vector<uint8_t> &src) +{ + std::vector<uint8_t> dst = shrink_len_2_to_1(src); + // erase subsequent octets for the packet to correspond the length + uint8_t old_length = dst[1]; + dst.erase(dst.begin() + 2, dst.begin() + 2 + old_length); + dst[1] = 0; + return dst; +} + +/* extend the length to 4 octets (preserving the value) + * set packet length type as PGP_PTAG_OLD_LEN_4 and set 4 octet length instead of 2 + */ +static std::vector<uint8_t> +extend_len_2_to_4(const std::vector<uint8_t> &src) +{ + std::vector<uint8_t> dst = std::vector<uint8_t>(); + dst.reserve(src.size() + 2); + dst.insert(dst.end(), src.begin(), src.begin() + 3); + dst[0] &= ~PGP_PTAG_OF_LENGTH_TYPE_MASK; + dst[0] |= PGP_PTAG_OLD_LEN_4; + dst.insert(dst.begin() + 1, 2, 0); + dst.insert(dst.end(), src.begin() + 3, src.end()); + return dst; +} + +static bool +import_public_keys_from_vector(std::vector<uint8_t> keyring) +{ + rnp_ffi_t ffi = NULL; + rnp_ffi_create(&ffi, "GPG", "GPG"); + bool res = import_pub_keys(ffi, &keyring[0], keyring.size()); + rnp_ffi_destroy(ffi); + return res; +} + +TEST_F(rnp_tests, test_ffi_import_keys_check_pktlen) +{ + std::vector<uint8_t> keyring = file_to_vec("data/keyrings/2/pubring.gpg"); + // check tag + // we are assuming that original key uses old format and packet length type is + // PGP_PTAG_OLD_LEN_2 + assert_true(keyring.size() >= 5); + uint8_t expected_tag = PGP_PTAG_ALWAYS_SET | + (PGP_PKT_PUBLIC_KEY << PGP_PTAG_OF_CONTENT_TAG_SHIFT) | + PGP_PTAG_OLD_LEN_2; + assert_int_equal(expected_tag, 0x99); + assert_int_equal(keyring[0], expected_tag); + // original file can be loaded correctly + assert_true(import_public_keys_from_vector(keyring)); + { + // Shrink the packet length to 1 octet + std::vector<uint8_t> keyring_valid_1 = shrink_len_2_to_1(keyring); + assert_int_equal(keyring_valid_1.size(), keyring.size() - 1); + assert_true(import_public_keys_from_vector(keyring_valid_1)); + } + { + // get invalid key with length 0 + std::vector<uint8_t> keyring_invalid_0 = fake_len_0(keyring); + assert_false(import_public_keys_from_vector(keyring_invalid_0)); + } + { + // get invalid key with length 0xEEEE + std::vector<uint8_t> keyring_invalid_EEEE = fake_len_EEEE(keyring); + assert_int_equal(keyring_invalid_EEEE.size(), keyring.size()); + assert_false(import_public_keys_from_vector(keyring_invalid_EEEE)); + } + { + std::vector<uint8_t> keyring_len_4 = extend_len_2_to_4(keyring); + assert_int_equal(keyring_len_4.size(), keyring.size() + 2); + assert_true(import_public_keys_from_vector(keyring_len_4)); + // get invalid key with length 0xEEEEEEEE + keyring_len_4[1] = 0xEE; + keyring_len_4[2] = 0xEE; + keyring_len_4[3] = 0xEE; + keyring_len_4[4] = 0xEE; + assert_false(import_public_keys_from_vector(keyring_len_4)); + } +} + +TEST_F(rnp_tests, test_ffi_calculate_iterations) +{ + size_t iterations = 0; + assert_rnp_failure(rnp_calculate_iterations(NULL, 500, &iterations)); + assert_rnp_failure(rnp_calculate_iterations("SHA256", 500, NULL)); + assert_rnp_failure(rnp_calculate_iterations("WRONG", 500, &iterations)); + assert_rnp_success(rnp_calculate_iterations("SHA256", 500, &iterations)); + assert_true(iterations > 65536); +} + +static bool +check_features(const char *type, const char *json, size_t count) +{ + size_t got_count = 0; + + json_object *features = json_tokener_parse(json); + if (!features) { + return false; + } + bool res = false; + if (!json_object_is_type(features, json_type_array)) { + goto done; + } + got_count = json_object_array_length(features); + if (got_count != count) { + RNP_LOG("wrong feature count for %s: expected %zu, got %zu", type, count, got_count); + goto done; + } + for (size_t i = 0; i < count; i++) { + json_object *val = json_object_array_get_idx(features, i); + const char * str = json_object_get_string(val); + bool supported = false; + if (!str || rnp_supports_feature(type, str, &supported) || !supported) { + goto done; + } + } + + res = true; +done: + json_object_put(features); + return res; +} + +TEST_F(rnp_tests, test_ffi_supported_features) +{ + char *features = NULL; + /* some edge cases */ + assert_rnp_failure(rnp_supported_features(NULL, &features)); + assert_rnp_failure(rnp_supported_features("something", NULL)); + assert_rnp_failure(rnp_supported_features(RNP_FEATURE_SYMM_ALG, NULL)); + assert_rnp_failure(rnp_supported_features("something", &features)); + /* symmetric algorithms */ + assert_rnp_success(rnp_supported_features("Symmetric Algorithm", &features)); + assert_non_null(features); + bool has_sm2 = sm2_enabled(); + bool has_tf = twofish_enabled(); + bool has_brainpool = brainpool_enabled(); + bool has_idea = idea_enabled(); + assert_true( + check_features(RNP_FEATURE_SYMM_ALG, + features, + 7 + has_sm2 + has_tf + has_idea + blowfish_enabled() + cast5_enabled())); + rnp_buffer_destroy(features); + bool supported = false; + assert_rnp_failure(rnp_supports_feature(NULL, "IDEA", &supported)); + assert_rnp_failure(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, NULL, &supported)); + assert_rnp_failure(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "IDEA", NULL)); + assert_rnp_failure(rnp_supports_feature("WRONG", "IDEA", &supported)); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "IDEA", &supported)); + assert_true(supported == has_idea); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "TRIPLEDES", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "CAST5", &supported)); + assert_int_equal(supported, cast5_enabled()); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "BLOWFISH", &supported)); + assert_int_equal(supported, blowfish_enabled()); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "AES128", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "AES192", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "AES256", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "TWOFISH", &supported)); + assert_true(supported == has_tf); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "CAMELLIA128", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "CAMELLIA192", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "CAMELLIA256", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "SM4", &supported)); + assert_true(supported == has_sm2); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "idea", &supported)); + assert_true(supported == has_idea); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "tripledes", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "cast5", &supported)); + assert_true(supported == cast5_enabled()); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "blowfish", &supported)); + assert_true(supported == blowfish_enabled()); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "aes128", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "aes192", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "aes256", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "twofish", &supported)); + assert_true(supported == has_tf); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "camellia128", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "camellia192", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "camellia256", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "sm4", &supported)); + assert_true(supported == has_sm2); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_SYMM_ALG, "wrong", &supported)); + assert_false(supported); + /* aead algorithms */ + bool has_eax = aead_eax_enabled(); + bool has_ocb = aead_ocb_enabled(); + assert_rnp_success(rnp_supported_features(RNP_FEATURE_AEAD_ALG, &features)); + assert_non_null(features); + assert_true(check_features(RNP_FEATURE_AEAD_ALG, features, 1 + has_eax + has_ocb)); + rnp_buffer_destroy(features); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_AEAD_ALG, "eax", &supported)); + assert_true(supported == has_eax); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_AEAD_ALG, "ocb", &supported)); + assert_true(supported == has_ocb); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_AEAD_ALG, "none", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_AEAD_ALG, "wrong", &supported)); + assert_false(supported); + /* protection mode */ + assert_rnp_success(rnp_supported_features(RNP_FEATURE_PROT_MODE, &features)); + assert_non_null(features); + assert_true(check_features(RNP_FEATURE_PROT_MODE, features, 1)); + rnp_buffer_destroy(features); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_PROT_MODE, "cfb", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_PROT_MODE, "wrong", &supported)); + assert_false(supported); + /* public key algorithm */ + assert_rnp_success(rnp_supported_features(RNP_FEATURE_PK_ALG, &features)); + assert_non_null(features); + assert_true(check_features(RNP_FEATURE_PK_ALG, features, 6 + has_sm2)); + rnp_buffer_destroy(features); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_PK_ALG, "RSA", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_PK_ALG, "DSA", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_PK_ALG, "ELGAMAL", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_PK_ALG, "ECDSA", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_PK_ALG, "ECDH", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_PK_ALG, "EDDSA", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_PK_ALG, "SM2", &supported)); + assert_true(supported == has_sm2); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_PK_ALG, "rsa", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_PK_ALG, "dsa", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_PK_ALG, "elgamal", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_PK_ALG, "ecdsa", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_PK_ALG, "ecdh", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_PK_ALG, "eddsa", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_PK_ALG, "sm2", &supported)); + assert_true(supported == has_sm2); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_PK_ALG, "wrong", &supported)); + assert_false(supported); + /* hash algorithm */ + assert_rnp_success(rnp_supported_features(RNP_FEATURE_HASH_ALG, &features)); + assert_non_null(features); + assert_true( + check_features(RNP_FEATURE_HASH_ALG, features, 8 + has_sm2 + ripemd160_enabled())); + rnp_buffer_destroy(features); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "MD5", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "SHA1", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "RIPEMD160", &supported)); + assert_true(supported == ripemd160_enabled()); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "SHA256", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "SHA384", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "SHA512", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "SHA224", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "SHA3-256", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "SHA3-512", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "SM3", &supported)); + assert_true(supported == has_sm2); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "md5", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "sha1", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "ripemd160", &supported)); + assert_true(supported == ripemd160_enabled()); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "sha256", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "sha384", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "sha512", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "sha224", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "sha3-256", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "sha3-512", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "sm3", &supported)); + assert_true(supported == has_sm2); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "wrong", &supported)); + assert_false(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_HASH_ALG, "CRC24", &supported)); + assert_false(supported); + /* compression algorithm */ + assert_rnp_success(rnp_supported_features(RNP_FEATURE_COMP_ALG, &features)); + assert_non_null(features); + assert_true(check_features(RNP_FEATURE_COMP_ALG, features, 4)); + rnp_buffer_destroy(features); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_COMP_ALG, "Uncompressed", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_COMP_ALG, "Zlib", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_COMP_ALG, "ZIP", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_COMP_ALG, "BZIP2", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_COMP_ALG, "wrong", &supported)); + assert_false(supported); + /* elliptic curve */ + assert_rnp_success(rnp_supported_features(RNP_FEATURE_CURVE, &features)); + assert_non_null(features); + assert_true(check_features(RNP_FEATURE_CURVE, features, 6 + has_sm2 + 3 * has_brainpool)); + rnp_buffer_destroy(features); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_CURVE, "NIST P-256", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_CURVE, "NIST P-384", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_CURVE, "NIST P-521", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_CURVE, "ed25519", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_CURVE, "curve25519", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_CURVE, "brainpoolP256r1", &supported)); + assert_true(supported == has_brainpool); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_CURVE, "brainpoolP384r1", &supported)); + assert_true(supported == has_brainpool); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_CURVE, "brainpoolP512r1", &supported)); + assert_true(supported == has_brainpool); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_CURVE, "secp256k1", &supported)); + assert_true(supported); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_CURVE, "SM2 P-256", &supported)); + assert_true(supported == has_sm2); + assert_rnp_success(rnp_supports_feature(RNP_FEATURE_CURVE, "wrong", &supported)); + assert_false(supported); +} + +TEST_F(rnp_tests, test_ffi_output_to_armor) +{ + rnp_ffi_t ffi = NULL; + rnp_output_t memory = NULL; + rnp_output_t armor = NULL; + rnp_input_t input = NULL; + + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_true(load_keys_gpg(ffi, "data/keyrings/1/pubring.gpg")); + + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "2FCADF05FFA501BB", &key)); + assert_non_null(key); + + assert_rnp_success(rnp_output_to_memory(&memory, 0)); + /* some edge cases */ + assert_rnp_failure(rnp_output_to_armor(NULL, &armor, "message")); + assert_null(armor); + assert_rnp_failure(rnp_output_to_armor(memory, NULL, "message")); + assert_null(armor); + assert_rnp_failure(rnp_output_to_armor(memory, &armor, "wrong")); + assert_null(armor); + /* export raw key to armored stream with 'message' header */ + assert_rnp_success(rnp_output_to_armor(memory, &armor, "message")); + assert_rnp_success(rnp_key_export(key, armor, RNP_KEY_EXPORT_PUBLIC)); + assert_rnp_success(rnp_output_destroy(armor)); + uint8_t *buf = NULL; + size_t buf_len = 0; + /* check contents to make sure it is correct armored stream */ + assert_rnp_success(rnp_output_memory_get_buf(memory, &buf, &buf_len, false)); + assert_non_null(buf); + const char *hdr = "-----BEGIN PGP MESSAGE-----"; + assert_true(buf_len > strlen(hdr)); + assert_int_equal(strncmp((char *) buf, hdr, strlen(hdr)), 0); + assert_rnp_success(rnp_input_from_memory(&input, buf, buf_len, false)); + rnp_output_t memory2 = NULL; + assert_rnp_success(rnp_output_to_memory(&memory2, 0)); + assert_rnp_success(rnp_dearmor(input, memory2)); + rnp_output_destroy(memory2); + rnp_input_destroy(input); + + rnp_key_handle_destroy(key); + rnp_output_destroy(memory); + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_rnp_guess_contents) +{ + char * msgt = NULL; + rnp_input_t input = NULL; + assert_rnp_failure(rnp_guess_contents(NULL, &msgt)); + + assert_rnp_success( + rnp_input_from_path(&input, "data/issue1188/armored_revocation_signature.pgp")); + assert_rnp_success(rnp_guess_contents(input, &msgt)); + assert_int_equal(strcmp(msgt, "signature"), 0); + rnp_buffer_destroy(msgt); + rnp_input_destroy(input); + + assert_rnp_success(rnp_input_from_path(&input, "data/test_stream_key_merge/key-pub.pgp")); + assert_rnp_failure(rnp_guess_contents(input, NULL)); + assert_rnp_success(rnp_guess_contents(input, &msgt)); + assert_string_equal(msgt, "public key"); + rnp_buffer_destroy(msgt); + rnp_input_destroy(input); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_key_merge/key-pub-just-subkey-1.pgp")); + assert_rnp_success(rnp_guess_contents(input, &msgt)); + assert_string_equal(msgt, "public key"); + rnp_buffer_destroy(msgt); + rnp_input_destroy(input); + + assert_rnp_success(rnp_input_from_path(&input, "data/test_stream_key_merge/key-pub.asc")); + assert_rnp_success(rnp_guess_contents(input, &msgt)); + assert_string_equal(msgt, "public key"); + rnp_buffer_destroy(msgt); + rnp_input_destroy(input); + + assert_rnp_success(rnp_input_from_path(&input, "data/test_stream_key_merge/key-sec.pgp")); + assert_rnp_success(rnp_guess_contents(input, &msgt)); + assert_string_equal(msgt, "secret key"); + rnp_buffer_destroy(msgt); + rnp_input_destroy(input); + + assert_rnp_success(rnp_input_from_path(&input, "data/test_stream_key_merge/key-sec.asc")); + assert_rnp_success(rnp_guess_contents(input, &msgt)); + assert_string_equal(msgt, "secret key"); + rnp_buffer_destroy(msgt); + rnp_input_destroy(input); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_key_merge/key-sec-just-subkey-1.pgp")); + assert_rnp_success(rnp_guess_contents(input, &msgt)); + assert_string_equal(msgt, "secret key"); + rnp_buffer_destroy(msgt); + rnp_input_destroy(input); + + assert_rnp_success(rnp_input_from_path(&input, "data/test_stream_z/128mb.zip")); + assert_rnp_success(rnp_guess_contents(input, &msgt)); + assert_string_equal(msgt, "message"); + rnp_buffer_destroy(msgt); + rnp_input_destroy(input); + + assert_rnp_success(rnp_input_from_path(&input, "data/test_stream_z/4gb.bzip2.asc")); + assert_rnp_success(rnp_guess_contents(input, &msgt)); + assert_string_equal(msgt, "message"); + rnp_buffer_destroy(msgt); + rnp_input_destroy(input); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_signatures/source.txt.sig")); + assert_rnp_success(rnp_guess_contents(input, &msgt)); + assert_string_equal(msgt, "signature"); + rnp_buffer_destroy(msgt); + rnp_input_destroy(input); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_signatures/source.txt.sig.asc")); + assert_rnp_success(rnp_guess_contents(input, &msgt)); + assert_string_equal(msgt, "signature"); + rnp_buffer_destroy(msgt); + rnp_input_destroy(input); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_signatures/source.txt.asc.asc")); + assert_rnp_success(rnp_guess_contents(input, &msgt)); + assert_string_equal(msgt, "cleartext"); + rnp_buffer_destroy(msgt); + rnp_input_destroy(input); + + assert_rnp_success(rnp_input_from_path(&input, "data/test_stream_signatures/source.txt")); + assert_rnp_success(rnp_guess_contents(input, &msgt)); + assert_string_equal(msgt, "unknown"); + rnp_buffer_destroy(msgt); + rnp_input_destroy(input); + + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message.txt.marker")); + assert_rnp_success(rnp_guess_contents(input, &msgt)); + assert_string_equal(msgt, "message"); + rnp_buffer_destroy(msgt); + rnp_input_destroy(input); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.wrong-armor.asc")); + assert_rnp_success(rnp_guess_contents(input, &msgt)); + assert_string_equal(msgt, "unknown"); + rnp_buffer_destroy(msgt); + rnp_input_destroy(input); +} + +TEST_F(rnp_tests, test_ffi_literal_filename) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_op_sign_t op = NULL; + uint8_t * signed_buf; + size_t signed_len; + + // init ffi + test_ffi_init(&ffi); + // init input + test_ffi_init_sign_memory_input(&input, &output); + // create signature operation + assert_rnp_success(rnp_op_sign_create(&op, ffi, input, output)); + // setup signature(s) + test_ffi_setup_signatures(&ffi, &op); + // setup filename and modification time + assert_rnp_success(rnp_op_sign_set_file_name(op, "checkleak.dat")); + assert_rnp_success(rnp_op_sign_set_file_name(op, NULL)); + assert_rnp_success(rnp_op_sign_set_file_name(op, "testfile.dat")); + assert_rnp_success(rnp_op_sign_set_file_mtime(op, 12345678)); + // execute the operation + assert_rnp_success(rnp_op_sign_execute(op)); + // make sure the output file was created + assert_rnp_success(rnp_output_memory_get_buf(output, &signed_buf, &signed_len, true)); + assert_non_null(signed_buf); + assert_true(signed_len > 0); + + // cleanup + assert_rnp_success(rnp_input_destroy(input)); + input = NULL; + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_rnp_success(rnp_op_sign_destroy(op)); + op = NULL; + + // check the resulting stream for correct name/time + assert_rnp_success(rnp_input_from_memory(&input, signed_buf, signed_len, false)); + char *json = NULL; + assert_rnp_success(rnp_dump_packets_to_json(input, 0, &json)); + assert_non_null(json); + + std::string jstr = json; + assert_true(jstr.find("\"filename\":\"testfile.dat\"") != std::string::npos); + assert_true(jstr.find("\"timestamp\":12345678") != std::string::npos); + + assert_rnp_success(rnp_input_destroy(input)); + rnp_buffer_destroy(signed_buf); + rnp_buffer_destroy(json); + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_op_set_hash) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_op_sign_t op = NULL; + uint8_t * signed_buf; + size_t signed_len; + + // init ffi + test_ffi_init(&ffi); + // init input + test_ffi_init_sign_memory_input(&input, &output); + // create signature operation + assert_rnp_success(rnp_op_sign_create(&op, ffi, input, output)); + // setup signature(s) + test_ffi_setup_signatures(&ffi, &op); + // make sure it doesn't fail on NULL hash value + assert_rnp_failure(rnp_op_sign_set_hash(op, NULL)); + assert_rnp_failure(rnp_op_sign_set_hash(op, "Unknown")); + assert_rnp_success(rnp_op_sign_set_hash(op, "SHA256")); + // execute the operation with wrong password + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "wrong")); + assert_int_equal(rnp_op_sign_execute(op), RNP_ERROR_BAD_PASSWORD); + assert_rnp_success(rnp_op_sign_destroy(op)); + // execute the operation with valid password + assert_rnp_success(rnp_op_sign_create(&op, ffi, input, output)); + // setup signature(s) + test_ffi_setup_signatures(&ffi, &op); + assert_rnp_success(rnp_op_sign_execute(op)); + // make sure the output file was created + assert_rnp_success(rnp_output_memory_get_buf(output, &signed_buf, &signed_len, true)); + assert_non_null(signed_buf); + assert_true(signed_len > 0); + + // cleanup + assert_rnp_success(rnp_input_destroy(input)); + assert_rnp_success(rnp_output_destroy(output)); + assert_rnp_success(rnp_op_sign_destroy(op)); + + rnp_buffer_destroy(signed_buf); + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_op_set_compression) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_op_sign_t op = NULL; + uint8_t * signed_buf; + size_t signed_len; + + // init ffi + test_ffi_init(&ffi); + // init input + test_ffi_init_sign_memory_input(&input, &output); + // create signature operation + assert_rnp_success(rnp_op_sign_create(&op, ffi, input, output)); + // setup signature(s) + test_ffi_setup_signatures(&ffi, &op); + // make sure it doesn't fail on NULL compression algorithm value + assert_rnp_failure(rnp_op_sign_set_compression(op, NULL, 6)); + assert_rnp_failure(rnp_op_sign_set_compression(op, "Unknown", 6)); + assert_rnp_failure(rnp_op_sign_set_compression(NULL, "ZLib", 6)); + assert_rnp_success(rnp_op_sign_set_compression(op, "ZLib", 6)); + // execute the operation + assert_rnp_success(rnp_op_sign_execute(op)); + // make sure the output file was created + assert_rnp_success(rnp_output_memory_get_buf(output, &signed_buf, &signed_len, true)); + assert_non_null(signed_buf); + assert_true(signed_len > 0); + + // cleanup + assert_rnp_success(rnp_input_destroy(input)); + assert_rnp_success(rnp_output_destroy(output)); + assert_rnp_success(rnp_op_sign_destroy(op)); + + rnp_buffer_destroy(signed_buf); + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_aead_params) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_op_encrypt_t op = NULL; + const char *plaintext = "Some data to encrypt using the AEAD-EAX and AEAD-OCB encryption."; + + // setup FFI + test_ffi_init(&ffi); + + // write out some data + str_to_file("plaintext", plaintext); + // create input+output + assert_rnp_success(rnp_input_from_path(&input, "plaintext")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "encrypted")); + assert_non_null(output); + // create encrypt operation + assert_rnp_success(rnp_op_encrypt_create(&op, ffi, input, output)); + // setup AEAD params + assert_rnp_failure(rnp_op_encrypt_set_aead(NULL, "OCB")); + assert_rnp_failure(rnp_op_encrypt_set_aead(op, NULL)); + assert_rnp_failure(rnp_op_encrypt_set_aead(op, "WRONG")); + if (!aead_ocb_enabled()) { + assert_rnp_failure(rnp_op_encrypt_set_aead(op, "OCB")); + } else { + assert_rnp_success(rnp_op_encrypt_set_aead(op, "OCB")); + } + assert_rnp_failure(rnp_op_encrypt_set_aead_bits(NULL, 10)); + assert_rnp_failure(rnp_op_encrypt_set_aead_bits(op, -1)); + assert_rnp_failure(rnp_op_encrypt_set_aead_bits(op, 60)); + assert_rnp_failure(rnp_op_encrypt_set_aead_bits(op, 17)); + assert_rnp_success(rnp_op_encrypt_set_aead_bits(op, 10)); + // add password (using all defaults) + assert_rnp_success(rnp_op_encrypt_add_password(op, "pass1", NULL, 0, NULL)); + // setup compression + assert_rnp_failure(rnp_op_encrypt_set_compression(NULL, "ZLIB", 6)); + assert_rnp_failure(rnp_op_encrypt_set_compression(op, NULL, 6)); + assert_rnp_failure(rnp_op_encrypt_set_compression(op, "WRONG", 6)); + assert_rnp_success(rnp_op_encrypt_set_compression(op, "ZLIB", 6)); + // set filename and mtime + assert_rnp_failure(rnp_op_encrypt_set_file_name(NULL, "filename")); + assert_rnp_success(rnp_op_encrypt_set_file_name(op, NULL)); + assert_rnp_success(rnp_op_encrypt_set_file_name(op, "filename")); + assert_rnp_failure(rnp_op_encrypt_set_file_mtime(NULL, 1000)); + assert_rnp_success(rnp_op_encrypt_set_file_mtime(op, 1000)); + // execute the operation + assert_rnp_success(rnp_op_encrypt_execute(op)); + // make sure the output file was created + assert_true(rnp_file_exists("encrypted")); + // cleanup + assert_rnp_success(rnp_input_destroy(input)); + input = NULL; + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_rnp_success(rnp_op_encrypt_destroy(op)); + op = NULL; + + // list packets + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_non_null(input); + char *json = NULL; + assert_rnp_success(rnp_dump_packets_to_json(input, 0, &json)); + assert_rnp_success(rnp_input_destroy(input)); + input = NULL; + json_object *jso = json_tokener_parse(json); + rnp_buffer_destroy(json); + assert_non_null(jso); + assert_true(json_object_is_type(jso, json_type_array)); + /* check the symmetric-key encrypted session key packet */ + json_object *pkt = json_object_array_get_idx(jso, 0); + assert_true(check_json_pkt_type(pkt, PGP_PKT_SK_SESSION_KEY)); + if (!aead_ocb_enabled()) { + // if AEAD is not enabled then v4 encrypted packet will be created + assert_true(check_json_field_int(pkt, "version", 4)); + assert_true(check_json_field_str(pkt, "algorithm.str", "AES-256")); + } else { + assert_true(check_json_field_int(pkt, "version", 5)); + assert_true(check_json_field_str(pkt, "aead algorithm.str", "OCB")); + } + /* check the aead-encrypted packet */ + pkt = json_object_array_get_idx(jso, 1); + if (!aead_ocb_enabled()) { + assert_true(check_json_pkt_type(pkt, PGP_PKT_SE_IP_DATA)); + } else { + assert_true(check_json_pkt_type(pkt, PGP_PKT_AEAD_ENCRYPTED)); + assert_true(check_json_field_int(pkt, "version", 1)); + assert_true(check_json_field_str(pkt, "aead algorithm.str", "OCB")); + assert_true(check_json_field_int(pkt, "chunk size", 10)); + } + json_object_put(jso); + + /* decrypt */ + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_non_null(output); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "pass1")); + assert_rnp_success(rnp_decrypt(ffi, input, output)); + // cleanup + rnp_input_destroy(input); + input = NULL; + rnp_output_destroy(output); + output = NULL; + // compare the decrypted file + assert_true(file_equals("decrypted", plaintext, strlen(plaintext))); + rnp_unlink("decrypted"); + + // final cleanup + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_detached_verify_input) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + + // init ffi + test_ffi_init(&ffi); + /* verify detached signature via rnp_op_verify_create - should not crash */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_signatures/source.txt.sig")); + assert_rnp_success(rnp_output_to_null(&output)); + rnp_op_verify_t verify = NULL; + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_detached_cleartext_signed_input) +{ + rnp_ffi_t ffi = NULL; + test_ffi_init(&ffi); + /* verify detached signature with cleartext input - must fail */ + rnp_input_t inputmsg = NULL; + assert_rnp_success(rnp_input_from_path(&inputmsg, "data/test_messages/message.txt")); + rnp_input_t inputsig = NULL; + assert_rnp_success( + rnp_input_from_path(&inputsig, "data/test_messages/message.txt.cleartext-signed")); + rnp_op_verify_t verify = NULL; + assert_rnp_success(rnp_op_verify_detached_create(&verify, ffi, inputmsg, inputsig)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(inputmsg); + rnp_input_destroy(inputsig); + /* verify detached signature with signed/embedded input - must fail */ + assert_rnp_success(rnp_input_from_path(&inputmsg, "data/test_messages/message.txt")); + assert_rnp_success( + rnp_input_from_path(&inputsig, "data/test_messages/message.txt.empty.sig")); + assert_rnp_success(rnp_op_verify_detached_create(&verify, ffi, inputmsg, inputsig)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(inputmsg); + rnp_input_destroy(inputsig); + /* verify detached signature as a whole message - must fail */ + assert_rnp_success(rnp_input_from_path(&inputmsg, "data/test_messages/message.txt.sig")); + rnp_output_t output = NULL; + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, inputmsg, output)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + rnp_op_verify_destroy(verify); + rnp_output_destroy(output); + rnp_input_destroy(inputmsg); + + rnp_ffi_destroy(ffi); +} + +static bool +check_signature(rnp_op_verify_t op, size_t idx, rnp_result_t status) +{ + rnp_op_verify_signature_t sig = NULL; + if (rnp_op_verify_get_signature_at(op, idx, &sig)) { + return false; + } + return rnp_op_verify_signature_get_status(sig) == status; +} + +TEST_F(rnp_tests, test_ffi_op_verify_sig_count) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + + // init ffi + test_ffi_init(&ffi); + + /* signed message */ + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message.txt.signed")); + assert_rnp_success(rnp_output_to_null(&output)); + rnp_op_verify_t verify = NULL; + assert_rnp_failure(rnp_op_verify_create(NULL, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_create(&verify, NULL, input, output)); + assert_rnp_failure(rnp_op_verify_create(&verify, ffi, NULL, output)); + assert_rnp_failure(rnp_op_verify_create(&verify, ffi, input, NULL)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(NULL)); + assert_rnp_success(rnp_op_verify_execute(verify)); + size_t sigcount = 0; + assert_rnp_failure(rnp_op_verify_get_signature_count(verify, NULL)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_SUCCESS)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* signed with unknown key */ + sigcount = 255; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.signed.unknown")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_int_equal(rnp_op_verify_execute(verify), RNP_ERROR_SIGNATURE_INVALID); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_ERROR_KEY_NOT_FOUND)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* signed with malformed signature (bad version) */ + sigcount = 255; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.signed.malfsig")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_ERROR_SIGNATURE_UNKNOWN)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* signed with invalid signature (modified hash alg) */ + sigcount = 255; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.signed.invsig")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_int_equal(rnp_op_verify_execute(verify), RNP_ERROR_SIGNATURE_INVALID); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_ERROR_SIGNATURE_INVALID)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* signed without the signature */ + sigcount = 255; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.signed.nosig")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 0); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* detached signature */ + rnp_input_t source = NULL; + sigcount = 255; + assert_rnp_success(rnp_input_from_path(&source, "data/test_messages/message.txt")); + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message.txt.sig")); + assert_rnp_failure(rnp_op_verify_detached_create(NULL, ffi, source, input)); + assert_rnp_failure(rnp_op_verify_detached_create(&verify, NULL, source, input)); + assert_rnp_failure(rnp_op_verify_detached_create(&verify, ffi, NULL, input)); + assert_rnp_failure(rnp_op_verify_detached_create(&verify, ffi, source, NULL)); + assert_rnp_success(rnp_op_verify_detached_create(&verify, ffi, source, input)); + assert_rnp_success(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_SUCCESS)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(source); + rnp_input_destroy(input); + + /* detached text-mode signature */ + source = NULL; + sigcount = 255; + assert_rnp_success(rnp_input_from_path(&source, "data/test_messages/message.txt")); + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message.txt.sig-text")); + assert_rnp_success(rnp_op_verify_detached_create(&verify, ffi, source, input)); + assert_rnp_success(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_SUCCESS)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(source); + rnp_input_destroy(input); + + source = NULL; + sigcount = 255; + assert_rnp_success(rnp_input_from_path(&source, "data/test_messages/message.txt.crlf")); + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message.txt.sig-text")); + assert_rnp_success(rnp_op_verify_detached_create(&verify, ffi, source, input)); + assert_rnp_success(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_SUCCESS)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(source); + rnp_input_destroy(input); + + /* detached text-mode signature with trailing CR characters */ + source = NULL; + sigcount = 255; + assert_rnp_success( + rnp_input_from_path(&source, "data/test_messages/message-trailing-cr.txt")); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message-trailing-cr.txt.sig-text")); + assert_rnp_success(rnp_op_verify_detached_create(&verify, ffi, source, input)); + assert_rnp_success(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_SUCCESS)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(source); + rnp_input_destroy(input); + + /* detached text-mode signature with CRLF on 32k boundary */ + source = NULL; + sigcount = 255; + assert_rnp_success( + rnp_input_from_path(&source, "data/test_messages/message-32k-crlf.txt")); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message-32k-crlf.txt.sig")); + assert_rnp_success(rnp_op_verify_detached_create(&verify, ffi, source, input)); + assert_rnp_success(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_SUCCESS)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(source); + rnp_input_destroy(input); + + /* embedded text-mode signature with CRLF on 32k boundary */ + sigcount = 255; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message-32k-crlf.txt.gpg")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_SUCCESS)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* malformed detached signature */ + sigcount = 255; + assert_rnp_success(rnp_input_from_path(&source, "data/test_messages/message.txt")); + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message.txt.sig.malf")); + assert_rnp_success(rnp_op_verify_detached_create(&verify, ffi, source, input)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 0); + rnp_op_verify_destroy(verify); + rnp_input_destroy(source); + rnp_input_destroy(input); + + /* malformed detached signature, wrong bitlen in MPI */ + sigcount = 255; + assert_rnp_success(rnp_input_from_path(&source, "data/test_messages/message.txt")); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.sig.wrong-mpi-bitlen")); + assert_rnp_success(rnp_op_verify_detached_create(&verify, ffi, source, input)); + assert_rnp_success(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + rnp_op_verify_destroy(verify); + rnp_input_destroy(source); + rnp_input_destroy(input); + + /* encrypted message */ + sigcount = 255; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.encrypted")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + if (!aead_eax_enabled()) { + assert_rnp_failure(rnp_op_verify_execute(verify)); + } else { + assert_rnp_success(rnp_op_verify_execute(verify)); + } + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 0); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* encrypted and signed message */ + sigcount = 255; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.signed-encrypted")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_SUCCESS)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* cleartext signed message */ + sigcount = 255; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.cleartext-signed")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_SUCCESS)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* cleartext signed message without newline */ + sigcount = 255; + assert_rnp_success(rnp_input_from_path( + &input, "data/test_messages/message.txt.cleartext-signed-nonewline")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 0); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* cleartext signed with malformed signature (wrong mpi len) */ + sigcount = 255; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.cleartext-malf")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_int_equal(rnp_op_verify_execute(verify), RNP_ERROR_SIGNATURE_INVALID); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_ERROR_SIGNATURE_UNKNOWN)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* cleartext signed without the signature */ + sigcount = 255; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.cleartext-nosig")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 0); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* signed message without compression */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.signed-no-z")); + assert_rnp_success(rnp_output_to_null(&output)); + verify = NULL; + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + sigcount = 255; + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_SUCCESS)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* signed and password-encrypted data with 0 compression algo */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.signed-sym-none-z")); + assert_rnp_success(rnp_output_to_null(&output)); + verify = NULL; + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + sigcount = 255; + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_SUCCESS)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* signed message with one-pass with wrong version */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.signed-no-z-malf")); + assert_rnp_success(rnp_output_to_null(&output)); + verify = NULL; + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* encrypted and signed message with marker packet */ + sigcount = 255; + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message.txt.marker")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_SUCCESS)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* encrypted and signed message with marker packet, armored */ + sigcount = 255; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.marker.asc")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_SUCCESS)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* encrypted and signed message with malformed marker packet */ + sigcount = 255; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.marker.malf")); + assert_rnp_success(rnp_output_to_null(&output)); + verify = NULL; + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* sha1 detached signature over the collision-suspicious data */ + /* allow sha1 temporary */ + rnp::SecurityRule allow_sha1( + rnp::FeatureType::Hash, PGP_HASH_SHA1, rnp::SecurityLevel::Default, 1547856001); + global_ctx.profile.add_rule(allow_sha1); + sigcount = 255; + assert_rnp_success(rnp_input_from_path(&source, "data/test_messages/shattered-1.pdf")); + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/shattered-1.pdf.sig")); + assert_rnp_success(rnp_op_verify_detached_create(&verify, ffi, source, input)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_ERROR_SIGNATURE_INVALID)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(source); + rnp_input_destroy(input); + + /* sha1 detached signature over the document with collision*/ + sigcount = 255; + assert_rnp_success(rnp_input_from_path(&source, "data/test_messages/shattered-2.pdf")); + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/shattered-1.pdf.sig")); + assert_rnp_success(rnp_op_verify_detached_create(&verify, ffi, source, input)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_ERROR_SIGNATURE_INVALID)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(source); + rnp_input_destroy(input); + + /* sha1 attached signature over the collision-suspicious data */ + sigcount = 255; + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/shattered-2.pdf.gpg")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_ERROR_SIGNATURE_INVALID)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* remove sha1 rule */ + assert_true(global_ctx.profile.del_rule(allow_sha1)); + + /* signed message with key which is now expired */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET)); + import_pub_keys(ffi, "data/test_messages/expired_signing_key-pub.asc"); + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "30FC0D776915BA44", &key)); + uint64_t till = 0; + assert_rnp_success(rnp_key_valid_till64(key, &till)); + assert_int_equal(till, 1623424417); + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.signed-expired-key")); + assert_rnp_success(rnp_output_to_null(&output)); + verify = NULL; + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + sigcount = 255; + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_SUCCESS)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* signed message with subkey which is now expired */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET)); + import_pub_keys(ffi, "data/test_messages/expired_signing_sub-pub.asc"); + key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "D93A47FD93191FD1", &key)); + till = 0; + assert_rnp_success(rnp_key_valid_till64(key, &till)); + assert_int_equal(till, 1623933507); + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.signed-expired-sub")); + assert_rnp_success(rnp_output_to_null(&output)); + verify = NULL; + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + sigcount = 255; + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_SUCCESS)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* signed message with md5 hash */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET)); + assert_true(load_keys_gpg(ffi, "data/keyrings/1/pubring.gpg")); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.signed.md5")); + assert_rnp_success(rnp_output_to_null(&output)); + verify = NULL; + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + sigcount = 255; + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_ERROR_SIGNATURE_INVALID)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* signed message with md5 hash before the cut-off date */ + assert_true(import_all_keys(ffi, "data/test_key_edge_cases/key-rsa-2001-pub.asc")); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.signed-md5-before")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + sigcount = 255; + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_SUCCESS)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* signed message with md5 hash right after the cut-off date */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.signed-md5-after")); + assert_rnp_success(rnp_output_to_null(&output)); + verify = NULL; + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + sigcount = 255; + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_ERROR_SIGNATURE_INVALID)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* signed message with sha1 hash */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.signed.sha1")); + assert_rnp_success(rnp_output_to_null(&output)); + verify = NULL; + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + sigcount = 255; + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_ERROR_SIGNATURE_INVALID)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* message signed with sha1 before the cut-off date */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.signed-sha1-before")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + sigcount = 255; + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_SUCCESS)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* message signed with sha1 right after the cut-off date */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.signed-sha1-after")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + sigcount = 255; + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount)); + assert_int_equal(sigcount, 1); + assert_true(check_signature(verify, 0, RNP_ERROR_SIGNATURE_INVALID)); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_op_verify_get_protection_info) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + + // init ffi + test_ffi_init(&ffi); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + + /* message just signed */ + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message.txt.signed")); + assert_rnp_success(rnp_output_to_null(&output)); + rnp_op_verify_t verify = NULL; + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + char *mode = NULL; + char *cipher = NULL; + bool valid = true; + assert_rnp_failure(rnp_op_verify_get_protection_info(NULL, &mode, &cipher, &valid)); + assert_rnp_success(rnp_op_verify_get_protection_info(verify, &mode, NULL, NULL)); + assert_string_equal(mode, "none"); + rnp_buffer_destroy(mode); + assert_rnp_success(rnp_op_verify_get_protection_info(verify, NULL, &cipher, NULL)); + assert_string_equal(cipher, "none"); + rnp_buffer_destroy(cipher); + valid = true; + assert_rnp_success(rnp_op_verify_get_protection_info(verify, NULL, NULL, &valid)); + assert_false(valid); + assert_rnp_failure(rnp_op_verify_get_protection_info(verify, NULL, NULL, NULL)); + assert_rnp_success(rnp_op_verify_get_protection_info(verify, &mode, &cipher, &valid)); + assert_string_equal(mode, "none"); + assert_string_equal(cipher, "none"); + assert_false(valid); + rnp_buffer_destroy(mode); + rnp_buffer_destroy(cipher); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* message without MDC */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-no-mdc")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + mode = NULL; + cipher = NULL; + valid = true; + assert_rnp_success(rnp_op_verify_get_protection_info(verify, &mode, &cipher, &valid)); + assert_string_equal(mode, "cfb"); + assert_string_equal(cipher, "AES256"); + assert_false(valid); + rnp_buffer_destroy(mode); + rnp_buffer_destroy(cipher); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* message with MDC */ + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message.txt.enc-mdc")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + mode = NULL; + cipher = NULL; + valid = false; + assert_rnp_success(rnp_op_verify_get_protection_info(verify, &mode, &cipher, &valid)); + assert_string_equal(mode, "cfb-mdc"); + assert_string_equal(cipher, "AES256"); + assert_true(valid); + rnp_buffer_destroy(mode); + rnp_buffer_destroy(cipher); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* message with AEAD-OCB */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-aead-ocb")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + if (!aead_ocb_enabled() || aead_ocb_aes_only()) { + assert_rnp_failure(rnp_op_verify_execute(verify)); + } else { + assert_rnp_success(rnp_op_verify_execute(verify)); + } + mode = NULL; + cipher = NULL; + valid = false; + assert_rnp_success(rnp_op_verify_get_protection_info(verify, &mode, &cipher, &valid)); + assert_string_equal(mode, "aead-ocb"); + assert_string_equal(cipher, "CAMELLIA192"); + assert_true(valid == (aead_ocb_enabled() && !aead_ocb_aes_only())); + rnp_buffer_destroy(mode); + rnp_buffer_destroy(cipher); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* message with AEAD-OCB AES-192 */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-aead-ocb-aes")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + if (!aead_ocb_enabled()) { + assert_rnp_failure(rnp_op_verify_execute(verify)); + } else { + assert_rnp_success(rnp_op_verify_execute(verify)); + } + mode = NULL; + cipher = NULL; + valid = false; + assert_rnp_success(rnp_op_verify_get_protection_info(verify, &mode, &cipher, &valid)); + assert_string_equal(mode, "aead-ocb"); + assert_string_equal(cipher, "AES192"); + assert_true(valid == aead_ocb_enabled()); + rnp_buffer_destroy(mode); + rnp_buffer_destroy(cipher); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* modified message with AEAD-OCB */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-aead-ocb-malf")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + mode = NULL; + cipher = NULL; + valid = false; + assert_rnp_success(rnp_op_verify_get_protection_info(verify, &mode, &cipher, &valid)); + assert_string_equal(mode, "aead-ocb"); + assert_string_equal(cipher, "CAMELLIA192"); + assert_false(valid); + rnp_buffer_destroy(mode); + rnp_buffer_destroy(cipher); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* message with AEAD-EAX */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-aead-eax")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + if (!aead_eax_enabled()) { + assert_rnp_failure(rnp_op_verify_execute(verify)); + } else { + assert_rnp_success(rnp_op_verify_execute(verify)); + } + mode = NULL; + cipher = NULL; + valid = false; + assert_rnp_success(rnp_op_verify_get_protection_info(verify, &mode, &cipher, &valid)); + assert_string_equal(mode, "aead-eax"); + assert_string_equal(cipher, "AES256"); + assert_true(valid == aead_eax_enabled()); + rnp_buffer_destroy(mode); + rnp_buffer_destroy(cipher); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* modified message with AEAD-EAX */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-aead-eax-malf")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + mode = NULL; + cipher = NULL; + valid = false; + assert_rnp_success(rnp_op_verify_get_protection_info(verify, &mode, &cipher, &valid)); + assert_string_equal(mode, "aead-eax"); + assert_string_equal(cipher, "AES256"); + assert_false(valid); + rnp_buffer_destroy(mode); + rnp_buffer_destroy(cipher); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + rnp_ffi_destroy(ffi); +} + +static bool +getpasscb_for_key(rnp_ffi_t ffi, + void * app_ctx, + rnp_key_handle_t key, + const char * pgp_context, + char * buf, + size_t buf_len) +{ + if (!key) { + return false; + } + char *keyid = NULL; + rnp_key_get_keyid(key, &keyid); + if (!keyid) { + return false; + } + const char *pass = "password"; + if (strcmp(keyid, (const char *) app_ctx)) { + pass = "wrongpassword"; + } + size_t pass_len = strlen(pass); + rnp_buffer_destroy(keyid); + + if (pass_len >= buf_len) { + return false; + } + memcpy(buf, pass, pass_len + 1); + return true; +} + +TEST_F(rnp_tests, test_ffi_op_verify_recipients_info) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + + // init ffi + test_ffi_init(&ffi); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + + /* message just signed */ + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message.txt.signed")); + assert_rnp_success(rnp_output_to_null(&output)); + rnp_op_verify_t verify = NULL; + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + /* check filename and mtime */ + char * filename = NULL; + uint32_t mtime = 0; + assert_rnp_failure(rnp_op_verify_get_file_info(NULL, &filename, &mtime)); + assert_rnp_success(rnp_op_verify_get_file_info(verify, &filename, &mtime)); + assert_string_equal(filename, "message.txt"); + assert_int_equal(mtime, 1571991574); + rnp_buffer_destroy(filename); + filename = NULL; + assert_rnp_success(rnp_op_verify_get_file_info(verify, &filename, NULL)); + assert_string_equal(filename, "message.txt"); + rnp_buffer_destroy(filename); + mtime = 0; + assert_rnp_success(rnp_op_verify_get_file_info(verify, NULL, &mtime)); + assert_int_equal(mtime, 1571991574); + /* rnp_op_verify_get_recipient_count */ + assert_rnp_failure(rnp_op_verify_get_recipient_count(verify, NULL)); + size_t count = 255; + assert_rnp_failure(rnp_op_verify_get_recipient_count(NULL, &count)); + assert_rnp_success(rnp_op_verify_get_recipient_count(verify, &count)); + assert_int_equal(count, 0); + /* rnp_op_verify_get_recipient_at */ + rnp_recipient_handle_t recipient = NULL; + assert_rnp_failure(rnp_op_verify_get_recipient_at(NULL, 0, &recipient)); + assert_rnp_failure(rnp_op_verify_get_recipient_at(verify, 0, NULL)); + assert_rnp_failure(rnp_op_verify_get_recipient_at(verify, 0, &recipient)); + assert_rnp_failure(rnp_op_verify_get_recipient_at(verify, 10, &recipient)); + /* rnp_op_verify_get_used_recipient */ + assert_rnp_failure(rnp_op_verify_get_used_recipient(NULL, &recipient)); + assert_rnp_failure(rnp_op_verify_get_used_recipient(verify, NULL)); + assert_rnp_success(rnp_op_verify_get_used_recipient(verify, &recipient)); + assert_null(recipient); + /* rnp_op_verify_get_symenc_count */ + assert_rnp_failure(rnp_op_verify_get_symenc_count(verify, NULL)); + count = 255; + assert_rnp_failure(rnp_op_verify_get_symenc_count(NULL, &count)); + assert_rnp_success(rnp_op_verify_get_symenc_count(verify, &count)); + assert_int_equal(count, 0); + /* rnp_op_verify_get_symenc_at */ + rnp_symenc_handle_t symenc = NULL; + assert_rnp_failure(rnp_op_verify_get_symenc_at(NULL, 0, &symenc)); + assert_rnp_failure(rnp_op_verify_get_symenc_at(verify, 0, NULL)); + assert_rnp_failure(rnp_op_verify_get_symenc_at(verify, 0, &symenc)); + assert_rnp_failure(rnp_op_verify_get_symenc_at(verify, 10, &symenc)); + /* rnp_op_verify_get_used_symenc */ + assert_rnp_failure(rnp_op_verify_get_used_symenc(NULL, &symenc)); + assert_rnp_failure(rnp_op_verify_get_used_symenc(verify, NULL)); + assert_rnp_success(rnp_op_verify_get_used_symenc(verify, &symenc)); + assert_null(symenc); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* message without MDC: single recipient */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-no-mdc")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_recipient_count(verify, &count)); + assert_int_equal(count, 1); + assert_rnp_failure(rnp_op_verify_get_recipient_at(verify, 1, &recipient)); + assert_rnp_success(rnp_op_verify_get_recipient_at(verify, 0, &recipient)); + assert_non_null(recipient); + char *alg = NULL; + assert_rnp_failure(rnp_recipient_get_alg(NULL, &alg)); + assert_rnp_failure(rnp_recipient_get_alg(recipient, NULL)); + assert_rnp_success(rnp_recipient_get_alg(recipient, &alg)); + assert_string_equal(alg, "RSA"); + rnp_buffer_destroy(alg); + char *keyid = NULL; + assert_rnp_failure(rnp_recipient_get_keyid(NULL, &keyid)); + assert_rnp_failure(rnp_recipient_get_keyid(recipient, NULL)); + assert_rnp_success(rnp_recipient_get_keyid(recipient, &keyid)); + assert_string_equal(keyid, "8A05B89FAD5ADED1"); + rnp_buffer_destroy(keyid); + recipient = NULL; + assert_rnp_success(rnp_op_verify_get_used_recipient(verify, &recipient)); + assert_non_null(recipient); + alg = NULL; + assert_rnp_success(rnp_recipient_get_alg(recipient, &alg)); + assert_string_equal(alg, "RSA"); + rnp_buffer_destroy(alg); + keyid = NULL; + assert_rnp_success(rnp_recipient_get_keyid(recipient, &keyid)); + assert_string_equal(keyid, "8A05B89FAD5ADED1"); + rnp_buffer_destroy(keyid); + assert_rnp_success(rnp_op_verify_get_symenc_count(verify, &count)); + assert_int_equal(count, 0); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* message with AEAD-OCB: single password */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-aead-ocb")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + if (!aead_ocb_enabled() || aead_ocb_aes_only()) { + assert_rnp_failure(rnp_op_verify_execute(verify)); + } else { + assert_rnp_success(rnp_op_verify_execute(verify)); + } + count = 255; + assert_rnp_success(rnp_op_verify_get_recipient_count(verify, &count)); + assert_int_equal(count, 0); + assert_rnp_success(rnp_op_verify_get_symenc_count(verify, &count)); + assert_int_equal(count, 1); + assert_rnp_failure(rnp_op_verify_get_symenc_at(verify, 1, &symenc)); + assert_rnp_success(rnp_op_verify_get_symenc_at(verify, 0, &symenc)); + assert_non_null(symenc); + char *cipher = NULL; + assert_rnp_failure(rnp_symenc_get_cipher(symenc, NULL)); + assert_rnp_success(rnp_symenc_get_cipher(symenc, &cipher)); + assert_string_equal(cipher, "CAMELLIA192"); + rnp_buffer_destroy(cipher); + char *aead = NULL; + assert_rnp_failure(rnp_symenc_get_aead_alg(symenc, NULL)); + assert_rnp_success(rnp_symenc_get_aead_alg(symenc, &aead)); + assert_string_equal(aead, "OCB"); + rnp_buffer_destroy(aead); + char *hash = NULL; + assert_rnp_failure(rnp_symenc_get_hash_alg(symenc, NULL)); + assert_rnp_success(rnp_symenc_get_hash_alg(symenc, &hash)); + assert_string_equal(hash, "SHA1"); + rnp_buffer_destroy(hash); + char *s2k = NULL; + assert_rnp_failure(rnp_symenc_get_s2k_type(symenc, NULL)); + assert_rnp_success(rnp_symenc_get_s2k_type(symenc, &s2k)); + assert_string_equal(s2k, "Iterated and salted"); + rnp_buffer_destroy(s2k); + uint32_t iterations = 0; + assert_rnp_failure(rnp_symenc_get_s2k_iterations(symenc, NULL)); + assert_rnp_success(rnp_symenc_get_s2k_iterations(symenc, &iterations)); + assert_int_equal(iterations, 30408704); + assert_rnp_success(rnp_op_verify_get_used_symenc(verify, &symenc)); + if (!aead_ocb_enabled() || aead_ocb_aes_only()) { + assert_null(symenc); + } else { + assert_non_null(symenc); + cipher = NULL; + assert_rnp_success(rnp_symenc_get_cipher(symenc, &cipher)); + assert_string_equal(cipher, "CAMELLIA192"); + rnp_buffer_destroy(cipher); + aead = NULL; + assert_rnp_success(rnp_symenc_get_aead_alg(symenc, &aead)); + assert_string_equal(aead, "OCB"); + rnp_buffer_destroy(aead); + hash = NULL; + assert_rnp_success(rnp_symenc_get_hash_alg(symenc, &hash)); + assert_string_equal(hash, "SHA1"); + rnp_buffer_destroy(hash); + s2k = NULL; + assert_rnp_success(rnp_symenc_get_s2k_type(symenc, &s2k)); + assert_string_equal(s2k, "Iterated and salted"); + rnp_buffer_destroy(s2k); + iterations = 0; + assert_rnp_success(rnp_symenc_get_s2k_iterations(symenc, &iterations)); + assert_int_equal(iterations, 30408704); + } + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* message with AEAD-OCB-AES: single password */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-aead-ocb-aes")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + if (!aead_ocb_enabled()) { + assert_rnp_failure(rnp_op_verify_execute(verify)); + } else { + assert_rnp_success(rnp_op_verify_execute(verify)); + } + count = 255; + assert_rnp_success(rnp_op_verify_get_recipient_count(verify, &count)); + assert_int_equal(count, 0); + assert_rnp_success(rnp_op_verify_get_symenc_count(verify, &count)); + assert_int_equal(count, 1); + assert_rnp_success(rnp_op_verify_get_symenc_at(verify, 0, &symenc)); + assert_non_null(symenc); + cipher = NULL; + assert_rnp_success(rnp_symenc_get_cipher(symenc, &cipher)); + assert_string_equal(cipher, "AES192"); + rnp_buffer_destroy(cipher); + aead = NULL; + assert_rnp_success(rnp_symenc_get_aead_alg(symenc, &aead)); + assert_string_equal(aead, "OCB"); + rnp_buffer_destroy(aead); + assert_rnp_success(rnp_op_verify_get_used_symenc(verify, &symenc)); + if (!aead_ocb_enabled()) { + assert_null(symenc); + } else { + assert_non_null(symenc); + cipher = NULL; + assert_rnp_success(rnp_symenc_get_cipher(symenc, &cipher)); + assert_string_equal(cipher, "AES192"); + rnp_buffer_destroy(cipher); + aead = NULL; + assert_rnp_success(rnp_symenc_get_aead_alg(symenc, &aead)); + assert_string_equal(aead, "OCB"); + rnp_buffer_destroy(aead); + } + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* modified message with AEAD-EAX: one recipient and one password, decrypt with recipient + */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-aead-eax-malf")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + count = 255; + assert_rnp_success(rnp_op_verify_get_recipient_count(verify, &count)); + assert_int_equal(count, 1); + assert_rnp_success(rnp_op_verify_get_recipient_at(verify, 0, &recipient)); + assert_non_null(recipient); + alg = NULL; + assert_rnp_success(rnp_recipient_get_alg(recipient, &alg)); + assert_string_equal(alg, "RSA"); + rnp_buffer_destroy(alg); + keyid = NULL; + assert_rnp_success(rnp_recipient_get_keyid(recipient, &keyid)); + assert_string_equal(keyid, "1ED63EE56FADC34D"); + rnp_buffer_destroy(keyid); + recipient = NULL; + assert_rnp_success(rnp_op_verify_get_used_recipient(verify, &recipient)); + if (!aead_eax_enabled()) { + assert_null(recipient); + } else { + assert_non_null(recipient); + assert_rnp_success(rnp_recipient_get_alg(recipient, &alg)); + assert_string_equal(alg, "RSA"); + rnp_buffer_destroy(alg); + assert_rnp_success(rnp_recipient_get_keyid(recipient, &keyid)); + assert_string_equal(keyid, "1ED63EE56FADC34D"); + rnp_buffer_destroy(keyid); + assert_rnp_success(rnp_op_verify_get_used_recipient(verify, &recipient)); + assert_non_null(recipient); + alg = NULL; + assert_rnp_success(rnp_recipient_get_alg(recipient, &alg)); + assert_string_equal(alg, "RSA"); + rnp_buffer_destroy(alg); + keyid = NULL; + assert_rnp_success(rnp_recipient_get_keyid(recipient, &keyid)); + assert_string_equal(keyid, "1ED63EE56FADC34D"); + rnp_buffer_destroy(keyid); + recipient = NULL; + assert_rnp_success(rnp_op_verify_get_used_recipient(verify, &recipient)); + assert_non_null(recipient); + assert_rnp_success(rnp_recipient_get_alg(recipient, &alg)); + assert_string_equal(alg, "RSA"); + rnp_buffer_destroy(alg); + assert_rnp_success(rnp_recipient_get_keyid(recipient, &keyid)); + assert_string_equal(keyid, "1ED63EE56FADC34D"); + rnp_buffer_destroy(keyid); + } + + count = 255; + assert_rnp_success(rnp_op_verify_get_symenc_count(verify, &count)); + assert_int_equal(count, 1); + assert_rnp_success(rnp_op_verify_get_symenc_at(verify, 0, &symenc)); + assert_non_null(symenc); + cipher = NULL; + assert_rnp_success(rnp_symenc_get_cipher(symenc, &cipher)); + assert_string_equal(cipher, "AES256"); + rnp_buffer_destroy(cipher); + aead = NULL; + assert_rnp_success(rnp_symenc_get_aead_alg(symenc, &aead)); + assert_string_equal(aead, "EAX"); + rnp_buffer_destroy(aead); + hash = NULL; + assert_rnp_success(rnp_symenc_get_hash_alg(symenc, &hash)); + assert_string_equal(hash, "SHA256"); + rnp_buffer_destroy(hash); + s2k = NULL; + assert_rnp_success(rnp_symenc_get_s2k_type(symenc, &s2k)); + assert_string_equal(s2k, "Iterated and salted"); + rnp_buffer_destroy(s2k); + iterations = 0; + assert_rnp_success(rnp_symenc_get_s2k_iterations(symenc, &iterations)); + assert_int_equal(iterations, 3932160); + assert_rnp_success(rnp_op_verify_get_used_symenc(verify, &symenc)); + assert_null(symenc); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* message with AEAD-EAX: one recipient and one password, decrypt with password */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_SECRET)); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-aead-eax")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + if (!aead_eax_enabled()) { + assert_rnp_failure(rnp_op_verify_execute(verify)); + } else { + assert_rnp_success(rnp_op_verify_execute(verify)); + } + count = 255; + assert_rnp_success(rnp_op_verify_get_recipient_count(verify, &count)); + assert_int_equal(count, 1); + assert_rnp_success(rnp_op_verify_get_used_recipient(verify, &recipient)); + assert_null(recipient); + count = 255; + assert_rnp_success(rnp_op_verify_get_symenc_count(verify, &count)); + assert_int_equal(count, 1); + assert_rnp_success(rnp_op_verify_get_symenc_at(verify, 0, &symenc)); + assert_non_null(symenc); + assert_rnp_success(rnp_op_verify_get_used_symenc(verify, &symenc)); + if (!aead_eax_enabled()) { + assert_null(symenc); + } else { + assert_non_null(symenc); + cipher = NULL; + assert_rnp_success(rnp_symenc_get_cipher(symenc, &cipher)); + assert_string_equal(cipher, "AES256"); + rnp_buffer_destroy(cipher); + aead = NULL; + assert_rnp_success(rnp_symenc_get_aead_alg(symenc, &aead)); + assert_string_equal(aead, "EAX"); + rnp_buffer_destroy(aead); + hash = NULL; + assert_rnp_success(rnp_symenc_get_hash_alg(symenc, &hash)); + assert_string_equal(hash, "SHA256"); + rnp_buffer_destroy(hash); + s2k = NULL; + assert_rnp_success(rnp_symenc_get_s2k_type(symenc, &s2k)); + assert_string_equal(s2k, "Iterated and salted"); + rnp_buffer_destroy(s2k); + iterations = 0; + assert_rnp_success(rnp_symenc_get_s2k_iterations(symenc, &iterations)); + assert_int_equal(iterations, 3932160); + } + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* message encrypted to 3 recipients and 2 passwords: password1, password2 */ + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "wrongpassword")); + assert_true(import_sec_keys(ffi, "data/keyrings/1/secring.gpg")); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-3key-2p")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + count = 255; + assert_rnp_success(rnp_op_verify_get_recipient_count(verify, &count)); + assert_int_equal(count, 3); + assert_rnp_success(rnp_op_verify_get_recipient_at(verify, 0, &recipient)); + assert_rnp_success(rnp_recipient_get_keyid(recipient, &keyid)); + assert_string_equal(keyid, "1ED63EE56FADC34D"); + rnp_buffer_destroy(keyid); + assert_rnp_success(rnp_op_verify_get_recipient_at(verify, 1, &recipient)); + assert_rnp_success(rnp_recipient_get_keyid(recipient, &keyid)); + assert_string_equal(keyid, "8A05B89FAD5ADED1"); + rnp_buffer_destroy(keyid); + assert_rnp_success(rnp_op_verify_get_recipient_at(verify, 2, &recipient)); + assert_rnp_success(rnp_recipient_get_keyid(recipient, &keyid)); + assert_string_equal(keyid, "54505A936A4A970E"); + rnp_buffer_destroy(keyid); + assert_rnp_success(rnp_op_verify_get_used_recipient(verify, &recipient)); + assert_null(recipient); + count = 255; + assert_rnp_success(rnp_op_verify_get_symenc_count(verify, &count)); + assert_int_equal(count, 2); + assert_rnp_success(rnp_op_verify_get_symenc_at(verify, 0, &symenc)); + assert_rnp_success(rnp_symenc_get_s2k_iterations(symenc, &iterations)); + assert_int_equal(iterations, 3932160); + assert_rnp_success(rnp_op_verify_get_symenc_at(verify, 1, &symenc)); + assert_rnp_success(rnp_symenc_get_s2k_iterations(symenc, &iterations)); + assert_int_equal(iterations, 3276800); + assert_rnp_success(rnp_op_verify_get_used_symenc(verify, &symenc)); + assert_null(symenc); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password2")); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-3key-2p")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_used_symenc(verify, &symenc)); + assert_rnp_success(rnp_symenc_get_s2k_iterations(symenc, &iterations)); + assert_rnp_success(rnp_op_verify_get_used_recipient(verify, &recipient)); + assert_null(recipient); + assert_int_equal(iterations, 3276800); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, getpasscb_for_key, (void *) "8A05B89FAD5ADED1")); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-3key-2p")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + assert_rnp_success(rnp_op_verify_get_used_symenc(verify, &symenc)); + assert_null(symenc); + assert_rnp_success(rnp_op_verify_get_used_recipient(verify, &recipient)); + assert_rnp_success(rnp_recipient_get_keyid(recipient, &keyid)); + assert_string_equal(keyid, "8A05B89FAD5ADED1"); + rnp_buffer_destroy(keyid); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_secret_sig_import) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_true(import_sec_keys(ffi, "data/test_key_validity/alice-sec.asc")); + rnp_key_handle_t key_handle = NULL; + assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key_handle)); + bool locked = false; + /* unlock secret key */ + assert_rnp_success(rnp_key_is_locked(key_handle, &locked)); + assert_true(locked); + assert_rnp_success(rnp_key_unlock(key_handle, "password")); + assert_rnp_success(rnp_key_is_locked(key_handle, &locked)); + assert_false(locked); + /* import revocation signature */ + assert_rnp_success(rnp_input_from_path(&input, "data/test_key_validity/alice-rev.pgp")); + assert_rnp_success(rnp_import_signatures(ffi, input, 0, NULL)); + assert_rnp_success(rnp_input_destroy(input)); + /* make sure that key is still unlocked */ + assert_rnp_success(rnp_key_is_locked(key_handle, &locked)); + assert_false(locked); + /* import subkey */ + assert_true(import_sec_keys(ffi, "data/test_key_validity/alice-sub-sec.pgp")); + /* make sure that primary key is still unlocked */ + assert_rnp_success(rnp_key_is_locked(key_handle, &locked)); + assert_false(locked); + /* unlock subkey and make sure it is unlocked after revocation */ + rnp_key_handle_t sub_handle = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "DD23CEB7FEBEFF17", &sub_handle)); + assert_rnp_success(rnp_key_unlock(sub_handle, "password")); + assert_rnp_success(rnp_key_is_locked(sub_handle, &locked)); + assert_false(locked); + assert_rnp_success(rnp_key_revoke(sub_handle, 0, "SHA256", "retired", "Custom reason")); + assert_rnp_success(rnp_key_is_locked(sub_handle, &locked)); + assert_false(locked); + assert_rnp_success(rnp_key_handle_destroy(sub_handle)); + assert_rnp_success(rnp_key_handle_destroy(key_handle)); + assert_rnp_success(rnp_ffi_destroy(ffi)); +} + +static bool +getpasscb_fail(rnp_ffi_t ffi, + void * app_ctx, + rnp_key_handle_t key, + const char * pgp_context, + char * buf, + size_t buf_len) +{ + return false; +} + +static bool +getpasscb_context(rnp_ffi_t ffi, + void * app_ctx, + rnp_key_handle_t key, + const char * pgp_context, + char * buf, + size_t buf_len) +{ + strncpy(buf, pgp_context, buf_len - 1); + return true; +} + +static bool +getpasscb_keyid(rnp_ffi_t ffi, + void * app_ctx, + rnp_key_handle_t key, + const char * pgp_context, + char * buf, + size_t buf_len) +{ + if (!key) { + return false; + } + char *keyid = NULL; + if (rnp_key_get_keyid(key, &keyid)) { + return false; + } + strncpy(buf, keyid, buf_len - 1); + rnp_buffer_destroy(keyid); + return true; +} + +TEST_F(rnp_tests, test_ffi_rnp_request_password) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + /* check wrong parameters cases */ + char *password = NULL; + assert_rnp_failure(rnp_request_password(ffi, NULL, "sign", &password)); + assert_null(password); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + assert_rnp_failure(rnp_request_password(NULL, NULL, "sign", &password)); + assert_rnp_failure(rnp_request_password(ffi, NULL, "sign", NULL)); + /* now it should succeed */ + assert_rnp_success(rnp_request_password(ffi, NULL, "sign", &password)); + assert_string_equal(password, "password"); + rnp_buffer_destroy(password); + /* let's try failing password provider */ + assert_rnp_success(rnp_ffi_set_pass_provider(ffi, getpasscb_fail, NULL)); + assert_rnp_failure(rnp_request_password(ffi, NULL, "sign", &password)); + /* let's try to return request context */ + assert_rnp_success(rnp_ffi_set_pass_provider(ffi, getpasscb_context, NULL)); + assert_rnp_success(rnp_request_password(ffi, NULL, "custom context", &password)); + assert_string_equal(password, "custom context"); + rnp_buffer_destroy(password); + /* let's check whether key is correctly passed to handler */ + assert_true(import_sec_keys(ffi, "data/test_key_validity/alice-sec.asc")); + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key)); + assert_non_null(key); + assert_rnp_success(rnp_ffi_set_pass_provider(ffi, getpasscb_keyid, NULL)); + assert_rnp_success(rnp_request_password(ffi, key, NULL, &password)); + assert_string_equal(password, "0451409669FFDE3C"); + rnp_buffer_destroy(password); + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success(rnp_ffi_destroy(ffi)); +} + +TEST_F(rnp_tests, test_ffi_mdc_8k_boundary) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + + test_ffi_init(&ffi); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + + /* correctly process two messages */ + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message_mdc_8k_1.pgp")); + rnp_output_t output = NULL; + assert_rnp_success(rnp_output_to_null(&output)); + rnp_op_verify_t verify; + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + /* check signature */ + size_t sig_count = 0; + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sig_count)); + assert_int_equal(sig_count, 1); + rnp_op_verify_signature_t sig = NULL; + assert_rnp_success(rnp_op_verify_get_signature_at(verify, 0, &sig)); + assert_rnp_success(rnp_op_verify_signature_get_status(sig)); + /* cleanup */ + assert_rnp_success(rnp_op_verify_destroy(verify)); + assert_rnp_success(rnp_input_destroy(input)); + + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message_mdc_8k_2.pgp")); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + /* check signature */ + sig_count = 0; + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sig_count)); + assert_int_equal(sig_count, 1); + assert_rnp_success(rnp_op_verify_get_signature_at(verify, 0, &sig)); + assert_rnp_success(rnp_op_verify_signature_get_status(sig)); + /* cleanup */ + assert_rnp_success(rnp_op_verify_destroy(verify)); + assert_rnp_success(rnp_input_destroy(input)); + + /* let it gracefully fail on message 1 with the last byte cut */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message_mdc_8k_cut1.pgp")); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + /* cleanup */ + assert_rnp_success(rnp_op_verify_destroy(verify)); + assert_rnp_success(rnp_input_destroy(input)); + + /* let it gracefully fail on message 1 with the last 22 bytes (MDC size) cut */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message_mdc_8k_cut22.pgp")); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(verify)); + /* cleanup */ + assert_rnp_success(rnp_op_verify_destroy(verify)); + assert_rnp_success(rnp_input_destroy(input)); + + assert_rnp_success(rnp_output_destroy(output)); + assert_rnp_success(rnp_ffi_destroy(ffi)); +} + +TEST_F(rnp_tests, test_ffi_decrypt_wrong_mpi_bits) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + + // init ffi + test_ffi_init(&ffi); + + /* 1024 bitcount instead of 1023 */ + rnp_op_verify_t op = NULL; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-malf-1")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + assert_rnp_success(rnp_op_verify_create(&op, ffi, input, output)); + if (!aead_eax_enabled()) { + assert_rnp_failure(rnp_op_verify_execute(op)); + } else { + assert_rnp_success(rnp_op_verify_execute(op)); + } + rnp_op_verify_destroy(op); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* 1025 bitcount instead of 1023 */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-malf-2")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + assert_rnp_success(rnp_op_verify_create(&op, ffi, input, output)); + if (!aead_eax_enabled()) { + assert_rnp_failure(rnp_op_verify_execute(op)); + } else { + assert_rnp_success(rnp_op_verify_execute(op)); + } + rnp_op_verify_destroy(op); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* 1031 bitcount instead of 1023 */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-malf-3")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + assert_rnp_success(rnp_op_verify_create(&op, ffi, input, output)); + if (!aead_eax_enabled()) { + assert_rnp_failure(rnp_op_verify_execute(op)); + } else { + assert_rnp_success(rnp_op_verify_execute(op)); + } + rnp_op_verify_destroy(op); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* 1040 bitcount instead of 1023 */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-malf-4")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + assert_rnp_success(rnp_op_verify_create(&op, ffi, input, output)); + if (!aead_eax_enabled()) { + assert_rnp_failure(rnp_op_verify_execute(op)); + } else { + assert_rnp_success(rnp_op_verify_execute(op)); + } + rnp_op_verify_destroy(op); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* 1017 bitcount instead of 1023 */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-malf-5")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + assert_rnp_success(rnp_op_verify_create(&op, ffi, input, output)); + if (!aead_eax_enabled()) { + assert_rnp_failure(rnp_op_verify_execute(op)); + } else { + assert_rnp_success(rnp_op_verify_execute(op)); + } + rnp_op_verify_destroy(op); + rnp_input_destroy(input); + rnp_output_destroy(output); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_decrypt_edge_cases) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + + test_ffi_init(&ffi); + + /* unknown algorithm in public-key encrypted session key */ + rnp_op_verify_t op = NULL; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-wrong-alg")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + assert_rnp_success(rnp_op_verify_create(&op, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(op)); + rnp_op_verify_destroy(op); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* endless recursive compression packets, 'quine'. + * Generated using the code by Taylor R. Campbell */ + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message.zlib-quine")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&op, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(op)); + rnp_op_verify_destroy(op); + rnp_input_destroy(input); + rnp_output_destroy(output); + + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message.zlib-quine")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_dump_packets_to_output(input, output, 0)); + rnp_input_destroy(input); + rnp_output_destroy(output); + + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message.zlib-quine")); + char *json = NULL; + assert_rnp_success(rnp_dump_packets_to_json(input, 0, &json)); + assert_non_null(json); + rnp_buffer_destroy(json); + rnp_input_destroy(input); + + /* 128 levels of compression - fail decryption*/ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.compr.128-rounds")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_op_verify_create(&op, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(op)); + rnp_op_verify_destroy(op); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* but dumping will succeed */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.compr.128-rounds")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_dump_packets_to_output(input, output, 0)); + rnp_input_destroy(input); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.compr.128-rounds")); + json = NULL; + assert_rnp_success(rnp_dump_packets_to_json(input, 0, &json)); + assert_non_null(json); + rnp_buffer_destroy(json); + rnp_input_destroy(input); + + /* 32 levels of compression + encryption */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.compr-encr.32-rounds")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&op, ffi, input, output)); + assert_rnp_failure(rnp_op_verify_execute(op)); + rnp_op_verify_destroy(op); + rnp_input_destroy(input); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.compr-encr.32-rounds")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_dump_packets_to_output(input, output, 0)); + rnp_input_destroy(input); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.compr-encr.32-rounds")); + json = NULL; + assert_rnp_success(rnp_dump_packets_to_json(input, 0, &json)); + assert_non_null(json); + rnp_buffer_destroy(json); + rnp_input_destroy(input); + + /* 31 levels of compression + encryption */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.compr-encr.31-rounds")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_op_verify_create(&op, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(op)); + rnp_op_verify_destroy(op); + rnp_input_destroy(input); + uint8_t *buf = NULL; + size_t len = 0; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + assert_int_equal(len, 7); + assert_int_equal(memcmp(buf, "message", 7), 0); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.compr-encr.31-rounds")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_dump_packets_to_output(input, output, 0)); + rnp_input_destroy(input); + rnp_output_destroy(output); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.compr-encr.31-rounds")); + json = NULL; + assert_rnp_success(rnp_dump_packets_to_json(input, 0, &json)); + assert_non_null(json); + rnp_buffer_destroy(json); + rnp_input_destroy(input); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_key_remove) +{ + rnp_ffi_t ffi = NULL; + test_ffi_init(&ffi); + + rnp_key_handle_t key0 = NULL; + rnp_key_handle_t key0_sub0 = NULL; + rnp_key_handle_t key0_sub1 = NULL; + rnp_key_handle_t key0_sub2 = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "7bc6709b15c23a4a", &key0)); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "1ed63ee56fadc34d", &key0_sub0)); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "1d7e8a5393c997a8", &key0_sub1)); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "8a05b89fad5aded1", &key0_sub2)); + + /* edge cases */ + assert_rnp_failure(rnp_key_remove(NULL, RNP_KEY_REMOVE_PUBLIC)); + assert_rnp_failure(rnp_key_remove(key0, 0)); + /* make sure we correctly remove public and secret keys */ + bool pub = false; + assert_rnp_success(rnp_key_have_public(key0_sub2, &pub)); + assert_true(pub); + bool sec = false; + assert_rnp_success(rnp_key_have_secret(key0_sub2, &sec)); + assert_true(sec); + assert_rnp_success(rnp_key_remove(key0_sub2, RNP_KEY_REMOVE_PUBLIC)); + pub = true; + assert_rnp_success(rnp_key_have_public(key0_sub2, &pub)); + assert_false(pub); + sec = false; + assert_rnp_success(rnp_key_have_secret(key0_sub2, &sec)); + assert_true(sec); + assert_rnp_failure(rnp_key_remove(key0_sub2, RNP_KEY_REMOVE_PUBLIC)); + rnp_key_handle_destroy(key0_sub2); + /* locate it back */ + assert_rnp_success(rnp_locate_key(ffi, "keyid", "8a05b89fad5aded1", &key0_sub2)); + assert_non_null(key0_sub2); + pub = true; + assert_rnp_success(rnp_key_have_public(key0_sub2, &pub)); + assert_false(pub); + sec = false; + assert_rnp_success(rnp_key_have_secret(key0_sub2, &sec)); + assert_true(sec); + + pub = false; + assert_rnp_success(rnp_key_have_public(key0_sub0, &pub)); + assert_true(pub); + sec = false; + assert_rnp_success(rnp_key_have_secret(key0_sub0, &sec)); + assert_true(sec); + assert_rnp_success(rnp_key_remove(key0_sub0, RNP_KEY_REMOVE_SECRET)); + pub = false; + assert_rnp_success(rnp_key_have_public(key0_sub0, &pub)); + assert_true(pub); + sec = true; + assert_rnp_success(rnp_key_have_secret(key0_sub0, &sec)); + assert_false(sec); + assert_rnp_failure(rnp_key_remove(key0_sub0, RNP_KEY_REMOVE_SECRET)); + rnp_key_handle_destroy(key0_sub0); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "1ed63ee56fadc34d", &key0_sub0)); + assert_non_null(key0_sub0); + + size_t count = 0; + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(count, 6); + count = 0; + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(count, 6); + + /* while there are 2 public and 1 secret subkey, this calculates only public */ + assert_rnp_success(rnp_key_get_subkey_count(key0, &count)); + assert_int_equal(count, 2); + + assert_rnp_success(rnp_key_remove(key0_sub0, RNP_KEY_REMOVE_PUBLIC)); + assert_rnp_success(rnp_key_get_subkey_count(key0, &count)); + assert_int_equal(count, 1); + count = 0; + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(count, 5); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(count, 6); + + assert_rnp_success(rnp_key_remove(key0_sub2, RNP_KEY_REMOVE_SECRET)); + assert_rnp_success(rnp_key_get_subkey_count(key0, &count)); + assert_int_equal(count, 1); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(count, 5); + + assert_rnp_success(rnp_key_remove(key0, RNP_KEY_REMOVE_PUBLIC | RNP_KEY_REMOVE_SECRET)); + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(count, 4); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(count, 4); + + rnp_key_handle_destroy(key0_sub1); + /* key0_sub1 should be left in keyring */ + assert_rnp_success(rnp_locate_key(ffi, "keyid", "1d7e8a5393c997a8", &key0_sub1)); + pub = false; + assert_rnp_success(rnp_key_have_public(key0_sub1, &pub)); + assert_true(pub); + sec = false; + assert_rnp_success(rnp_key_have_secret(key0_sub1, &sec)); + assert_true(sec); + + rnp_key_handle_destroy(key0); + rnp_key_handle_destroy(key0_sub0); + rnp_key_handle_destroy(key0_sub1); + rnp_key_handle_destroy(key0_sub2); + + /* let's import keys back */ + assert_true(import_pub_keys(ffi, "data/keyrings/1/pubring.gpg")); + assert_true(import_sec_keys(ffi, "data/keyrings/1/secring.gpg")); + + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(count, 7); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(count, 7); + + /* now try to remove the whole key */ + assert_rnp_success(rnp_locate_key(ffi, "keyid", "7bc6709b15c23a4a", &key0)); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "1ed63ee56fadc34d", &key0_sub0)); + + assert_rnp_failure( + rnp_key_remove(key0_sub0, RNP_KEY_REMOVE_SECRET | RNP_KEY_REMOVE_SUBKEYS)); + assert_rnp_success(rnp_key_remove(key0_sub0, RNP_KEY_REMOVE_SECRET)); + assert_rnp_success(rnp_key_remove(key0_sub0, RNP_KEY_REMOVE_PUBLIC)); + + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(count, 6); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(count, 6); + + assert_rnp_success(rnp_key_remove(key0, RNP_KEY_REMOVE_SECRET | RNP_KEY_REMOVE_SUBKEYS)); + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(count, 6); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(count, 3); + + assert_rnp_success(rnp_key_remove(key0, RNP_KEY_REMOVE_PUBLIC | RNP_KEY_REMOVE_SUBKEYS)); + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(count, 3); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(count, 3); + + rnp_key_handle_destroy(key0); + rnp_key_handle_destroy(key0_sub0); + + /* delete the second key all at once */ + assert_rnp_success(rnp_locate_key(ffi, "keyid", "2fcadf05ffa501bb", &key0)); + assert_rnp_success(rnp_key_remove( + key0, RNP_KEY_REMOVE_PUBLIC | RNP_KEY_REMOVE_SECRET | RNP_KEY_REMOVE_SUBKEYS)); + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(count, 0); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(count, 0); + rnp_key_handle_destroy(key0); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_literal_packet) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + + // init ffi + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + + /* try rnp_decrypt() */ + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message.txt.literal")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_decrypt(ffi, input, output)); + uint8_t *buf = NULL; + size_t len = 0; + rnp_output_memory_get_buf(output, &buf, &len, false); + std::string out; + out.assign((char *) buf, len); + assert_true(out == file_to_str("data/test_messages/message.txt")); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* try rnp_op_verify() */ + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message.txt.literal")); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + rnp_op_verify_t verify = NULL; + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + rnp_output_memory_get_buf(output, &buf, &len, false); + out.assign((char *) buf, len); + assert_true(out == file_to_str("data/test_messages/message.txt")); + char *mode = NULL; + char *cipher = NULL; + bool valid = true; + assert_rnp_success(rnp_op_verify_get_protection_info(verify, &mode, &cipher, &valid)); + assert_string_equal(mode, "none"); + assert_string_equal(cipher, "none"); + assert_false(valid); + rnp_buffer_destroy(mode); + rnp_buffer_destroy(cipher); + size_t count = 255; + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &count)); + assert_int_equal(count, 0); + count = 255; + assert_rnp_success(rnp_op_verify_get_recipient_count(verify, &count)); + assert_int_equal(count, 0); + count = 255; + assert_rnp_success(rnp_op_verify_get_symenc_count(verify, &count)); + assert_int_equal(count, 0); + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + rnp_ffi_destroy(ffi); +} + +/* This test checks that any exceptions thrown by the internal library + * will not propagate beyond the FFI boundary. + * In this case we (ab)use a callback to mimic this scenario. + */ +TEST_F(rnp_tests, test_ffi_exception) +{ + rnp_input_t input = NULL; + rnp_output_t output = NULL; + + // bad_alloc -> RNP_ERROR_OUT_OF_MEMORY + { + auto reader = [](void *app_ctx, void *buf, size_t len, size_t *read) { + throw std::bad_alloc(); + return true; + }; + assert_rnp_success(rnp_input_from_callback(&input, reader, NULL, NULL)); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_int_equal(RNP_ERROR_OUT_OF_MEMORY, rnp_output_pipe(input, output)); + rnp_input_destroy(input); + rnp_output_destroy(output); + } + + // runtime_error -> RNP_ERROR_GENERIC + { + auto reader = [](void *app_ctx, void *buf, size_t len, size_t *read) { + throw std::runtime_error(""); + return true; + }; + assert_rnp_success(rnp_input_from_callback(&input, reader, NULL, NULL)); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_int_equal(RNP_ERROR_GENERIC, rnp_output_pipe(input, output)); + rnp_input_destroy(input); + rnp_output_destroy(output); + } + + // everything else -> RNP_ERROR_GENERIC + { + auto reader = [](void *app_ctx, void *buf, size_t len, size_t *read) { + throw 5; + return true; + }; + assert_rnp_success(rnp_input_from_callback(&input, reader, NULL, NULL)); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_int_equal(RNP_ERROR_GENERIC, rnp_output_pipe(input, output)); + rnp_input_destroy(input); + rnp_output_destroy(output); + } +} + +TEST_F(rnp_tests, test_ffi_key_protection_change) +{ + rnp_ffi_t ffi = NULL; + test_ffi_init(&ffi); + + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "7bc6709b15c23a4a", &key)); + rnp_key_handle_t sub = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "8a05b89fad5aded1", &sub)); + + assert_rnp_success(rnp_key_unprotect(key, "password")); + assert_rnp_success(rnp_key_unprotect(sub, "password")); + + bool protect = true; + assert_rnp_success(rnp_key_is_protected(key, &protect)); + assert_false(protect); + protect = true; + assert_rnp_success(rnp_key_is_protected(sub, &protect)); + assert_false(protect); + + assert_rnp_success(rnp_key_protect(key, "password2", "Camellia128", NULL, "SHA1", 0)); + assert_rnp_success(rnp_key_protect(sub, "password2", "Camellia256", NULL, "SHA512", 0)); + protect = false; + assert_rnp_success(rnp_key_is_protected(key, &protect)); + assert_true(protect); + protect = false; + assert_rnp_success(rnp_key_is_protected(sub, &protect)); + assert_true(protect); + + rnp_key_handle_destroy(key); + rnp_key_handle_destroy(sub); + + reload_keyrings(&ffi); + + assert_rnp_success(rnp_locate_key(ffi, "keyid", "7bc6709b15c23a4a", &key)); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "8a05b89fad5aded1", &sub)); + + protect = false; + assert_rnp_success(rnp_key_is_protected(key, &protect)); + assert_true(protect); + protect = false; + assert_rnp_success(rnp_key_is_protected(sub, &protect)); + assert_true(protect); + + char *cipher = NULL; + assert_rnp_success(rnp_key_get_protection_cipher(key, &cipher)); + assert_string_equal(cipher, "CAMELLIA128"); + rnp_buffer_destroy(cipher); + char *hash = NULL; + assert_rnp_success(rnp_key_get_protection_hash(key, &hash)); + assert_string_equal(hash, "SHA1"); + rnp_buffer_destroy(hash); + cipher = NULL; + assert_rnp_success(rnp_key_get_protection_cipher(sub, &cipher)); + assert_string_equal(cipher, "CAMELLIA256"); + rnp_buffer_destroy(cipher); + hash = NULL; + assert_rnp_success(rnp_key_get_protection_hash(sub, &hash)); + assert_string_equal(hash, "SHA512"); + rnp_buffer_destroy(hash); + + assert_rnp_failure(rnp_key_unlock(key, "password")); + assert_rnp_failure(rnp_key_unlock(sub, "password")); + + assert_rnp_success(rnp_key_unlock(key, "password2")); + assert_rnp_success(rnp_key_unlock(sub, "password2")); + + protect = false; + assert_rnp_success(rnp_key_is_protected(key, &protect)); + assert_true(protect); + protect = false; + assert_rnp_success(rnp_key_is_protected(sub, &protect)); + assert_true(protect); + + assert_rnp_success(rnp_key_protect(key, "password3", "AES256", NULL, "SHA512", 10000000)); + assert_rnp_success(rnp_key_protect(sub, "password3", "AES128", NULL, "SHA1", 10000000)); + protect = false; + assert_rnp_success(rnp_key_is_protected(key, &protect)); + assert_true(protect); + protect = false; + assert_rnp_success(rnp_key_is_protected(sub, &protect)); + assert_true(protect); + + rnp_key_handle_destroy(key); + rnp_key_handle_destroy(sub); + + reload_keyrings(&ffi); + + assert_rnp_success(rnp_locate_key(ffi, "keyid", "7bc6709b15c23a4a", &key)); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "8a05b89fad5aded1", &sub)); + + protect = false; + assert_rnp_success(rnp_key_is_protected(key, &protect)); + assert_true(protect); + protect = false; + assert_rnp_success(rnp_key_is_protected(sub, &protect)); + assert_true(protect); + + cipher = NULL; + assert_rnp_success(rnp_key_get_protection_cipher(key, &cipher)); + assert_string_equal(cipher, "AES256"); + rnp_buffer_destroy(cipher); + hash = NULL; + assert_rnp_success(rnp_key_get_protection_hash(key, &hash)); + assert_string_equal(hash, "SHA512"); + rnp_buffer_destroy(hash); + size_t iterations = 0; + assert_rnp_success(rnp_key_get_protection_iterations(key, &iterations)); + assert_true(iterations >= 10000000); + cipher = NULL; + assert_rnp_success(rnp_key_get_protection_cipher(sub, &cipher)); + assert_string_equal(cipher, "AES128"); + rnp_buffer_destroy(cipher); + hash = NULL; + assert_rnp_success(rnp_key_get_protection_hash(sub, &hash)); + assert_string_equal(hash, "SHA1"); + rnp_buffer_destroy(hash); + iterations = 0; + assert_rnp_success(rnp_key_get_protection_iterations(sub, &iterations)); + assert_true(iterations >= 10000000); + + assert_rnp_failure(rnp_key_unlock(key, "password")); + assert_rnp_failure(rnp_key_unlock(sub, "password")); + + assert_rnp_success(rnp_key_unlock(key, "password3")); + assert_rnp_success(rnp_key_unlock(sub, "password3")); + + rnp_key_handle_destroy(key); + rnp_key_handle_destroy(sub); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_set_log_fd) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_failure(rnp_ffi_set_log_fd(NULL, 0)); + assert_rnp_failure(rnp_ffi_set_log_fd(ffi, 100)); + int file_fd = rnp_open("tests.txt", O_RDWR | O_CREAT | O_TRUNC, 0777); + assert_true(file_fd > 0); + assert_rnp_success(rnp_ffi_set_log_fd(ffi, file_fd)); + rnp_input_t input = NULL; + const char *msg = "hello"; + assert_rnp_success( + rnp_input_from_memory(&input, (const uint8_t *) msg, strlen(msg), true)); + char *saved_env = NULL; + if (getenv(RNP_LOG_CONSOLE)) { + saved_env = strdup(getenv(RNP_LOG_CONSOLE)); + } + setenv(RNP_LOG_CONSOLE, "1", 1); + assert_rnp_failure(rnp_load_keys(ffi, "GPG", input, 119)); + assert_rnp_success(rnp_input_destroy(input)); + rnp_ffi_destroy(ffi); + if (saved_env) { + assert_int_equal(0, setenv(RNP_LOG_CONSOLE, saved_env, 1)); + free(saved_env); + } else { + unsetenv(RNP_LOG_CONSOLE); + } +#ifndef _WIN32 + assert_int_equal(fcntl(file_fd, F_GETFD), -1); + assert_int_equal(errno, EBADF); +#endif + char buffer[128] = {0}; + file_fd = rnp_open("tests.txt", O_RDONLY, 0); + int64_t rres = read(file_fd, buffer, sizeof(buffer)); + assert_true(rres > 0); + assert_non_null(strstr(buffer, "unexpected flags remaining: 0x")); + close(file_fd); +} + +TEST_F(rnp_tests, test_ffi_security_profile) +{ + rnp_ffi_t ffi = NULL; + rnp_ffi_create(&ffi, "GPG", "GPG"); + /* check predefined rules */ + uint32_t flags = 0; + uint64_t from = 0; + uint32_t level = 0; + assert_rnp_failure( + rnp_get_security_rule(NULL, RNP_FEATURE_HASH_ALG, "SHA1", 0, &flags, &from, &level)); + assert_rnp_failure(rnp_get_security_rule(ffi, NULL, "SHA1", 0, &flags, &from, &level)); + assert_rnp_failure( + rnp_get_security_rule(ffi, RNP_FEATURE_SYMM_ALG, "AES256", 0, &flags, &from, &level)); + assert_rnp_failure( + rnp_get_security_rule(ffi, RNP_FEATURE_HASH_ALG, "Unknown", 0, &flags, &from, &level)); + assert_rnp_failure( + rnp_get_security_rule(ffi, RNP_FEATURE_HASH_ALG, "SHA1", 0, NULL, NULL, NULL)); + /* default rule */ + from = 256; + assert_rnp_success( + rnp_get_security_rule(ffi, RNP_FEATURE_HASH_ALG, "SHA256", 0, &flags, &from, &level)); + assert_int_equal(level, RNP_SECURITY_DEFAULT); + assert_int_equal(from, 0); + /* MD5 */ + from = 256; + assert_rnp_success( + rnp_get_security_rule(ffi, RNP_FEATURE_HASH_ALG, "MD5", 0, &flags, &from, &level)); + assert_int_equal(from, 0); + assert_int_equal(level, RNP_SECURITY_DEFAULT); + assert_rnp_success(rnp_get_security_rule( + ffi, RNP_FEATURE_HASH_ALG, "MD5", time(NULL), &flags, &from, &level)); + assert_int_equal(from, MD5_FROM); + assert_int_equal(level, RNP_SECURITY_INSECURE); + assert_rnp_success(rnp_get_security_rule( + ffi, RNP_FEATURE_HASH_ALG, "MD5", MD5_FROM - 1, &flags, &from, &level)); + assert_int_equal(from, 0); + assert_int_equal(flags, 0); + assert_int_equal(level, RNP_SECURITY_DEFAULT); + /* Override it */ + assert_rnp_failure(rnp_add_security_rule( + NULL, RNP_FEATURE_HASH_ALG, "MD5", RNP_SECURITY_OVERRIDE, 0, RNP_SECURITY_DEFAULT)); + assert_rnp_failure(rnp_add_security_rule( + ffi, RNP_FEATURE_SYMM_ALG, "MD5", RNP_SECURITY_OVERRIDE, 0, RNP_SECURITY_DEFAULT)); + assert_rnp_failure( + rnp_add_security_rule(ffi, NULL, "MD5", RNP_SECURITY_OVERRIDE, 0, RNP_SECURITY_DEFAULT)); + assert_rnp_failure(rnp_add_security_rule( + ffi, RNP_FEATURE_HASH_ALG, NULL, RNP_SECURITY_OVERRIDE, 0, RNP_SECURITY_DEFAULT)); + assert_rnp_failure(rnp_add_security_rule(ffi, + RNP_FEATURE_HASH_ALG, + "MD5", + RNP_SECURITY_OVERRIDE | 0x17, + 0, + RNP_SECURITY_DEFAULT)); + assert_rnp_failure( + rnp_add_security_rule(ffi, RNP_FEATURE_HASH_ALG, "MD5", RNP_SECURITY_OVERRIDE, 0, 25)); + assert_rnp_success(rnp_add_security_rule(ffi, + RNP_FEATURE_HASH_ALG, + "MD5", + RNP_SECURITY_OVERRIDE, + MD5_FROM - 1, + RNP_SECURITY_DEFAULT)); + assert_rnp_success(rnp_get_security_rule( + ffi, RNP_FEATURE_HASH_ALG, "MD5", time(NULL), &flags, &from, &level)); + assert_int_equal(from, MD5_FROM - 1); + assert_int_equal(flags, RNP_SECURITY_OVERRIDE); + assert_int_equal(level, RNP_SECURITY_DEFAULT); + /* Remove and check back */ + size_t removed = 0; + assert_rnp_failure(rnp_remove_security_rule(NULL, + RNP_FEATURE_HASH_ALG, + "MD5", + RNP_SECURITY_DEFAULT, + RNP_SECURITY_OVERRIDE, + MD5_FROM - 1, + &removed)); + assert_rnp_failure(rnp_remove_security_rule(ffi, + RNP_FEATURE_SYMM_ALG, + "MD5", + RNP_SECURITY_DEFAULT, + RNP_SECURITY_OVERRIDE, + MD5_FROM - 1, + &removed)); + assert_rnp_success(rnp_remove_security_rule(ffi, + RNP_FEATURE_HASH_ALG, + "SHA256", + RNP_SECURITY_DEFAULT, + RNP_SECURITY_OVERRIDE, + MD5_FROM - 1, + &removed)); + assert_int_equal(removed, 0); + removed = 1; + assert_rnp_success(rnp_remove_security_rule(ffi, + RNP_FEATURE_HASH_ALG, + "MD5", + RNP_SECURITY_INSECURE, + RNP_SECURITY_OVERRIDE, + MD5_FROM - 1, + &removed)); + assert_int_equal(removed, 0); + removed = 1; + assert_rnp_success(rnp_remove_security_rule( + ffi, RNP_FEATURE_HASH_ALG, "MD5", RNP_SECURITY_DEFAULT, 0, MD5_FROM - 1, &removed)); + assert_int_equal(removed, 0); + removed = 1; + assert_rnp_success(rnp_remove_security_rule(ffi, + RNP_FEATURE_HASH_ALG, + "MD5", + RNP_SECURITY_DEFAULT, + RNP_SECURITY_OVERRIDE, + MD5_FROM, + &removed)); + assert_int_equal(removed, 0); + assert_rnp_success(rnp_remove_security_rule(ffi, + RNP_FEATURE_HASH_ALG, + "MD5", + RNP_SECURITY_DEFAULT, + RNP_SECURITY_OVERRIDE, + MD5_FROM - 1, + &removed)); + assert_int_equal(removed, 1); + assert_rnp_success(rnp_get_security_rule( + ffi, RNP_FEATURE_HASH_ALG, "MD5", time(NULL), &flags, &from, &level)); + assert_int_equal(from, MD5_FROM); + assert_int_equal(level, RNP_SECURITY_INSECURE); + assert_int_equal(flags, 0); + /* Add for data sigs only */ + assert_rnp_success(rnp_add_security_rule(ffi, + RNP_FEATURE_HASH_ALG, + "MD5", + RNP_SECURITY_VERIFY_DATA, + MD5_FROM + 1, + RNP_SECURITY_DEFAULT)); + flags = RNP_SECURITY_VERIFY_DATA; + assert_rnp_success(rnp_get_security_rule( + ffi, RNP_FEATURE_HASH_ALG, "MD5", time(NULL), &flags, &from, &level)); + assert_int_equal(from, MD5_FROM + 1); + assert_int_equal(flags, RNP_SECURITY_VERIFY_DATA); + assert_int_equal(level, RNP_SECURITY_DEFAULT); + /* Add for key sigs only */ + assert_rnp_success(rnp_add_security_rule(ffi, + RNP_FEATURE_HASH_ALG, + "MD5", + RNP_SECURITY_VERIFY_KEY, + MD5_FROM + 2, + RNP_SECURITY_DEFAULT)); + flags = RNP_SECURITY_VERIFY_KEY; + assert_rnp_success(rnp_get_security_rule( + ffi, RNP_FEATURE_HASH_ALG, "MD5", time(NULL), &flags, &from, &level)); + assert_int_equal(from, MD5_FROM + 2); + assert_int_equal(flags, RNP_SECURITY_VERIFY_KEY); + assert_int_equal(level, RNP_SECURITY_DEFAULT); + /* Remove added two rules */ + assert_rnp_success(rnp_remove_security_rule(ffi, + RNP_FEATURE_HASH_ALG, + "MD5", + RNP_SECURITY_DEFAULT, + RNP_SECURITY_VERIFY_DATA, + MD5_FROM + 1, + &removed)); + assert_int_equal(removed, 1); + assert_rnp_success(rnp_remove_security_rule(ffi, + RNP_FEATURE_HASH_ALG, + "MD5", + RNP_SECURITY_DEFAULT, + RNP_SECURITY_VERIFY_KEY, + MD5_FROM + 2, + &removed)); + assert_int_equal(removed, 1); + /* Remove all */ + removed = 0; + assert_rnp_failure(rnp_remove_security_rule(ffi, NULL, NULL, 0, 0x17, 0, &removed)); + assert_rnp_success(rnp_remove_security_rule(ffi, NULL, NULL, 0, 0, 0, &removed)); + assert_int_equal(removed, 3); + rnp_ffi_destroy(ffi); + rnp_ffi_create(&ffi, "GPG", "GPG"); + /* Remove all rules for hash */ + assert_rnp_failure( + rnp_remove_security_rule(ffi, RNP_FEATURE_SYMM_ALG, NULL, 0, 0, 0, &removed)); + removed = 0; + assert_rnp_success( + rnp_remove_security_rule(ffi, RNP_FEATURE_HASH_ALG, NULL, 0, 0, 0, &removed)); + assert_int_equal(removed, 3); + rnp_ffi_destroy(ffi); + rnp_ffi_create(&ffi, "GPG", "GPG"); + /* Remove all rules for specific hash */ + assert_rnp_success(rnp_remove_security_rule( + ffi, RNP_FEATURE_HASH_ALG, "MD5", 0, RNP_SECURITY_REMOVE_ALL, 0, &removed)); + assert_int_equal(removed, 1); + assert_rnp_success(rnp_remove_security_rule( + ffi, RNP_FEATURE_HASH_ALG, "SHA1", 0, RNP_SECURITY_REMOVE_ALL, 0, &removed)); + assert_int_equal(removed, 2); + rnp_ffi_destroy(ffi); + rnp_ffi_create(&ffi, "GPG", "GPG"); + /* SHA1 - ancient times */ + from = 256; + flags = 0; + assert_rnp_success( + rnp_get_security_rule(ffi, RNP_FEATURE_HASH_ALG, "SHA1", 0, &flags, &from, &level)); + assert_int_equal(from, 0); + assert_int_equal(level, RNP_SECURITY_DEFAULT); + assert_int_equal(flags, 0); + /* SHA1 - now, data verify disabled, key sig verify is enabled */ + flags = 0; + assert_rnp_success(rnp_get_security_rule( + ffi, RNP_FEATURE_HASH_ALG, "SHA1", time(NULL), &flags, &from, &level)); + assert_int_equal(from, SHA1_DATA_FROM); + assert_int_equal(level, RNP_SECURITY_INSECURE); + assert_int_equal(flags, RNP_SECURITY_VERIFY_DATA); + flags = 0; + assert_rnp_success(rnp_get_security_rule( + ffi, RNP_FEATURE_HASH_ALG, "SHA1", SHA1_DATA_FROM - 1, &flags, &from, &level)); + assert_int_equal(from, 0); + assert_int_equal(level, RNP_SECURITY_DEFAULT); + flags = RNP_SECURITY_VERIFY_DATA; + assert_rnp_success(rnp_get_security_rule( + ffi, RNP_FEATURE_HASH_ALG, "SHA1", time(NULL), &flags, &from, &level)); + assert_int_equal(from, SHA1_DATA_FROM); + assert_int_equal(level, RNP_SECURITY_INSECURE); + assert_int_equal(flags, RNP_SECURITY_VERIFY_DATA); + flags = RNP_SECURITY_VERIFY_KEY; + assert_rnp_success(rnp_get_security_rule( + ffi, RNP_FEATURE_HASH_ALG, "SHA1", time(NULL), &flags, &from, &level)); + assert_int_equal(from, 0); + assert_int_equal(level, RNP_SECURITY_DEFAULT); + assert_int_equal(flags, 0); + flags = RNP_SECURITY_VERIFY_KEY; + assert_rnp_success(rnp_get_security_rule( + ffi, RNP_FEATURE_HASH_ALG, "SHA1", SHA1_KEY_FROM + 5, &flags, &from, &level)); + assert_int_equal(from, SHA1_KEY_FROM); + assert_int_equal(level, RNP_SECURITY_INSECURE); + assert_int_equal(flags, RNP_SECURITY_VERIFY_KEY); + flags = 0; + assert_rnp_success(rnp_get_security_rule( + ffi, RNP_FEATURE_HASH_ALG, "SHA1", SHA1_KEY_FROM + 5, &flags, &from, &level)); + assert_int_equal(from, SHA1_KEY_FROM); + assert_int_equal(level, RNP_SECURITY_INSECURE); + assert_int_equal(flags, RNP_SECURITY_VERIFY_KEY); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_result_to_string) +{ + const char * result_string = NULL; + rnp_result_t code; + std::set<std::string> stringset; + + result_string = rnp_result_to_string(RNP_SUCCESS); + assert_string_equal(result_string, "Success"); + + /* Cover all defined error code ranges, + * check that each defined + * code has corresponding unique string */ + + std::vector<std::pair<rnp_result_t, rnp_result_t>> error_codes = { + {RNP_ERROR_GENERIC, RNP_ERROR_NULL_POINTER}, + {RNP_ERROR_ACCESS, RNP_ERROR_WRITE}, + {RNP_ERROR_BAD_STATE, RNP_ERROR_SIGNATURE_UNKNOWN}, + {RNP_ERROR_NOT_ENOUGH_DATA, RNP_ERROR_EOF}}; + + for (auto &range : error_codes) { + for (code = range.first; code <= range.second; code++) { + result_string = rnp_result_to_string(code); + + assert_int_not_equal(strcmp("Unsupported error code", result_string), 0); + + auto search = stringset.find(result_string); + + /* Make sure returned error string is not already returned for other codes */ + assert_true(search == stringset.end()); + + stringset.insert(result_string); + } + } +} |