summaryrefslogtreecommitdiffstats
path: root/src/tests/generatekey.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests/generatekey.cpp')
-rw-r--r--src/tests/generatekey.cpp1243
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;
+}