diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:32:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:32:49 +0000 |
commit | 8053187731ae8e3eb368d8360989cf5fd6eed9f7 (patch) | |
tree | 32bada84ff5d7460cdf3934fcbdbe770d6afe4cd /src/tests/generatekey.cpp | |
parent | Initial commit. (diff) | |
download | rnp-8053187731ae8e3eb368d8360989cf5fd6eed9f7.tar.xz rnp-8053187731ae8e3eb368d8360989cf5fd6eed9f7.zip |
Adding upstream version 0.17.0.upstream/0.17.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tests/generatekey.cpp')
-rw-r--r-- | src/tests/generatekey.cpp | 1243 |
1 files changed, 1243 insertions, 0 deletions
diff --git a/src/tests/generatekey.cpp b/src/tests/generatekey.cpp new file mode 100644 index 0000000..b846ebb --- /dev/null +++ b/src/tests/generatekey.cpp @@ -0,0 +1,1243 @@ +/* + * 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 "rnp.h" +#include <rekey/rnp_key_store.h> +#include <rnp/rnpcfg.h> +#include <rnpkeys/rnpkeys.h> + +#include "rnp_tests.h" +#include "support.h" +#include "crypto/common.h" +#include "crypto.h" +#include "pgp-key.h" +#include "librepgp/stream-ctx.h" +#include "librepgp/stream-sig.h" +#include "librepgp/stream-key.h" +#include "defaults.h" +#include <fstream> + +static bool +generate_test_key(const char *keystore, const char *userid, const char *hash, const char *home) +{ + cli_rnp_t rnp; + int pipefd[2] = {-1, -1}; + bool res = false; + size_t keycount = 0; + + /* Initialize the cli rnp structure and generate key */ + if (!setup_cli_rnp_common(&rnp, keystore, home, pipefd)) { + return false; + } + + std::vector<rnp_key_handle_t> keys; + /* Generate the key */ + cli_set_default_rsa_key_desc(rnp.cfg(), hash); + if (!cli_rnp_generate_key(&rnp, userid)) { + goto done; + } + + if (!rnp.load_keyrings(true)) { + goto done; + } + if (rnp_get_public_key_count(rnp.ffi, &keycount) || (keycount != 2)) { + goto done; + } + if (rnp_get_secret_key_count(rnp.ffi, &keycount) || (keycount != 2)) { + goto done; + } + if (!cli_rnp_keys_matching_string( + &rnp, keys, userid ? userid : "", CLI_SEARCH_SUBKEYS_AFTER)) { + goto done; + } + if (keys.size() != 2) { + goto done; + } + res = true; +done: + if (pipefd[0] != -1) { + close(pipefd[0]); + } + clear_key_handles(keys); + rnp.end(); + return res; +} + +static bool +hash_supported(const std::string &hash) +{ + if (!sm2_enabled() && lowercase(hash) == "sm3") { + return false; + } + return true; +} + +static bool +hash_secure(rnp_ffi_t ffi, const std::string &hash, uint32_t action) +{ + uint32_t flags = action; + uint32_t level = 0; + rnp_get_security_rule( + ffi, RNP_FEATURE_HASH_ALG, hash.c_str(), global_ctx.time(), &flags, NULL, &level); + return level == RNP_SECURITY_DEFAULT; +} + +TEST_F(rnp_tests, rnpkeys_generatekey_testSignature) +{ + /* Set the UserId = custom value. + * Execute the Generate-key command to generate a new pair of private/public + * key + * Sign a message, then verify it + */ + + const char *hashAlg[] = {"SHA1", + "SHA224", + "SHA256", + "SHA384", + "SHA512", + "SM3", + "sha1", + "sha224", + "sha256", + "sha384", + "sha512", + "sm3", + NULL}; + int pipefd[2] = {-1, -1}; + char memToSign[] = "A simple test message"; + cli_rnp_t rnp; + + std::ofstream out("dummyfile.dat"); + out << memToSign; + out.close(); + + for (int i = 0; hashAlg[i] != NULL; i++) { + std::string userId = std::string("sigtest_") + hashAlg[i]; + /* Generate key for test */ + assert_true( + generate_test_key(RNP_KEYSTORE_GPG, userId.c_str(), DEFAULT_HASH_ALG, NULL)); + + for (unsigned int cleartext = 0; cleartext <= 1; ++cleartext) { + for (unsigned int armored = 0; armored <= 1; ++armored) { + if (cleartext && !armored) { + // This combination doesn't make sense + continue; + } + /* Setup password input and rnp structure */ + assert_true(setup_cli_rnp_common(&rnp, RNP_KEYSTORE_GPG, NULL, pipefd)); + /* Load keyring */ + assert_true(rnp.load_keyrings(true)); + size_t seccount = 0; + assert_rnp_success(rnp_get_secret_key_count(rnp.ffi, &seccount)); + assert_true(seccount > 0); + + /* Setup signing context */ + rnp_cfg &cfg = rnp.cfg(); + cfg.load_defaults(); + cfg.set_bool(CFG_ARMOR, armored); + cfg.set_bool(CFG_SIGN_NEEDED, true); + cfg.set_str(CFG_HASH, hashAlg[i]); + cfg.set_int(CFG_ZLEVEL, 0); + cfg.set_str(CFG_INFILE, "dummyfile.dat"); + cfg.set_str(CFG_OUTFILE, "dummyfile.dat.pgp"); + cfg.set_bool(CFG_CLEARTEXT, cleartext); + cfg.add_str(CFG_SIGNERS, userId); + + /* Sign the file */ + if (!hash_supported(hashAlg[i])) { + assert_false(cli_rnp_protect_file(&rnp)); + rnp.end(); + assert_int_equal(rnp_unlink("dummyfile.dat.pgp"), -1); + continue; + } + assert_true(cli_rnp_protect_file(&rnp)); + if (pipefd[0] != -1) { + close(pipefd[0]); + pipefd[0] = -1; + } + + /* Verify the file */ + cfg.clear(); + cfg.load_defaults(); + cfg.set_bool(CFG_OVERWRITE, true); + cfg.set_str(CFG_INFILE, "dummyfile.dat.pgp"); + cfg.set_str(CFG_OUTFILE, "dummyfile.verify"); + if (!hash_secure(rnp.ffi, hashAlg[i], RNP_SECURITY_VERIFY_DATA)) { + assert_false(cli_rnp_process_file(&rnp)); + rnp.end(); + assert_int_equal(rnp_unlink("dummyfile.dat.pgp"), 0); + continue; + } + assert_true(cli_rnp_process_file(&rnp)); + + /* Ensure signature verification passed */ + std::string verify = file_to_str("dummyfile.verify"); + if (cleartext) { + verify = strip_eol(verify); + } + assert_true(verify == memToSign); + + /* Corrupt the signature if not armored/cleartext */ + if (!cleartext && !armored) { + std::fstream verf("dummyfile.dat.pgp", + std::ios_base::binary | std::ios_base::out | + std::ios_base::in); + off_t versize = file_size("dummyfile.dat.pgp"); + verf.seekg(versize - 10, std::ios::beg); + char sigch = 0; + verf.read(&sigch, 1); + sigch = sigch ^ 0xff; + verf.seekg(versize - 10, std::ios::beg); + verf.write(&sigch, 1); + verf.close(); + assert_false(cli_rnp_process_file(&rnp)); + } + + rnp.end(); + assert_int_equal(rnp_unlink("dummyfile.dat.pgp"), 0); + rnp_unlink("dummyfile.verify"); + } + } + } + assert_int_equal(rnp_unlink("dummyfile.dat"), 0); +} + +static bool +cipher_supported(const std::string &cipher) +{ + if (!sm2_enabled() && lowercase(cipher) == "sm4") { + return false; + } + if (!twofish_enabled() && lowercase(cipher) == "twofish") { + return false; + } + if (!blowfish_enabled() && lowercase(cipher) == "blowfish") { + return false; + } + if (!cast5_enabled() && lowercase(cipher) == "cast5") { + return false; + } + return true; +} + +TEST_F(rnp_tests, rnpkeys_generatekey_testEncryption) +{ + const char *cipherAlg[] = { + "BLOWFISH", "TWOFISH", "CAST5", "TRIPLEDES", "AES128", "AES192", + "AES256", "CAMELLIA128", "CAMELLIA192", "CAMELLIA256", "SM4", "blowfish", + "twofish", "cast5", "tripledes", "aes128", "aes192", "aes256", + "camellia128", "camellia192", "camellia256", "sm4", NULL}; + + cli_rnp_t rnp = {}; + char memToEncrypt[] = "A simple test message"; + int pipefd[2] = {-1, -1}; + const char *userid = "ciphertest"; + + std::ofstream out("dummyfile.dat"); + out << memToEncrypt; + out.close(); + + assert_true(generate_test_key(RNP_KEYSTORE_GPG, userid, "SHA256", NULL)); + for (int i = 0; cipherAlg[i] != NULL; i++) { + for (unsigned int armored = 0; armored <= 1; ++armored) { + /* Set up rnp and encrypt the dataa */ + assert_true(setup_cli_rnp_common(&rnp, RNP_KEYSTORE_GPG, NULL, NULL)); + /* Load keyring */ + assert_true(rnp.load_keyrings(false)); + size_t seccount = 0; + assert_rnp_success(rnp_get_secret_key_count(rnp.ffi, &seccount)); + assert_true(seccount == 0); + /* Set the cipher and armored flags */ + rnp_cfg &cfg = rnp.cfg(); + cfg.load_defaults(); + cfg.set_bool(CFG_ARMOR, armored); + cfg.set_bool(CFG_ENCRYPT_PK, true); + cfg.set_int(CFG_ZLEVEL, 0); + cfg.set_str(CFG_INFILE, "dummyfile.dat"); + cfg.set_str(CFG_OUTFILE, "dummyfile.dat.pgp"); + cfg.set_str(CFG_CIPHER, cipherAlg[i]); + cfg.add_str(CFG_RECIPIENTS, userid); + /* Encrypt the file */ + bool supported = cipher_supported(cipherAlg[i]); + assert_true(cli_rnp_protect_file(&rnp) == supported); + rnp.end(); + if (!supported) { + continue; + } + + /* Set up rnp again and decrypt the file */ + assert_true(setup_cli_rnp_common(&rnp, RNP_KEYSTORE_GPG, NULL, pipefd)); + /* Load the keyrings */ + assert_true(rnp.load_keyrings(true)); + assert_rnp_success(rnp_get_secret_key_count(rnp.ffi, &seccount)); + assert_true(seccount > 0); + /* Setup the decryption context and decrypt */ + rnp_cfg &newcfg = rnp.cfg(); + newcfg.load_defaults(); + newcfg.set_bool(CFG_OVERWRITE, true); + newcfg.set_str(CFG_INFILE, "dummyfile.dat.pgp"); + newcfg.set_str(CFG_OUTFILE, "dummyfile.decrypt"); + assert_true(cli_rnp_process_file(&rnp)); + rnp.end(); + if (pipefd[0] != -1) { + close(pipefd[0]); + } + + /* Ensure plaintext recovered */ + std::string decrypt = file_to_str("dummyfile.decrypt"); + assert_true(decrypt == memToEncrypt); + assert_int_equal(rnp_unlink("dummyfile.dat.pgp"), 0); + assert_int_equal(rnp_unlink("dummyfile.decrypt"), 0); + } + } + assert_int_equal(rnp_unlink("dummyfile.dat"), 0); +} + +TEST_F(rnp_tests, rnpkeys_generatekey_verifySupportedHashAlg) +{ + /* Generate key for each of the hash algorithms. Check whether key was generated + * successfully */ + + const char *hashAlg[] = {"MD5", + "SHA1", + "SHA256", + "SHA384", + "SHA512", + "SHA224", + "SM3", + "md5", + "sha1", + "sha256", + "sha384", + "sha512", + "sha224", + "sm3"}; + const char *keystores[] = {RNP_KEYSTORE_GPG, RNP_KEYSTORE_GPG21, RNP_KEYSTORE_KBX}; + cli_rnp_t rnp = {}; + + for (size_t i = 0; i < ARRAY_SIZE(hashAlg); i++) { + const char *keystore = keystores[i % ARRAY_SIZE(keystores)]; + /* Setting up rnp again and decrypting memory */ + printf("keystore: %s, hashalg %s\n", keystore, hashAlg[i]); + /* Generate key with specified hash algorithm */ + bool supported = hash_supported(hashAlg[i]); + assert_true(generate_test_key(keystore, hashAlg[i], hashAlg[i], NULL) == supported); + if (!supported) { + continue; + } + /* Load and check key */ + assert_true(setup_cli_rnp_common(&rnp, keystore, NULL, NULL)); + /* Loading the keyrings */ + assert_true(rnp.load_keyrings(true)); + /* Some minor checks */ + size_t keycount = 0; + assert_rnp_success(rnp_get_secret_key_count(rnp.ffi, &keycount)); + assert_true(keycount > 0); + keycount = 0; + assert_rnp_success(rnp_get_public_key_count(rnp.ffi, &keycount)); + assert_true(keycount > 0); + rnp_key_handle_t handle = NULL; + assert_rnp_success(rnp_locate_key(rnp.ffi, "userid", hashAlg[i], &handle)); + if (hash_secure(rnp.ffi, hashAlg[i], RNP_SECURITY_VERIFY_KEY)) { + assert_non_null(handle); + bool valid = false; + rnp_key_is_valid(handle, &valid); + assert_true(valid); + } else { + assert_null(handle); + } + rnp_key_handle_destroy(handle); + rnp.end(); + delete_recursively(".rnp"); + } +} + +TEST_F(rnp_tests, rnpkeys_generatekey_verifyUserIdOption) +{ + /* Set the UserId = custom value. + * Execute the Generate-key command to generate a new keypair + * Verify the key was generated with the correct UserId. */ + + const char *userIds[] = {"rnpkeys_generatekey_verifyUserIdOption_MD5", + "rnpkeys_generatekey_verifyUserIdOption_SHA-1", + "rnpkeys_generatekey_verifyUserIdOption_RIPEMD160", + "rnpkeys_generatekey_verifyUserIdOption_SHA256", + "rnpkeys_generatekey_verifyUserIdOption_SHA384", + "rnpkeys_generatekey_verifyUserIdOption_SHA512", + "rnpkeys_generatekey_verifyUserIdOption_SHA224"}; + + const char *keystores[] = {RNP_KEYSTORE_GPG, RNP_KEYSTORE_GPG21, RNP_KEYSTORE_KBX}; + cli_rnp_t rnp = {}; + + for (size_t i = 0; i < ARRAY_SIZE(userIds); i++) { + const char *keystore = keystores[i % ARRAY_SIZE(keystores)]; + /* Generate key with specified hash algorithm */ + assert_true(generate_test_key(keystore, userIds[i], "SHA256", NULL)); + + /* Initialize the basic RNP structure. */ + assert_true(setup_cli_rnp_common(&rnp, keystore, NULL, NULL)); + /* Load the newly generated rnp key*/ + assert_true(rnp.load_keyrings(true)); + size_t keycount = 0; + assert_rnp_success(rnp_get_secret_key_count(rnp.ffi, &keycount)); + assert_true(keycount > 0); + keycount = 0; + assert_rnp_success(rnp_get_public_key_count(rnp.ffi, &keycount)); + assert_true(keycount > 0); + + rnp_key_handle_t handle = NULL; + assert_rnp_success(rnp_locate_key(rnp.ffi, "userid", userIds[i], &handle)); + assert_non_null(handle); + rnp_key_handle_destroy(handle); + rnp.end(); + delete_recursively(".rnp"); + } +} + +TEST_F(rnp_tests, rnpkeys_generatekey_verifykeyHomeDirOption) +{ + /* Try to generate keypair in different home directories */ + cli_rnp_t rnp = {}; + + /* Initialize the rnp structure. */ + assert_true(setup_cli_rnp_common(&rnp, RNP_KEYSTORE_GPG, NULL, NULL)); + + /* Pubring and secring should not exist yet */ + assert_false(path_rnp_file_exists(".rnp/pubring.gpg", NULL)); + assert_false(path_rnp_file_exists(".rnp/secring.gpg", NULL)); + + /* Ensure the key was generated. */ + assert_true(generate_test_key(RNP_KEYSTORE_GPG, NULL, "SHA256", NULL)); + + /* Pubring and secring should now exist */ + assert_true(path_rnp_file_exists(".rnp/pubring.gpg", NULL)); + assert_true(path_rnp_file_exists(".rnp/secring.gpg", NULL)); + + /* Loading keyrings and checking whether they have correct key */ + assert_true(rnp.load_keyrings(true)); + size_t keycount = 0; + assert_rnp_success(rnp_get_secret_key_count(rnp.ffi, &keycount)); + assert_int_equal(keycount, 2); + keycount = 0; + assert_rnp_success(rnp_get_public_key_count(rnp.ffi, &keycount)); + assert_int_equal(keycount, 2); + + std::string userid = + fmt("RSA (Encrypt or Sign) 1024-bit key <%s@localhost>", getenv_logname()); + rnp_key_handle_t handle = NULL; + assert_rnp_success(rnp_locate_key(rnp.ffi, "userid", userid.c_str(), &handle)); + assert_non_null(handle); + rnp_key_handle_destroy(handle); + rnp.end(); + + /* Now we start over with a new home. When home is specified explicitly then it should + * include .rnp as well */ + std::string newhome = "newhome/.rnp"; + path_mkdir(0700, "newhome", NULL); + path_mkdir(0700, newhome.c_str(), NULL); + + /* Initialize the rnp structure. */ + assert_true(setup_cli_rnp_common(&rnp, RNP_KEYSTORE_GPG, newhome.c_str(), NULL)); + + /* Pubring and secring should not exist yet */ + assert_false(path_rnp_file_exists(newhome.c_str(), "pubring.gpg", NULL)); + assert_false(path_rnp_file_exists(newhome.c_str(), "secring.gpg", NULL)); + + /* Ensure the key was generated. */ + assert_true(generate_test_key(RNP_KEYSTORE_GPG, "newhomekey", "SHA256", newhome.c_str())); + + /* Pubring and secring should now exist */ + assert_true(path_rnp_file_exists(newhome.c_str(), "pubring.gpg", NULL)); + assert_true(path_rnp_file_exists(newhome.c_str(), "secring.gpg", NULL)); + + /* Loading keyrings and checking whether they have correct key */ + assert_true(rnp.load_keyrings(true)); + keycount = 0; + assert_rnp_success(rnp_get_secret_key_count(rnp.ffi, &keycount)); + assert_int_equal(keycount, 2); + keycount = 0; + assert_rnp_success(rnp_get_public_key_count(rnp.ffi, &keycount)); + assert_int_equal(keycount, 2); + + /* We should not find this key */ + assert_rnp_success(rnp_locate_key(rnp.ffi, "userid", userid.c_str(), &handle)); + assert_null(handle); + assert_rnp_success(rnp_locate_key(rnp.ffi, "userid", "newhomekey", &handle)); + assert_non_null(handle); + rnp_key_handle_destroy(handle); + rnp.end(); // Free memory and other allocated resources. +} + +TEST_F(rnp_tests, rnpkeys_generatekey_verifykeyKBXHomeDirOption) +{ + /* Try to generate keypair in different home directories for KBX keystorage */ + const char *newhome = "newhome"; + cli_rnp_t rnp = {}; + + /* Initialize the rnp structure. */ + assert_true(setup_cli_rnp_common(&rnp, RNP_KEYSTORE_KBX, NULL, NULL)); + /* Pubring and secring should not exist yet */ + assert_false(path_rnp_file_exists(".rnp/pubring.kbx", NULL)); + assert_false(path_rnp_file_exists(".rnp/secring.kbx", NULL)); + assert_false(path_rnp_file_exists(".rnp/pubring.gpg", NULL)); + assert_false(path_rnp_file_exists(".rnp/secring.gpg", NULL)); + /* Ensure the key was generated. */ + assert_true(generate_test_key(RNP_KEYSTORE_KBX, NULL, "SHA256", NULL)); + /* Pubring and secring should now exist, but only for the KBX */ + assert_true(path_rnp_file_exists(".rnp/pubring.kbx", NULL)); + assert_true(path_rnp_file_exists(".rnp/secring.kbx", NULL)); + assert_false(path_rnp_file_exists(".rnp/pubring.gpg", NULL)); + assert_false(path_rnp_file_exists(".rnp/secring.gpg", NULL)); + + /* Loading keyrings and checking whether they have correct key */ + assert_true(rnp.load_keyrings(true)); + size_t keycount = 0; + assert_rnp_success(rnp_get_secret_key_count(rnp.ffi, &keycount)); + assert_int_equal(keycount, 2); + keycount = 0; + assert_rnp_success(rnp_get_public_key_count(rnp.ffi, &keycount)); + assert_int_equal(keycount, 2); + std::string userid = + fmt("RSA (Encrypt or Sign) 1024-bit key <%s@localhost>", getenv_logname()); + rnp_key_handle_t handle = NULL; + assert_rnp_success(rnp_locate_key(rnp.ffi, "userid", userid.c_str(), &handle)); + assert_non_null(handle); + rnp_key_handle_destroy(handle); + rnp.end(); + + /* Now we start over with a new home. */ + path_mkdir(0700, newhome, NULL); + /* Initialize the rnp structure. */ + assert_true(setup_cli_rnp_common(&rnp, RNP_KEYSTORE_KBX, newhome, NULL)); + /* Pubring and secring should not exist yet */ + assert_false(path_rnp_file_exists(newhome, "pubring.kbx", NULL)); + assert_false(path_rnp_file_exists(newhome, "secring.kbx", NULL)); + assert_false(path_rnp_file_exists(newhome, "pubring.gpg", NULL)); + assert_false(path_rnp_file_exists(newhome, "secring.gpg", NULL)); + + /* Ensure the key was generated. */ + assert_true(generate_test_key(RNP_KEYSTORE_KBX, "newhomekey", "SHA256", newhome)); + /* Pubring and secring should now exist, but only for the KBX */ + assert_true(path_rnp_file_exists(newhome, "pubring.kbx", NULL)); + assert_true(path_rnp_file_exists(newhome, "secring.kbx", NULL)); + assert_false(path_rnp_file_exists(newhome, "pubring.gpg", NULL)); + assert_false(path_rnp_file_exists(newhome, "secring.gpg", NULL)); + /* Loading keyrings and checking whether they have correct key */ + assert_true(rnp.load_keyrings(true)); + keycount = 0; + assert_rnp_success(rnp_get_secret_key_count(rnp.ffi, &keycount)); + assert_int_equal(keycount, 2); + keycount = 0; + assert_rnp_success(rnp_get_public_key_count(rnp.ffi, &keycount)); + assert_int_equal(keycount, 2); + /* We should not find this key */ + assert_rnp_success(rnp_locate_key(rnp.ffi, "userid", userid.c_str(), &handle)); + assert_null(handle); + assert_rnp_success(rnp_locate_key(rnp.ffi, "userid", "newhomekey", &handle)); + assert_non_null(handle); + rnp_key_handle_destroy(handle); + rnp.end(); +} + +TEST_F(rnp_tests, rnpkeys_generatekey_verifykeyHomeDirNoPermission) +{ + const char *nopermsdir = "noperms"; + path_mkdir(0000, nopermsdir, NULL); +/* Try to generate key in the directory and make sure generation fails */ +#ifndef _WIN32 + assert_false(generate_test_key(RNP_KEYSTORE_GPG, NULL, "SHA256", nopermsdir)); +#else + /* There are no permissions for mkdir() under the Windows */ + assert_true(generate_test_key(RNP_KEYSTORE_GPG, NULL, "SHA256", nopermsdir)); +#endif +} + +static bool +ask_expert_details(cli_rnp_t *ctx, rnp_cfg &ops, const char *rsp) +{ + /* Run tests*/ + bool ret = false; + int pipefd[2] = {-1, -1}; + int user_input_pipefd[2] = {-1, -1}; + size_t rsp_len; + + if (pipe(pipefd) == -1) { + return false; + } + ops.set_int(CFG_PASSFD, pipefd[0]); + write_pass_to_pipe(pipefd[1], 2); + close(pipefd[1]); + if (!rnpkeys_init(*ctx, ops)) { + close(pipefd[0]); // otherwise will be closed via passfp + goto end; + } + + /* Write response to fd */ + if (pipe(user_input_pipefd) == -1) { + goto end; + } + rsp_len = strlen(rsp); + for (size_t i = 0; i < rsp_len;) { + i += write(user_input_pipefd[1], rsp + i, rsp_len - i); + } + close(user_input_pipefd[1]); + + /* Mock user-input*/ + ctx->cfg().set_int(CFG_USERINPUTFD, user_input_pipefd[0]); + + if (!rnp_cmd(ctx, CMD_GENERATE_KEY, NULL)) { + ret = false; + goto end; + } + ops.copy(ctx->cfg()); + ret = true; +end: + /* Close & clean fd*/ + if (user_input_pipefd[0]) { + close(user_input_pipefd[0]); + } + return ret; +} + +static bool +check_key_props(cli_rnp_t * rnp, + const char *uid, + const char *primary_alg, + const char *sub_alg, + const char *primary_curve, + const char *sub_curve, + int bits, + int sub_bits, + const char *hash) +{ + rnp_key_handle_t key = NULL; + rnp_key_handle_t subkey = NULL; + rnp_signature_handle_t sig = NULL; + uint32_t kbits = 0; + char * str = NULL; + bool res = false; + + /* check primary key properties */ + if (rnp_locate_key(rnp->ffi, "userid", uid, &key) || !key) { + return false; + } + if (rnp_key_get_alg(key, &str) || strcmp(str, primary_alg)) { + goto done; + } + rnp_buffer_destroy(str); + str = NULL; + + if (primary_curve && (rnp_key_get_curve(key, &str) || strcmp(str, primary_curve))) { + goto done; + } + rnp_buffer_destroy(str); + str = NULL; + if (bits && (rnp_key_get_bits(key, &kbits) || (bits != (int) kbits))) { + goto done; + } + + /* check subkey properties */ + if (!sub_alg) { + res = true; + goto done; + } + + if (rnp_key_get_subkey_at(key, 0, &subkey)) { + goto done; + } + + if (rnp_key_get_alg(subkey, &str) || strcmp(str, sub_alg)) { + goto done; + } + rnp_buffer_destroy(str); + str = NULL; + + if (sub_curve && (rnp_key_get_curve(subkey, &str) || strcmp(str, sub_curve))) { + goto done; + } + rnp_buffer_destroy(str); + str = NULL; + if (sub_bits && (rnp_key_get_bits(subkey, &kbits) || (sub_bits != (int) kbits))) { + goto done; + } + + if (rnp_key_get_signature_at(subkey, 0, &sig) || !sig) { + goto done; + } + if (rnp_signature_get_hash_alg(sig, &str) || strcmp(str, hash)) { + goto done; + } + + res = true; +done: + rnp_signature_handle_destroy(sig); + rnp_key_handle_destroy(key); + rnp_key_handle_destroy(subkey); + rnp_buffer_destroy(str); + return res; +} + +static bool +check_cfg_props(const rnp_cfg &cfg, + const char * primary_alg, + const char * sub_alg, + const char * primary_curve, + const char * sub_curve, + int bits, + int sub_bits) +{ + if (cfg.get_str(CFG_KG_PRIMARY_ALG) != primary_alg) { + return false; + } + if (cfg.get_str(CFG_KG_SUBKEY_ALG) != sub_alg) { + return false; + } + if (primary_curve && (cfg.get_str(CFG_KG_PRIMARY_CURVE) != primary_curve)) { + return false; + } + if (sub_curve && (cfg.get_str(CFG_KG_SUBKEY_CURVE) != sub_curve)) { + return false; + } + if (bits && (cfg.get_int(CFG_KG_PRIMARY_BITS) != bits)) { + return false; + } + if (sub_bits && (cfg.get_int(CFG_KG_SUBKEY_BITS) != sub_bits)) { + return false; + } + return true; +} + +TEST_F(rnp_tests, rnpkeys_generatekey_testExpertMode) +{ + cli_rnp_t rnp; + rnp_cfg ops; + + ops.set_bool(CFG_EXPERT, true); + ops.set_int(CFG_S2K_ITER, 1); + + /* ecdsa/ecdh p256 keypair */ + ops.unset(CFG_USERID); + ops.add_str(CFG_USERID, "expert_ecdsa_p256"); + assert_true(ask_expert_details(&rnp, ops, "19\n1\n")); + assert_false(check_cfg_props(ops, "ECDH", "ECDH", "NIST P-256", "NIST P-256", 0, 0)); + assert_false(check_cfg_props(ops, "ECDSA", "ECDSA", "NIST P-256", "NIST P-256", 0, 0)); + assert_false(check_cfg_props(ops, "ECDSA", "ECDH", "NIST P-384", "NIST P-256", 0, 0)); + assert_false(check_cfg_props(ops, "ECDSA", "ECDH", "NIST P-256", "NIST P-384", 0, 0)); + assert_false(check_cfg_props(ops, "ECDSA", "ECDH", "NIST P-256", "NIST P-256", 1024, 0)); + assert_false(check_cfg_props(ops, "ECDSA", "ECDH", "NIST P-256", "NIST P-256", 0, 1024)); + assert_true(check_cfg_props(ops, "ECDSA", "ECDH", "NIST P-256", "NIST P-256", 0, 0)); + assert_true(check_key_props( + &rnp, "expert_ecdsa_p256", "ECDSA", "ECDH", "NIST P-256", "NIST P-256", 0, 0, "SHA256")); + rnp.end(); + + /* ecdsa/ecdh p384 keypair */ + ops.unset(CFG_USERID); + ops.add_str(CFG_USERID, "expert_ecdsa_p384"); + assert_true(ask_expert_details(&rnp, ops, "19\n2\n")); + assert_true(check_cfg_props(ops, "ECDSA", "ECDH", "NIST P-384", "NIST P-384", 0, 0)); + assert_false(check_key_props( + &rnp, "expert_ecdsa_p256", "ECDSA", "ECDH", "NIST P-384", "NIST P-384", 0, 0, "SHA384")); + assert_true(check_key_props( + &rnp, "expert_ecdsa_p384", "ECDSA", "ECDH", "NIST P-384", "NIST P-384", 0, 0, "SHA384")); + rnp.end(); + + /* ecdsa/ecdh p521 keypair */ + ops.unset(CFG_USERID); + ops.add_str(CFG_USERID, "expert_ecdsa_p521"); + assert_true(ask_expert_details(&rnp, ops, "19\n3\n")); + assert_true(check_cfg_props(ops, "ECDSA", "ECDH", "NIST P-521", "NIST P-521", 0, 0)); + assert_true(check_key_props( + &rnp, "expert_ecdsa_p521", "ECDSA", "ECDH", "NIST P-521", "NIST P-521", 0, 0, "SHA512")); + rnp.end(); + + /* ecdsa/ecdh brainpool256 keypair */ + ops.unset(CFG_USERID); + ops.add_str(CFG_USERID, "expert_ecdsa_bp256"); + assert_true(ask_expert_details(&rnp, ops, "19\n4\n")); + if (brainpool_enabled()) { + assert_true( + check_cfg_props(ops, "ECDSA", "ECDH", "brainpoolP256r1", "brainpoolP256r1", 0, 0)); + assert_true(check_key_props(&rnp, + "expert_ecdsa_bp256", + "ECDSA", + "ECDH", + "brainpoolP256r1", + "brainpoolP256r1", + 0, + 0, + "SHA256")); + } else { + /* secp256k1 will be selected instead */ + assert_true(check_cfg_props(ops, "ECDSA", "ECDH", "secp256k1", "secp256k1", 0, 0)); + assert_true(check_key_props(&rnp, + "expert_ecdsa_bp256", + "ECDSA", + "ECDH", + "secp256k1", + "secp256k1", + 0, + 0, + "SHA256")); + } + rnp.end(); + + /* ecdsa/ecdh brainpool384 keypair */ + ops.unset(CFG_USERID); + ops.add_str(CFG_USERID, "expert_ecdsa_bp384"); + if (brainpool_enabled()) { + assert_true(ask_expert_details(&rnp, ops, "19\n5\n")); + assert_true( + check_cfg_props(ops, "ECDSA", "ECDH", "brainpoolP384r1", "brainpoolP384r1", 0, 0)); + assert_true(check_key_props(&rnp, + "expert_ecdsa_bp384", + "ECDSA", + "ECDH", + "brainpoolP384r1", + "brainpoolP384r1", + 0, + 0, + "SHA384")); + } else { + assert_false(ask_expert_details(&rnp, ops, "19\n5\n")); + } + rnp.end(); + + /* ecdsa/ecdh brainpool512 keypair */ + ops.unset(CFG_USERID); + ops.add_str(CFG_USERID, "expert_ecdsa_bp512"); + if (brainpool_enabled()) { + assert_true(ask_expert_details(&rnp, ops, "19\n6\n")); + assert_true( + check_cfg_props(ops, "ECDSA", "ECDH", "brainpoolP512r1", "brainpoolP512r1", 0, 0)); + assert_true(check_key_props(&rnp, + "expert_ecdsa_bp512", + "ECDSA", + "ECDH", + "brainpoolP512r1", + "brainpoolP512r1", + 0, + 0, + "SHA512")); + } else { + assert_false(ask_expert_details(&rnp, ops, "19\n6\n")); + } + rnp.end(); + + /* ecdsa/ecdh secp256k1 keypair */ + ops.unset(CFG_USERID); + ops.add_str(CFG_USERID, "expert_ecdsa_p256k1"); + if (brainpool_enabled()) { + assert_true(ask_expert_details(&rnp, ops, "19\n7\n")); + assert_true(check_cfg_props(ops, "ECDSA", "ECDH", "secp256k1", "secp256k1", 0, 0)); + assert_true(check_key_props(&rnp, + "expert_ecdsa_p256k1", + "ECDSA", + "ECDH", + "secp256k1", + "secp256k1", + 0, + 0, + "SHA256")); + } else { + assert_false(ask_expert_details(&rnp, ops, "19\n7\n")); + } + rnp.end(); + + /* eddsa/x25519 keypair */ + ops.clear(); + ops.set_bool(CFG_EXPERT, true); + ops.set_int(CFG_S2K_ITER, 1); + ops.unset(CFG_USERID); + ops.add_str(CFG_USERID, "expert_eddsa_ecdh"); + assert_true(ask_expert_details(&rnp, ops, "22\n")); + assert_true(check_cfg_props(ops, "EDDSA", "ECDH", NULL, "Curve25519", 0, 0)); + assert_true(check_key_props( + &rnp, "expert_eddsa_ecdh", "EDDSA", "ECDH", "Ed25519", "Curve25519", 0, 0, "SHA256")); + rnp.end(); + + /* rsa/rsa 1024 key */ + ops.clear(); + ops.set_bool(CFG_EXPERT, true); + ops.set_int(CFG_S2K_ITER, 1); + ops.unset(CFG_USERID); + ops.add_str(CFG_USERID, "expert_rsa_1024"); + assert_true(ask_expert_details(&rnp, ops, "1\n1024\n")); + assert_true(check_cfg_props(ops, "RSA", "RSA", NULL, NULL, 1024, 1024)); + assert_true(check_key_props( + &rnp, "expert_rsa_1024", "RSA", "RSA", NULL, NULL, 1024, 1024, "SHA256")); + rnp.end(); + + /* rsa 4096 key, asked twice */ + ops.unset(CFG_USERID); + ops.add_str(CFG_USERID, "expert_rsa_4096"); + assert_true(ask_expert_details(&rnp, ops, "1\n1023\n4096\n")); + assert_true(check_cfg_props(ops, "RSA", "RSA", NULL, NULL, 4096, 4096)); + assert_true(check_key_props( + &rnp, "expert_rsa_4096", "RSA", "RSA", NULL, NULL, 4096, 4096, "SHA256")); + rnp.end(); + + /* sm2 key */ + ops.clear(); + ops.set_bool(CFG_EXPERT, true); + ops.set_int(CFG_S2K_ITER, 1); + ops.unset(CFG_USERID); + ops.add_str(CFG_USERID, "expert_sm2"); + if (!sm2_enabled()) { + assert_false(ask_expert_details(&rnp, ops, "99\n")); + } else { + assert_true(ask_expert_details(&rnp, ops, "99\n")); + assert_true(check_cfg_props(ops, "SM2", "SM2", NULL, NULL, 0, 0)); + assert_true(check_key_props( + &rnp, "expert_sm2", "SM2", "SM2", "SM2 P-256", "SM2 P-256", 0, 0, "SM3")); + } + rnp.end(); +} + +TEST_F(rnp_tests, generatekeyECDSA_explicitlySetSmallOutputDigest_DigestAlgAdjusted) +{ + cli_rnp_t rnp; + rnp_cfg ops; + + ops.set_bool(CFG_EXPERT, true); + ops.set_str(CFG_HASH, "SHA1"); + ops.set_bool(CFG_WEAK_HASH, true); + ops.set_int(CFG_S2K_ITER, 1); + ops.add_str(CFG_USERID, "expert_small_digest"); + assert_true(ask_expert_details(&rnp, ops, "19\n2\n")); + assert_true(check_cfg_props(ops, "ECDSA", "ECDH", "NIST P-384", "NIST P-384", 0, 0)); + assert_true(check_key_props(&rnp, + "expert_small_digest", + "ECDSA", + "ECDH", + "NIST P-384", + "NIST P-384", + 0, + 0, + "SHA384")); + rnp.end(); +} + +TEST_F(rnp_tests, generatekey_multipleUserIds_ShouldFail) +{ + cli_rnp_t rnp; + rnp_cfg ops; + + ops.set_bool(CFG_EXPERT, true); + ops.set_int(CFG_S2K_ITER, 1); + ops.add_str(CFG_USERID, "multi_userid_1"); + ops.add_str(CFG_USERID, "multi_userid_2"); + assert_false(ask_expert_details(&rnp, ops, "1\n1024\n")); + rnp.end(); +} + +TEST_F(rnp_tests, generatekeyECDSA_explicitlySetBiggerThanNeededDigest_ShouldSuceed) +{ + cli_rnp_t rnp; + rnp_cfg ops; + + ops.set_bool(CFG_EXPERT, true); + ops.set_str(CFG_HASH, "SHA512"); + ops.set_int(CFG_S2K_ITER, 1); + ops.add_str(CFG_USERID, "expert_large_digest"); + assert_true(ask_expert_details(&rnp, ops, "19\n2\n")); + assert_true(check_cfg_props(ops, "ECDSA", "ECDH", "NIST P-384", "NIST P-384", 0, 0)); + assert_true(check_key_props(&rnp, + "expert_large_digest", + "ECDSA", + "ECDH", + "NIST P-384", + "NIST P-384", + 0, + 0, + "SHA512")); + rnp.end(); +} + +TEST_F(rnp_tests, generatekeyECDSA_explicitlySetUnknownDigest_ShouldFail) +{ + cli_rnp_t rnp; + rnp_cfg ops; + + ops.set_bool(CFG_EXPERT, true); + ops.set_str(CFG_HASH, "WRONG_DIGEST_ALGORITHM"); + ops.set_int(CFG_S2K_ITER, 1); + + // Finds out that hash doesn't exist and returns an error + assert_false(ask_expert_details(&rnp, ops, "19\n2\n")); + rnp.end(); +} + +/* This tests some of the mid-level key generation functions and their + * generated sigs in the keyring. + */ +TEST_F(rnp_tests, test_generated_key_sigs) +{ + rnp_key_store_t *pubring = new rnp_key_store_t(global_ctx); + rnp_key_store_t *secring = new rnp_key_store_t(global_ctx); + pgp_key_t * primary_pub = NULL, *primary_sec = NULL; + pgp_key_t * sub_pub = NULL, *sub_sec = NULL; + + // primary + { + pgp_key_t pub; + pgp_key_t sec; + rnp_keygen_primary_desc_t desc; + pgp_sig_subpkt_t * subpkt = NULL; + pgp_signature_t * psig = NULL; + pgp_signature_t * ssig = NULL; + pgp_signature_info_t psiginfo = {}; + pgp_signature_info_t ssiginfo = {}; + + desc.crypto.key_alg = PGP_PKA_RSA; + desc.crypto.rsa.modulus_bit_len = 1024; + desc.crypto.ctx = &global_ctx; + desc.cert.userid = "test"; + + // generate + assert_true(pgp_generate_primary_key(desc, true, sec, pub, PGP_KEY_STORE_GPG)); + + // add to our rings + assert_true(rnp_key_store_add_key(pubring, &pub)); + assert_true(rnp_key_store_add_key(secring, &sec)); + // retrieve back from our rings (for later) + primary_pub = rnp_tests_get_key_by_grip(pubring, pub.grip()); + primary_sec = rnp_tests_get_key_by_grip(secring, pub.grip()); + assert_non_null(primary_pub); + assert_non_null(primary_sec); + assert_true(primary_pub->valid()); + assert_true(primary_pub->validated()); + assert_false(primary_pub->expired()); + assert_true(primary_sec->valid()); + assert_true(primary_sec->validated()); + assert_false(primary_sec->expired()); + + // check packet and subsig counts + assert_int_equal(3, pub.rawpkt_count()); + assert_int_equal(3, sec.rawpkt_count()); + assert_int_equal(1, pub.sig_count()); + assert_int_equal(1, sec.sig_count()); + psig = &pub.get_sig(0).sig; + ssig = &sec.get_sig(0).sig; + // make sure our sig MPI is not NULL + assert_int_not_equal(psig->material_len, 0); + assert_int_not_equal(ssig->material_len, 0); + // make sure we're targeting the right packet + assert_int_equal(PGP_PKT_SIGNATURE, pub.get_sig(0).rawpkt.tag); + assert_int_equal(PGP_PKT_SIGNATURE, sec.get_sig(0).rawpkt.tag); + + // validate the userid self-sig + + psiginfo.sig = psig; + pub.validate_cert(psiginfo, pub.pkt(), pub.get_uid(0).pkt, global_ctx); + assert_true(psiginfo.valid); + assert_true(psig->keyfp() == pub.fp()); + // check subpackets and their contents + subpkt = psig->get_subpkt(PGP_SIG_SUBPKT_ISSUER_FPR); + assert_non_null(subpkt); + assert_true(subpkt->hashed); + subpkt = psig->get_subpkt(PGP_SIG_SUBPKT_ISSUER_KEY_ID, false); + assert_non_null(subpkt); + assert_false(subpkt->hashed); + assert_int_equal(0, + memcmp(subpkt->fields.issuer, pub.keyid().data(), PGP_KEY_ID_SIZE)); + subpkt = psig->get_subpkt(PGP_SIG_SUBPKT_CREATION_TIME); + assert_non_null(subpkt); + assert_true(subpkt->hashed); + assert_true(subpkt->fields.create <= time(NULL)); + + ssiginfo.sig = ssig; + sec.validate_cert(ssiginfo, sec.pkt(), sec.get_uid(0).pkt, global_ctx); + assert_true(ssiginfo.valid); + assert_true(ssig->keyfp() == sec.fp()); + + // modify a hashed portion of the sig packets + psig->hashed_data[32] ^= 0xff; + ssig->hashed_data[32] ^= 0xff; + // ensure validation fails + pub.validate_cert(psiginfo, pub.pkt(), pub.get_uid(0).pkt, global_ctx); + assert_false(psiginfo.valid); + sec.validate_cert(ssiginfo, sec.pkt(), sec.get_uid(0).pkt, global_ctx); + assert_false(ssiginfo.valid); + // restore the original data + psig->hashed_data[32] ^= 0xff; + ssig->hashed_data[32] ^= 0xff; + // ensure validation fails with incorrect uid + pgp_userid_pkt_t uid; + uid.tag = PGP_PKT_USER_ID; + uid.uid = (uint8_t *) malloc(4); + assert_non_null(uid.uid); + uid.uid_len = 4; + memcpy(uid.uid, "fake", 4); + + pub.validate_cert(psiginfo, pub.pkt(), uid, global_ctx); + assert_false(psiginfo.valid); + sec.validate_cert(ssiginfo, sec.pkt(), uid, global_ctx); + assert_false(ssiginfo.valid); + + // validate via an alternative method + // primary_pub + pubring + primary_pub->validate(*pubring); + assert_true(primary_pub->valid()); + assert_true(primary_pub->validated()); + assert_false(primary_pub->expired()); + // primary_sec + pubring + primary_sec->validate(*pubring); + assert_true(primary_sec->valid()); + assert_true(primary_sec->validated()); + assert_false(primary_sec->expired()); + // primary_pub + secring + primary_pub->validate(*secring); + assert_true(primary_pub->valid()); + assert_true(primary_pub->validated()); + assert_false(primary_pub->expired()); + // primary_sec + secring + primary_sec->validate(*secring); + assert_true(primary_sec->valid()); + assert_true(primary_sec->validated()); + assert_false(primary_sec->expired()); + // modify a hashed portion of the sig packet, offset may change in future + pgp_subsig_t &sig = primary_pub->get_sig(0); + sig.sig.hashed_data[10] ^= 0xff; + sig.validity.validated = false; + // ensure validation fails + primary_pub->validate(*pubring); + assert_false(primary_pub->valid()); + assert_true(primary_pub->validated()); + assert_false(primary_pub->expired()); + // restore the original data + sig.sig.hashed_data[10] ^= 0xff; + sig.validity.validated = false; + primary_pub->validate(*pubring); + assert_true(primary_pub->valid()); + assert_true(primary_pub->validated()); + assert_false(primary_pub->expired()); + } + + // sub + { + pgp_key_t pub; + pgp_key_t sec; + rnp_keygen_subkey_desc_t desc; + pgp_sig_subpkt_t * subpkt = NULL; + pgp_signature_t * psig = NULL; + pgp_signature_t * ssig = NULL; + pgp_signature_info_t psiginfo = {}; + pgp_signature_info_t ssiginfo = {}; + + memset(&desc, 0, sizeof(desc)); + desc.crypto.key_alg = PGP_PKA_RSA; + desc.crypto.rsa.modulus_bit_len = 1024; + desc.crypto.ctx = &global_ctx; + + // generate + pgp_password_provider_t prov = {}; + assert_true(pgp_generate_subkey( + desc, true, *primary_sec, *primary_pub, sec, pub, prov, PGP_KEY_STORE_GPG)); + assert_true(pub.valid()); + assert_true(pub.validated()); + assert_false(pub.expired()); + assert_true(sec.valid()); + assert_true(sec.validated()); + assert_false(sec.expired()); + + // check packet and subsig counts + assert_int_equal(2, pub.rawpkt_count()); + assert_int_equal(2, sec.rawpkt_count()); + assert_int_equal(1, pub.sig_count()); + assert_int_equal(1, sec.sig_count()); + psig = &pub.get_sig(0).sig; + ssig = &sec.get_sig(0).sig; + // make sure our sig MPI is not NULL + assert_int_not_equal(psig->material_len, 0); + assert_int_not_equal(ssig->material_len, 0); + // make sure we're targeting the right packet + assert_int_equal(PGP_PKT_SIGNATURE, pub.get_sig(0).rawpkt.tag); + assert_int_equal(PGP_PKT_SIGNATURE, sec.get_sig(0).rawpkt.tag); + // validate the binding sig + psiginfo.sig = psig; + primary_pub->validate_binding(psiginfo, pub, global_ctx); + assert_true(psiginfo.valid); + assert_true(psig->keyfp() == primary_pub->fp()); + // check subpackets and their contents + subpkt = psig->get_subpkt(PGP_SIG_SUBPKT_ISSUER_FPR); + assert_non_null(subpkt); + assert_true(subpkt->hashed); + subpkt = psig->get_subpkt(PGP_SIG_SUBPKT_ISSUER_KEY_ID, false); + assert_non_null(subpkt); + assert_false(subpkt->hashed); + assert_int_equal( + 0, memcmp(subpkt->fields.issuer, primary_pub->keyid().data(), PGP_KEY_ID_SIZE)); + subpkt = psig->get_subpkt(PGP_SIG_SUBPKT_CREATION_TIME); + assert_non_null(subpkt); + assert_true(subpkt->hashed); + assert_true(subpkt->fields.create <= time(NULL)); + + ssiginfo.sig = ssig; + primary_pub->validate_binding(ssiginfo, sec, global_ctx); + assert_true(ssiginfo.valid); + assert_true(ssig->keyfp() == primary_sec->fp()); + + // modify a hashed portion of the sig packets + psig->hashed_data[10] ^= 0xff; + ssig->hashed_data[10] ^= 0xff; + // ensure validation fails + primary_pub->validate_binding(psiginfo, pub, global_ctx); + assert_false(psiginfo.valid); + primary_pub->validate_binding(ssiginfo, sec, global_ctx); + assert_false(ssiginfo.valid); + // restore the original data + psig->hashed_data[10] ^= 0xff; + ssig->hashed_data[10] ^= 0xff; + + // add to our rings + assert_true(rnp_key_store_add_key(pubring, &pub)); + assert_true(rnp_key_store_add_key(secring, &sec)); + // retrieve back from our rings + sub_pub = rnp_tests_get_key_by_grip(pubring, pub.grip()); + sub_sec = rnp_tests_get_key_by_grip(secring, pub.grip()); + assert_non_null(sub_pub); + assert_non_null(sub_sec); + assert_true(sub_pub->valid()); + assert_true(sub_pub->validated()); + assert_false(sub_pub->expired()); + assert_true(sub_sec->valid()); + assert_true(sub_sec->validated()); + assert_false(sub_sec->expired()); + + // validate via an alternative method + sub_pub->validate(*pubring); + assert_true(sub_pub->valid()); + assert_true(sub_pub->validated()); + assert_false(sub_pub->expired()); + sub_sec->validate(*pubring); + assert_true(sub_sec->valid()); + assert_true(sub_sec->validated()); + assert_false(sub_sec->expired()); + } + + delete pubring; + delete secring; +} |