summaryrefslogtreecommitdiffstats
path: root/src/tests/ffi-key-sig.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests/ffi-key-sig.cpp')
-rw-r--r--src/tests/ffi-key-sig.cpp1626
1 files changed, 1626 insertions, 0 deletions
diff --git a/src/tests/ffi-key-sig.cpp b/src/tests/ffi-key-sig.cpp
new file mode 100644
index 0000000..01bfdd2
--- /dev/null
+++ b/src/tests/ffi-key-sig.cpp
@@ -0,0 +1,1626 @@
+/*
+ * Copyright (c) 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 <sstream>
+#include <rnp/rnp.h>
+#include <librepgp/stream-ctx.h>
+#include "pgp-key.h"
+#include "ffi-priv-types.h"
+#include "rnp_tests.h"
+#include "support.h"
+
+static bool check_sig_status(json_object *sig,
+ const char * pub,
+ const char * sec,
+ const char * fp);
+
+TEST_F(rnp_tests, test_ffi_key_signatures)
+{
+ rnp_ffi_t ffi = NULL;
+
+ assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));
+ // load key
+ assert_true(load_keys_gpg(ffi, "data/test_stream_key_load/ecc-p384-pub.asc"));
+ // check primary key
+ rnp_key_handle_t key = NULL;
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "242A3AA5EA85F44A", &key));
+ // some edge cases
+ size_t sigs = 0;
+ rnp_signature_handle_t sig = NULL;
+ assert_rnp_failure(rnp_key_get_signature_count(NULL, &sigs));
+ assert_rnp_failure(rnp_key_get_signature_count(key, NULL));
+ assert_rnp_failure(rnp_key_get_signature_at(key, 0, &sig));
+ assert_rnp_failure(rnp_key_get_signature_at(key, 0x10000, &sig));
+ assert_rnp_failure(rnp_key_get_signature_at(NULL, 0x10000, &sig));
+ assert_rnp_failure(rnp_key_get_signature_at(NULL, 0, NULL));
+ // key doesn't have signatures
+ assert_rnp_success(rnp_key_get_signature_count(key, &sigs));
+ assert_int_equal(sigs, 0);
+ // uid must have one signature
+ rnp_uid_handle_t uid = NULL;
+ assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid));
+ assert_rnp_success(rnp_uid_get_signature_count(uid, &sigs));
+ assert_int_equal(sigs, 1);
+ assert_rnp_failure(rnp_uid_get_signature_at(uid, 1, &sig));
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 0, &sig));
+ char *type = NULL;
+ assert_rnp_failure(rnp_signature_get_type(NULL, &type));
+ assert_rnp_failure(rnp_signature_get_type(sig, NULL));
+ assert_rnp_success(rnp_signature_get_type(sig, &type));
+ assert_string_equal(type, "certification (positive)");
+ rnp_buffer_destroy(type);
+ uint32_t creation = 0;
+ assert_rnp_success(rnp_signature_get_creation(sig, &creation));
+ assert_int_equal(creation, 1549119505);
+ char *alg = NULL;
+ assert_rnp_failure(rnp_signature_get_alg(NULL, &alg));
+ assert_rnp_failure(rnp_signature_get_alg(sig, NULL));
+ assert_rnp_success(rnp_signature_get_alg(sig, &alg));
+ assert_string_equal(alg, "ECDSA");
+ rnp_buffer_destroy(alg);
+ assert_rnp_success(rnp_signature_get_hash_alg(sig, &alg));
+ assert_string_equal(alg, "SHA384");
+ rnp_buffer_destroy(alg);
+ char *keyid = NULL;
+ assert_rnp_success(rnp_signature_get_keyid(sig, &keyid));
+ assert_non_null(keyid);
+ assert_string_equal(keyid, "242A3AA5EA85F44A");
+ rnp_buffer_destroy(keyid);
+ char *keyfp = NULL;
+ assert_rnp_failure(rnp_signature_get_key_fprint(sig, NULL));
+ assert_rnp_failure(rnp_signature_get_key_fprint(NULL, &keyfp));
+ assert_null(keyfp);
+ assert_rnp_success(rnp_signature_get_key_fprint(sig, &keyfp));
+ assert_string_equal(keyfp, "AB25CBA042DD924C3ACC3ED3242A3AA5EA85F44A");
+ rnp_buffer_destroy(keyfp);
+ rnp_key_handle_t signer = NULL;
+ assert_rnp_success(rnp_signature_get_signer(sig, &signer));
+ assert_non_null(signer);
+ assert_rnp_success(rnp_key_get_keyid(signer, &keyid));
+ assert_non_null(keyid);
+ assert_string_equal(keyid, "242A3AA5EA85F44A");
+ rnp_buffer_destroy(keyid);
+ rnp_key_handle_destroy(signer);
+ assert_int_equal(rnp_signature_is_valid(NULL, 0), RNP_ERROR_NULL_POINTER);
+ assert_int_equal(rnp_signature_is_valid(sig, 17), RNP_ERROR_BAD_PARAMETERS);
+ assert_rnp_success(rnp_signature_is_valid(sig, 0));
+ assert_rnp_success(rnp_signature_handle_destroy(sig));
+ // subkey must have one signature
+ rnp_key_handle_t subkey = NULL;
+ assert_rnp_success(rnp_key_get_subkey_at(key, 0, &subkey));
+ assert_rnp_success(rnp_key_get_signature_count(subkey, &sigs));
+ assert_int_equal(sigs, 1);
+ assert_rnp_success(rnp_key_get_signature_at(subkey, 0, &sig));
+ // check signature export
+ rnp_output_t output = NULL;
+ assert_rnp_success(rnp_output_to_memory(&output, 0));
+ assert_rnp_failure(rnp_signature_export(NULL, output, 0));
+ assert_rnp_failure(rnp_signature_export(sig, NULL, 0));
+ assert_rnp_failure(rnp_signature_export(sig, output, 0x333));
+ assert_rnp_success(rnp_signature_export(sig, output, 0));
+ uint8_t *buf = NULL;
+ size_t len = 0;
+ assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false));
+ assert_int_equal(len, 154);
+ rnp_input_t input;
+ assert_rnp_success(rnp_input_from_memory(&input, buf, len, false));
+ char *json = NULL;
+ assert_rnp_success(rnp_import_signatures(ffi, input, 0, &json));
+ assert_non_null(json);
+ json_object *jso = json_tokener_parse(json);
+ assert_non_null(jso);
+ assert_true(json_object_is_type(jso, json_type_object));
+ json_object *jsigs = NULL;
+ assert_true(json_object_object_get_ex(jso, "sigs", &jsigs));
+ assert_true(json_object_is_type(jsigs, json_type_array));
+ assert_int_equal(json_object_array_length(jsigs), 1);
+ json_object *jsig = json_object_array_get_idx(jsigs, 0);
+ assert_true(check_sig_status(jsig, "none", "none", NULL));
+ json_object_put(jso);
+ rnp_buffer_destroy(json);
+ assert_rnp_success(rnp_input_destroy(input));
+ assert_rnp_success(rnp_output_destroy(output));
+
+ output = NULL;
+ assert_rnp_success(rnp_output_to_memory(&output, 0));
+ assert_rnp_success(rnp_signature_export(sig, output, RNP_KEY_EXPORT_ARMORED));
+ buf = NULL;
+ len = 0;
+ assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false));
+ assert_int_equal(len, 297);
+ std::string data((const char *) buf, len);
+ assert_true(starts_with(data, "-----BEGIN PGP PUBLIC KEY BLOCK-----"));
+ assert_true(ends_with(strip_eol(data), "-----END PGP PUBLIC KEY BLOCK-----"));
+
+ assert_rnp_success(rnp_input_from_memory(&input, buf, len, false));
+ assert_rnp_success(rnp_import_signatures(ffi, input, 0, NULL));
+ assert_rnp_success(rnp_input_destroy(input));
+ assert_rnp_success(rnp_output_destroy(output));
+
+ assert_rnp_success(rnp_signature_get_type(sig, &type));
+ assert_string_equal(type, "subkey binding");
+ rnp_buffer_destroy(type);
+ assert_rnp_success(rnp_signature_get_creation(sig, &creation));
+ assert_int_equal(creation, 1549119513);
+ assert_rnp_success(rnp_signature_get_alg(sig, &alg));
+ assert_string_equal(alg, "ECDSA");
+ rnp_buffer_destroy(alg);
+ assert_rnp_success(rnp_signature_get_hash_alg(sig, &alg));
+ assert_string_equal(alg, "SHA384");
+ rnp_buffer_destroy(alg);
+ assert_rnp_success(rnp_signature_get_keyid(sig, &keyid));
+ assert_non_null(keyid);
+ assert_string_equal(keyid, "242A3AA5EA85F44A");
+ rnp_buffer_destroy(keyid);
+ assert_rnp_success(rnp_signature_get_signer(sig, &signer));
+ assert_non_null(signer);
+ assert_rnp_success(rnp_key_get_keyid(signer, &keyid));
+ assert_non_null(keyid);
+ assert_string_equal(keyid, "242A3AA5EA85F44A");
+ rnp_buffer_destroy(keyid);
+ rnp_key_handle_destroy(signer);
+ rnp_key_handle_destroy(subkey);
+ assert_rnp_success(rnp_signature_handle_destroy(sig));
+ assert_rnp_success(rnp_uid_handle_destroy(uid));
+ assert_rnp_success(rnp_key_handle_destroy(key));
+
+ // check subkey which signature doesn't have issue fingerprint subpacket
+ assert_true(load_keys_gpg(ffi, "data/keyrings/1/pubring.gpg"));
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "326EF111425D14A5", &subkey));
+ assert_rnp_success(rnp_key_get_signature_count(subkey, &sigs));
+ assert_int_equal(sigs, 1);
+ assert_rnp_success(rnp_key_get_signature_at(subkey, 0, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &type));
+ assert_string_equal(type, "subkey binding");
+ rnp_buffer_destroy(type);
+ assert_rnp_success(rnp_signature_get_key_fprint(sig, &keyfp));
+ assert_null(keyfp);
+ rnp_signature_handle_destroy(sig);
+ rnp_key_handle_destroy(subkey);
+
+ // cleanup
+ rnp_ffi_destroy(ffi);
+}
+
+static bool
+check_import_sigs(rnp_ffi_t ffi, json_object **jso, json_object **sigarr, const char *sigpath)
+{
+ rnp_input_t input = NULL;
+ if (rnp_input_from_path(&input, sigpath)) {
+ return false;
+ }
+ bool res = false;
+ char *sigs = NULL;
+ *jso = NULL;
+
+ if (rnp_import_signatures(ffi, input, 0, &sigs)) {
+ goto done;
+ }
+ if (!sigs) {
+ goto done;
+ }
+
+ *jso = json_tokener_parse(sigs);
+ if (!jso) {
+ goto done;
+ }
+ if (!json_object_is_type(*jso, json_type_object)) {
+ goto done;
+ }
+ if (!json_object_object_get_ex(*jso, "sigs", sigarr)) {
+ goto done;
+ }
+ if (!json_object_is_type(*sigarr, json_type_array)) {
+ goto done;
+ }
+ res = true;
+done:
+ if (!res) {
+ json_object_put(*jso);
+ *jso = NULL;
+ }
+ rnp_input_destroy(input);
+ rnp_buffer_destroy(sigs);
+ return res;
+}
+
+static bool
+check_sig_status(json_object *sig, const char *pub, const char *sec, const char *fp)
+{
+ if (!sig) {
+ return false;
+ }
+ if (!json_object_is_type(sig, json_type_object)) {
+ return false;
+ }
+ json_object *fld = NULL;
+ if (!json_object_object_get_ex(sig, "public", &fld)) {
+ return false;
+ }
+ if (strcmp(json_object_get_string(fld), pub) != 0) {
+ return false;
+ }
+ if (!json_object_object_get_ex(sig, "secret", &fld)) {
+ return false;
+ }
+ if (strcmp(json_object_get_string(fld), sec) != 0) {
+ return false;
+ }
+ if (!fp && json_object_object_get_ex(sig, "signer fingerprint", &fld)) {
+ return false;
+ }
+ if (fp) {
+ if (!json_object_object_get_ex(sig, "signer fingerprint", &fld)) {
+ return false;
+ }
+ if (strcmp(json_object_get_string(fld), fp) != 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+TEST_F(rnp_tests, test_ffi_import_signatures)
+{
+ rnp_ffi_t ffi = NULL;
+ rnp_input_t input = NULL;
+ char * results = NULL;
+
+ assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/alice-pub.asc"));
+ /* find key and check signature count */
+ rnp_key_handle_t key_handle = NULL;
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key_handle));
+ size_t sigcount = 0;
+ assert_rnp_success(rnp_key_get_signature_count(key_handle, &sigcount));
+ assert_int_equal(sigcount, 0);
+ /* check revocation status */
+ bool revoked = false;
+ assert_rnp_success(rnp_key_is_revoked(key_handle, &revoked));
+ assert_false(revoked);
+ /* some import edge cases */
+ assert_rnp_failure(rnp_import_signatures(ffi, NULL, 0, &results));
+ assert_rnp_failure(rnp_import_signatures(NULL, input, 0, &results));
+ assert_rnp_failure(rnp_import_signatures(ffi, input, 0x18, &results));
+ /* import revocation signature */
+ json_object *jso = NULL;
+ json_object *jsosigs = NULL;
+ assert_true(
+ check_import_sigs(ffi, &jso, &jsosigs, "data/test_key_validity/alice-rev.pgp"));
+ assert_int_equal(json_object_array_length(jsosigs), 1);
+ json_object *jsosig = json_object_array_get_idx(jsosigs, 0);
+ assert_true(check_sig_status(
+ jsosig, "new", "unknown key", "73edcc9119afc8e2dbbdcde50451409669ffde3c"));
+ json_object_put(jso);
+ /* key now must become revoked */
+ assert_rnp_success(rnp_key_is_revoked(key_handle, &revoked));
+ assert_true(revoked);
+ /* check signature number - it now must be 1 */
+ assert_rnp_success(rnp_key_get_signature_count(key_handle, &sigcount));
+ assert_int_equal(sigcount, 1);
+ /* check signature type */
+ rnp_signature_handle_t sig = NULL;
+ assert_rnp_success(rnp_key_get_signature_at(key_handle, 0, &sig));
+ char *type = NULL;
+ assert_rnp_success(rnp_signature_get_type(sig, &type));
+ assert_string_equal(type, "key revocation");
+ rnp_buffer_destroy(type);
+ assert_rnp_success(rnp_signature_is_valid(sig, 0));
+ uint32_t screate = 0;
+ assert_rnp_success(rnp_signature_get_creation(sig, &screate));
+ assert_int_equal(screate, 1578663151);
+ rnp_signature_handle_destroy(sig);
+ /* check key validity */
+ bool valid = true;
+ assert_rnp_success(rnp_key_is_valid(key_handle, &valid));
+ assert_false(valid);
+ uint32_t till = 0;
+ assert_rnp_success(rnp_key_valid_till(key_handle, &till));
+ assert_int_equal(till, 1578663151);
+ /* check import with NULL results param */
+ 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));
+ /* import signature again, making sure it is not duplicated */
+ assert_true(
+ check_import_sigs(ffi, &jso, &jsosigs, "data/test_key_validity/alice-rev.pgp"));
+ assert_int_equal(json_object_array_length(jsosigs), 1);
+ jsosig = json_object_array_get_idx(jsosigs, 0);
+ assert_true(check_sig_status(
+ jsosig, "unchanged", "unknown key", "73edcc9119afc8e2dbbdcde50451409669ffde3c"));
+ json_object_put(jso);
+ /* check signature count, using the same key handle (it must not be changed) */
+ assert_rnp_success(rnp_key_get_signature_count(key_handle, &sigcount));
+ assert_int_equal(sigcount, 1);
+ rnp_key_handle_destroy(key_handle);
+ /* save and reload keyring, making sure signature is saved */
+ reload_pubring(&ffi);
+ /* find key and check sig count and revocation status */
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key_handle));
+ assert_rnp_success(rnp_key_get_signature_count(key_handle, &sigcount));
+ assert_int_equal(sigcount, 1);
+ assert_rnp_success(rnp_key_is_revoked(key_handle, &revoked));
+ assert_true(revoked);
+ assert_rnp_success(rnp_key_is_valid(key_handle, &valid));
+ assert_false(valid);
+ assert_rnp_success(rnp_key_valid_till(key_handle, &till));
+ assert_int_equal(till, 1578663151);
+ assert_rnp_success(rnp_key_handle_destroy(key_handle));
+
+ /* try to import wrong signature (certification) */
+ assert_true(
+ check_import_sigs(ffi, &jso, &jsosigs, "data/test_key_validity/alice-cert.pgp"));
+ assert_int_equal(json_object_array_length(jsosigs), 1);
+ jsosig = json_object_array_get_idx(jsosigs, 0);
+ assert_true(check_sig_status(jsosig, "none", "none", NULL));
+ json_object_put(jso);
+
+ /* try to import signature for both public and secret key */
+ assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/alice-pub.asc"));
+ assert_true(import_sec_keys(ffi, "data/test_key_validity/alice-sec.asc"));
+ assert_true(
+ check_import_sigs(ffi, &jso, &jsosigs, "data/test_key_validity/alice-rev.pgp"));
+ assert_int_equal(json_object_array_length(jsosigs), 1);
+ jsosig = json_object_array_get_idx(jsosigs, 0);
+ assert_true(
+ check_sig_status(jsosig, "new", "new", "73edcc9119afc8e2dbbdcde50451409669ffde3c"));
+ json_object_put(jso);
+
+ /* import direct-key signature (with revocation key subpacket) */
+ assert_true(
+ check_import_sigs(ffi, &jso, &jsosigs, "data/test_key_validity/alice-revoker-sig.pgp"));
+ assert_int_equal(json_object_array_length(jsosigs), 1);
+ jsosig = json_object_array_get_idx(jsosigs, 0);
+ assert_true(
+ check_sig_status(jsosig, "new", "new", "73edcc9119afc8e2dbbdcde50451409669ffde3c"));
+ json_object_put(jso);
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key_handle));
+ assert_rnp_success(rnp_key_get_signature_count(key_handle, &sigcount));
+ assert_int_equal(sigcount, 2);
+ assert_rnp_success(rnp_key_is_revoked(key_handle, &revoked));
+ assert_true(revoked);
+ assert_rnp_success(rnp_key_get_signature_at(key_handle, 1, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &type));
+ assert_string_equal(type, "direct");
+ rnp_buffer_destroy(type);
+ assert_rnp_success(rnp_signature_is_valid(sig, 0));
+ rnp_signature_handle_destroy(sig);
+ assert_rnp_success(rnp_key_handle_destroy(key_handle));
+
+ /* load two binary signatures from the file */
+ assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/alice-pub.asc"));
+
+ assert_true(
+ check_import_sigs(ffi, &jso, &jsosigs, "data/test_key_validity/alice-sigs.pgp"));
+ assert_int_equal(json_object_array_length(jsosigs), 2);
+ jsosig = json_object_array_get_idx(jsosigs, 0);
+ assert_true(check_sig_status(
+ jsosig, "new", "unknown key", "73edcc9119afc8e2dbbdcde50451409669ffde3c"));
+ jsosig = json_object_array_get_idx(jsosigs, 1);
+ assert_true(check_sig_status(
+ jsosig, "new", "unknown key", "73edcc9119afc8e2dbbdcde50451409669ffde3c"));
+ json_object_put(jso);
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key_handle));
+ assert_rnp_success(rnp_key_get_signature_count(key_handle, &sigcount));
+ assert_int_equal(sigcount, 2);
+ assert_rnp_success(rnp_key_is_revoked(key_handle, &revoked));
+ assert_true(revoked);
+ assert_rnp_success(rnp_key_handle_destroy(key_handle));
+
+ /* load two armored signatures from the single file */
+ assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET));
+ assert_true(import_sec_keys(ffi, "data/test_key_validity/alice-sec.asc"));
+
+ assert_true(
+ check_import_sigs(ffi, &jso, &jsosigs, "data/test_key_validity/alice-sigs.asc"));
+ assert_int_equal(json_object_array_length(jsosigs), 2);
+ jsosig = json_object_array_get_idx(jsosigs, 0);
+ /* when secret key is loaded then public copy is created automatically */
+ assert_true(
+ check_sig_status(jsosig, "new", "new", "73edcc9119afc8e2dbbdcde50451409669ffde3c"));
+ jsosig = json_object_array_get_idx(jsosigs, 1);
+ assert_true(
+ check_sig_status(jsosig, "new", "new", "73edcc9119afc8e2dbbdcde50451409669ffde3c"));
+ json_object_put(jso);
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key_handle));
+ assert_rnp_success(rnp_key_get_signature_count(key_handle, &sigcount));
+ assert_int_equal(sigcount, 2);
+ assert_rnp_success(rnp_key_is_revoked(key_handle, &revoked));
+ assert_true(revoked);
+ assert_rnp_success(rnp_key_handle_destroy(key_handle));
+ /* try to import signature from key file - must fail */
+ assert_rnp_success(rnp_input_from_path(&input, "data/test_key_validity/alice-sec.asc"));
+ results = NULL;
+ assert_rnp_failure(rnp_import_signatures(ffi, input, 0, &results));
+ assert_null(results);
+ assert_rnp_success(rnp_input_destroy(input));
+ /* try to import signatures from stream where second is malformed. Nothing should be
+ * imported. */
+ assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/alice-pub.asc"));
+ assert_rnp_success(
+ rnp_input_from_path(&input, "data/test_key_validity/alice-sigs-malf.pgp"));
+ results = NULL;
+ assert_rnp_failure(rnp_import_signatures(ffi, input, 0, &results));
+ assert_null(results);
+ assert_rnp_success(rnp_input_destroy(input));
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key_handle));
+ assert_rnp_success(rnp_key_get_signature_count(key_handle, &sigcount));
+ assert_int_equal(sigcount, 0);
+ assert_rnp_success(rnp_key_is_revoked(key_handle, &revoked));
+ assert_false(revoked);
+ assert_rnp_success(rnp_key_handle_destroy(key_handle));
+ assert_rnp_success(rnp_ffi_destroy(ffi));
+}
+
+TEST_F(rnp_tests, test_ffi_export_revocation)
+{
+ rnp_ffi_t ffi = 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));
+ rnp_output_t output = NULL;
+ assert_rnp_success(rnp_output_to_null(&output));
+ /* check for failure with wrong parameters */
+ assert_rnp_failure(rnp_key_export_revocation(
+ NULL, output, 0, "SHA256", "superseded", "test key revocation"));
+ assert_rnp_failure(rnp_key_export_revocation(key_handle, NULL, 0, "SHA256", NULL, NULL));
+ assert_rnp_failure(
+ rnp_key_export_revocation(key_handle, output, 0x17, "SHA256", NULL, NULL));
+ assert_rnp_failure(
+ rnp_key_export_revocation(key_handle, output, 0, "Wrong hash", NULL, NULL));
+ assert_rnp_failure(
+ rnp_key_export_revocation(key_handle, output, 0, "SHA256", "Wrong reason code", NULL));
+ assert_rnp_success(rnp_key_handle_destroy(key_handle));
+ /* check for failure with subkey */
+ assert_true(import_sec_keys(ffi, "data/test_key_validity/alice-sub-sec.pgp"));
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "DD23CEB7FEBEFF17", &key_handle));
+ assert_rnp_success(rnp_key_unlock(key_handle, "password"));
+ assert_rnp_failure(rnp_key_export_revocation(
+ key_handle, output, 0, "SHA256", "superseded", "test key revocation"));
+ assert_rnp_success(rnp_key_handle_destroy(key_handle));
+ /* try to export revocation having public key only */
+ assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_SECRET));
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key_handle));
+ assert_rnp_failure(rnp_key_export_revocation(
+ key_handle, output, 0, "SHA256", "superseded", "test key revocation"));
+ assert_rnp_success(rnp_key_handle_destroy(key_handle));
+ /* load secret key and export revocation - should succeed with correct password */
+ assert_true(import_sec_keys(ffi, "data/test_key_validity/alice-sec.asc"));
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key_handle));
+ /* wrong password - must fail */
+ assert_rnp_success(
+ rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "wrong"));
+ assert_rnp_failure(rnp_key_export_revocation(
+ key_handle, output, 0, "SHA256", "superseded", "test key revocation"));
+ assert_rnp_failure(rnp_key_export_revocation(key_handle,
+ output,
+ RNP_KEY_EXPORT_ARMORED,
+ "SHA256",
+ "superseded",
+ "test key revocation"));
+
+ /* unlocked key - must succeed */
+ assert_rnp_success(rnp_key_unlock(key_handle, "password"));
+ assert_rnp_success(rnp_key_export_revocation(key_handle, output, 0, "SHA256", NULL, NULL));
+ assert_rnp_success(rnp_output_destroy(output));
+ assert_rnp_success(rnp_output_to_path(&output, "alice-revocation.pgp"));
+ /* correct password provider - must succeed */
+ assert_rnp_success(rnp_key_lock(key_handle));
+ assert_rnp_success(
+ rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password"));
+ assert_rnp_success(rnp_key_export_revocation(
+ key_handle, output, 0, "SHA256", "superseded", "test key revocation"));
+ assert_rnp_success(rnp_output_destroy(output));
+
+ /* check that the output is binary or armored as requested */
+ std::string data = file_to_str("alice-revocation.pgp");
+ assert_false(starts_with(data, "-----BEGIN PGP PUBLIC KEY BLOCK-----"));
+ assert_false(ends_with(strip_eol(data), "-----END PGP PUBLIC KEY BLOCK-----"));
+
+ /* make sure FFI locks key back */
+ bool locked = false;
+ assert_rnp_success(rnp_key_is_locked(key_handle, &locked));
+ assert_true(locked);
+ assert_rnp_success(rnp_key_handle_destroy(key_handle));
+ /* make sure we can successfully import exported revocation */
+ json_object *jso = NULL;
+ json_object *jsosigs = NULL;
+ assert_true(check_import_sigs(ffi, &jso, &jsosigs, "alice-revocation.pgp"));
+ assert_int_equal(json_object_array_length(jsosigs), 1);
+ json_object *jsosig = json_object_array_get_idx(jsosigs, 0);
+ assert_true(
+ check_sig_status(jsosig, "new", "new", "73edcc9119afc8e2dbbdcde50451409669ffde3c"));
+ json_object_put(jso);
+ /* key now must become revoked */
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key_handle));
+ bool revoked = false;
+ assert_rnp_success(rnp_key_is_revoked(key_handle, &revoked));
+ assert_true(revoked);
+ /* check signature number - it now must be 1 */
+ size_t sigcount = 0;
+ assert_rnp_success(rnp_key_get_signature_count(key_handle, &sigcount));
+ assert_int_equal(sigcount, 1);
+ assert_rnp_success(rnp_key_handle_destroy(key_handle));
+
+ /* check signature contents */
+ pgp_source_t src = {};
+ assert_rnp_success(init_file_src(&src, "alice-revocation.pgp"));
+ pgp_signature_t sig = {};
+ assert_rnp_success(sig.parse(src));
+ src_close(&src);
+ assert_int_equal(sig.type(), PGP_SIG_REV_KEY);
+ assert_true(sig.has_subpkt(PGP_SIG_SUBPKT_REVOCATION_REASON));
+ assert_true(sig.has_keyfp());
+ assert_int_equal(sig.revocation_code(), PGP_REVOCATION_SUPERSEDED);
+ assert_string_equal(sig.revocation_reason().c_str(), "test key revocation");
+
+ assert_int_equal(rnp_unlink("alice-revocation.pgp"), 0);
+ assert_rnp_success(rnp_ffi_destroy(ffi));
+
+ /* testing armored revocation generation */
+
+ // load initial keyring
+ assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));
+ assert_true(import_sec_keys(ffi, "data/test_key_validity/alice-sec.asc"));
+
+ key_handle = NULL;
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key_handle));
+
+ // export revocation
+ assert_rnp_success(rnp_output_to_path(&output, "alice-revocation.asc"));
+ assert_rnp_success(rnp_key_unlock(key_handle, "password"));
+ assert_rnp_success(rnp_key_export_revocation(key_handle,
+ output,
+ RNP_KEY_EXPORT_ARMORED,
+ "SHA256",
+ "superseded",
+ "test key revocation"));
+ assert_rnp_success(rnp_output_destroy(output));
+ assert_rnp_success(rnp_key_handle_destroy(key_handle));
+
+ data = file_to_str("alice-revocation.asc");
+ assert_true(starts_with(data, "-----BEGIN PGP PUBLIC KEY BLOCK-----"));
+ assert_true(ends_with(strip_eol(data), "-----END PGP PUBLIC KEY BLOCK-----"));
+
+ // import it back
+ assert_true(check_import_sigs(ffi, &jso, &jsosigs, "alice-revocation.asc"));
+ assert_int_equal(json_object_array_length(jsosigs), 1);
+ jsosig = json_object_array_get_idx(jsosigs, 0);
+ assert_true(
+ check_sig_status(jsosig, "new", "new", "73edcc9119afc8e2dbbdcde50451409669ffde3c"));
+ json_object_put(jso);
+
+ // make sure that key becomes revoked
+ key_handle = NULL;
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key_handle));
+ assert_rnp_success(rnp_key_is_revoked(key_handle, &revoked));
+ assert_true(revoked);
+ assert_rnp_success(rnp_key_handle_destroy(key_handle));
+
+ assert_int_equal(rnp_unlink("alice-revocation.asc"), 0);
+ assert_rnp_success(rnp_ffi_destroy(ffi));
+}
+
+#define KEYSIG_PATH "data/test_key_validity/"
+
+TEST_F(rnp_tests, test_ffi_sig_validity)
+{
+ rnp_ffi_t ffi = NULL;
+ assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));
+
+ /* Case1:
+ * Keys: Alice [pub]
+ * Alice is signed by Basil, but without the Basil's key.
+ * Result: Alice [valid]
+ */
+ assert_true(import_pub_keys(ffi, KEYSIG_PATH "case1/pubring.gpg"));
+ rnp_key_handle_t key = NULL;
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key));
+ rnp_uid_handle_t uid = NULL;
+ assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid));
+ bool valid = false;
+ assert_rnp_success(rnp_uid_is_valid(uid, &valid));
+ assert_true(valid);
+ rnp_signature_handle_t sig = NULL;
+ /* signature 0: valid self-signature */
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 0, &sig));
+ char *sigtype = NULL;
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "certification (positive)");
+ rnp_buffer_destroy(sigtype);
+ assert_rnp_success(rnp_signature_is_valid(sig, 0));
+ rnp_signature_handle_destroy(sig);
+ /* signature 1: valid certification from Basil, but without Basil's key */
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 1, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "certification (generic)");
+ rnp_buffer_destroy(sigtype);
+ assert_int_equal(rnp_signature_is_valid(sig, 0), RNP_ERROR_KEY_NOT_FOUND);
+ /* let's load Basil's key and make sure signature is now validated and valid */
+ assert_true(import_pub_keys(ffi, KEYSIG_PATH "basil-pub.asc"));
+ assert_rnp_success(rnp_signature_is_valid(sig, 0));
+ rnp_signature_handle_destroy(sig);
+ rnp_uid_handle_destroy(uid);
+ rnp_key_handle_destroy(key);
+
+ /* Case2:
+ * Keys: Alice [pub], Basil [pub]
+ * Alice is signed by Basil, Basil is signed by Alice, but Alice's self-signature is
+ * corrupted.
+ * Result: Alice [invalid], Basil [valid]
+ */
+ assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC));
+ assert_true(import_pub_keys(ffi, KEYSIG_PATH "case2/pubring.gpg"));
+ /* Alice key */
+ /* we cannot get key by uid since uid is invalid */
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key));
+ assert_null(key);
+ /* get it via the fingerprint */
+ assert_rnp_success(
+ rnp_locate_key(ffi, "fingerprint", "73EDCC9119AFC8E2DBBDCDE50451409669FFDE3C", &key));
+ assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid));
+ assert_rnp_success(rnp_uid_is_valid(uid, &valid));
+ assert_false(valid);
+ /* signature 0: corrupted self-signature */
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 0, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "certification (positive)");
+ rnp_buffer_destroy(sigtype);
+ assert_int_equal(rnp_signature_is_valid(sig, 0), RNP_ERROR_SIGNATURE_INVALID);
+ rnp_signature_handle_destroy(sig);
+ /* signature 1: valid certification from Basil */
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 1, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "certification (generic)");
+ rnp_buffer_destroy(sigtype);
+ assert_rnp_success(rnp_signature_is_valid(sig, 0));
+ rnp_signature_handle_destroy(sig);
+ rnp_uid_handle_destroy(uid);
+ rnp_key_handle_destroy(key);
+ /* Basil key */
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Basil <basil@rnp>", &key));
+ assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid));
+ assert_rnp_success(rnp_uid_is_valid(uid, &valid));
+ assert_true(valid);
+ /* signature 0: valid self-signature */
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 0, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "certification (positive)");
+ rnp_buffer_destroy(sigtype);
+ assert_rnp_success(rnp_signature_is_valid(sig, 0));
+ rnp_signature_handle_destroy(sig);
+ /* signature 1: valid certification from Alice */
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 1, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "certification (generic)");
+ rnp_buffer_destroy(sigtype);
+ assert_rnp_success(rnp_signature_is_valid(sig, 0));
+ rnp_signature_handle_destroy(sig);
+ rnp_uid_handle_destroy(uid);
+ rnp_key_handle_destroy(key);
+
+ /* Case3:
+ * Keys: Alice [pub], Basil [pub]
+ * Alice is signed by Basil, but doesn't have self-signature
+ * Result: Alice [invalid]
+ */
+ assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC));
+ assert_true(import_pub_keys(ffi, KEYSIG_PATH "case3/pubring.gpg"));
+ /* Alice key */
+ /* cannot locate it via userid since it is invalid */
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key));
+ assert_null(key);
+ /* let's locate it via the fingerprint */
+ assert_rnp_success(
+ rnp_locate_key(ffi, "fingerprint", "73EDCC9119AFC8E2DBBDCDE50451409669FFDE3C", &key));
+ assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid));
+ assert_rnp_success(rnp_uid_is_valid(uid, &valid));
+ assert_false(valid);
+ /* signature 0: valid certification from Basil */
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 0, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "certification (generic)");
+ rnp_buffer_destroy(sigtype);
+ assert_rnp_success(rnp_signature_is_valid(sig, 0));
+ rnp_signature_handle_destroy(sig);
+ rnp_uid_handle_destroy(uid);
+ rnp_key_handle_destroy(key);
+
+ /* Case4:
+ * Keys Alice [pub, sub]
+ * Alice subkey has invalid binding signature
+ * Result: Alice [valid], Alice sub [invalid]
+ */
+ assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC));
+ assert_true(import_pub_keys(ffi, KEYSIG_PATH "case4/pubring.gpg"));
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key));
+ rnp_key_handle_t sub = NULL;
+ rnp_key_get_subkey_at(key, 0, &sub);
+ rnp_key_get_signature_at(sub, 0, &sig);
+ assert_int_equal(rnp_signature_is_valid(sig, 0), RNP_ERROR_SIGNATURE_INVALID);
+ rnp_signature_handle_destroy(sig);
+ rnp_key_handle_destroy(sub);
+ rnp_key_handle_destroy(key);
+
+ /* Case5:
+ * Keys Alice [pub, sub], Basil [pub]
+ * Alice subkey has valid binding signature, but from the key Basil
+ * Result: Alice [valid], Alice sub [invalid]
+ */
+ assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC));
+ assert_true(import_pub_keys(ffi, KEYSIG_PATH "case5/pubring.gpg"));
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key));
+ rnp_key_get_subkey_at(key, 0, &sub);
+ rnp_key_get_signature_at(sub, 0, &sig);
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "subkey binding");
+ rnp_buffer_destroy(sigtype);
+ assert_int_equal(rnp_signature_is_valid(sig, 0), RNP_ERROR_SIGNATURE_INVALID);
+ rnp_signature_handle_destroy(sig);
+ rnp_key_handle_destroy(sub);
+ rnp_key_handle_destroy(key);
+
+ /* Case6:
+ * Keys Alice [pub, sub]
+ * Key Alice has revocation signature by Alice, and subkey doesn't
+ * Result: Alice [invalid], Alice sub [invalid]
+ */
+ assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC));
+ assert_true(import_pub_keys(ffi, KEYSIG_PATH "case6/pubring.gpg"));
+ /* check revocation signature */
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key));
+ rnp_key_get_signature_at(key, 0, &sig);
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "key revocation");
+ rnp_buffer_destroy(sigtype);
+ assert_rnp_success(rnp_signature_is_valid(sig, 0));
+ rnp_signature_handle_destroy(sig);
+ /* check subkey binding */
+ rnp_key_get_subkey_at(key, 0, &sub);
+ rnp_key_get_signature_at(sub, 0, &sig);
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "subkey binding");
+ rnp_buffer_destroy(sigtype);
+ assert_rnp_success(rnp_signature_is_valid(sig, 0));
+ rnp_signature_handle_destroy(sig);
+ rnp_key_handle_destroy(sub);
+ rnp_key_handle_destroy(key);
+
+ /* Case7:
+ * Keys Alice [pub, sub]
+ * Alice subkey has revocation signature by Alice
+ * Result: Alice [valid], Alice sub [invalid]
+ */
+ assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC));
+ assert_true(import_pub_keys(ffi, KEYSIG_PATH "case7/pubring.gpg"));
+ /* check subkey revocation signature */
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key));
+ rnp_key_get_subkey_at(key, 0, &sub);
+ rnp_key_get_signature_at(sub, 0, &sig);
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "subkey revocation");
+ rnp_buffer_destroy(sigtype);
+ assert_rnp_success(rnp_signature_is_valid(sig, 0));
+ rnp_signature_handle_destroy(sig);
+ /* check subkey binding */
+ rnp_key_get_signature_at(sub, 1, &sig);
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "subkey binding");
+ rnp_buffer_destroy(sigtype);
+ assert_rnp_success(rnp_signature_is_valid(sig, 0));
+ rnp_signature_handle_destroy(sig);
+ rnp_key_handle_destroy(sub);
+ rnp_key_handle_destroy(key);
+
+ /* Case8:
+ * Keys Alice [pub, sub]
+ * Userid is stripped from the key, but it still has valid subkey binding
+ * Result: Alice [valid], Alice sub[valid]
+ */
+
+ /* not interesting for us at the moment */
+
+ /* Case9:
+ * Keys Alice [pub, sub]
+ * Alice key has two self-signatures, one which expires key and second without key
+ * expiration.
+ * Result: Alice [valid], Alice sub[valid]
+ */
+ assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC));
+ assert_true(import_pub_keys(ffi, KEYSIG_PATH "case9/pubring.gpg"));
+ /* Alice key */
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key));
+ assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid));
+ assert_rnp_success(rnp_uid_is_valid(uid, &valid));
+ assert_true(valid);
+ /* signature 0: valid certification */
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 0, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "certification (positive)");
+ rnp_buffer_destroy(sigtype);
+ assert_rnp_success(rnp_signature_is_valid(sig, 0));
+ rnp_signature_handle_destroy(sig);
+ /* signature 1: valid certification */
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 1, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "certification (positive)");
+ rnp_buffer_destroy(sigtype);
+ assert_rnp_success(rnp_signature_is_valid(sig, 0));
+ rnp_signature_handle_destroy(sig);
+ rnp_uid_handle_destroy(uid);
+ rnp_key_handle_destroy(key);
+
+ /* another case: expired certification */
+ assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC));
+ assert_true(import_pub_keys(ffi, KEYSIG_PATH "alice-expired-claus-cert.asc"));
+ assert_true(import_pub_keys(ffi, KEYSIG_PATH "claus-pub.asc"));
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key));
+ assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid));
+ assert_rnp_success(rnp_uid_is_valid(uid, &valid));
+ assert_true(valid);
+ /* signature 0: valid certification */
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 0, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "certification (positive)");
+ rnp_buffer_destroy(sigtype);
+ assert_rnp_success(rnp_signature_is_valid(sig, 0));
+ rnp_signature_handle_destroy(sig);
+ /* signature 1: expired claus's certification */
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 1, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "certification (generic)");
+ rnp_buffer_destroy(sigtype);
+ assert_int_equal(rnp_signature_is_valid(sig, 0), RNP_ERROR_SIGNATURE_EXPIRED);
+ uint32_t expires = 0;
+ assert_rnp_success(rnp_signature_get_expiration(sig, &expires));
+ assert_int_equal(expires, 86400);
+ rnp_signature_handle_destroy(sig);
+ rnp_uid_handle_destroy(uid);
+ rnp_key_handle_destroy(key);
+
+ assert_rnp_success(rnp_ffi_destroy(ffi));
+}
+
+TEST_F(rnp_tests, test_ffi_get_signature_type)
+{
+ rnp_ffi_t ffi = NULL;
+ assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));
+ assert_true(load_keys_gpg(ffi, "data/test_key_edge_cases/alice-sig-misc-values.pgp"));
+ assert_true(import_pub_keys(ffi, KEYSIG_PATH "basil-pub.asc"));
+
+ rnp_key_handle_t key = NULL;
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key));
+ rnp_uid_handle_t uid = NULL;
+ assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid));
+ rnp_signature_handle_t sig = NULL;
+ /* signature 0: valid self-signature */
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 0, &sig));
+ char *sigtype = NULL;
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "certification (positive)");
+ rnp_buffer_destroy(sigtype);
+ assert_rnp_success(rnp_signature_is_valid(sig, 0));
+ rnp_signature_handle_destroy(sig);
+ /* signature 1: valid signature by Basil */
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 1, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "certification (generic)");
+ rnp_buffer_destroy(sigtype);
+ assert_rnp_success(rnp_signature_is_valid(sig, 0));
+ rnp_signature_handle_destroy(sig);
+ /* signature 2..7: invalid signatures with misc types */
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 2, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "standalone");
+ rnp_buffer_destroy(sigtype);
+ assert_int_equal(rnp_signature_is_valid(sig, 0), RNP_ERROR_VERIFICATION_FAILED);
+ rnp_signature_handle_destroy(sig);
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 3, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "certification (persona)");
+ rnp_buffer_destroy(sigtype);
+ assert_int_equal(rnp_signature_is_valid(sig, 0), RNP_ERROR_SIGNATURE_INVALID);
+ rnp_signature_handle_destroy(sig);
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 4, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "certification (casual)");
+ rnp_buffer_destroy(sigtype);
+ assert_int_equal(rnp_signature_is_valid(sig, 0), RNP_ERROR_SIGNATURE_INVALID);
+ rnp_signature_handle_destroy(sig);
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 5, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "primary key binding");
+ rnp_buffer_destroy(sigtype);
+ assert_int_equal(rnp_signature_is_valid(sig, 0), RNP_ERROR_VERIFICATION_FAILED);
+ rnp_signature_handle_destroy(sig);
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 6, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "certification revocation");
+ rnp_buffer_destroy(sigtype);
+ assert_int_equal(rnp_signature_is_valid(sig, 0), RNP_ERROR_SIGNATURE_INVALID);
+ rnp_signature_handle_destroy(sig);
+
+ rnp_uid_handle_destroy(uid);
+ rnp_key_handle_destroy(key);
+
+ assert_rnp_success(rnp_ffi_destroy(ffi));
+}
+
+TEST_F(rnp_tests, test_ffi_remove_signature)
+{
+ rnp_ffi_t ffi = NULL;
+ assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));
+ assert_true(load_keys_gpg(ffi, "data/test_key_edge_cases/alice-sig-misc-values.pgp"));
+ assert_true(import_pub_keys(ffi, KEYSIG_PATH "basil-pub.asc"));
+
+ rnp_key_handle_t key = NULL;
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key));
+ size_t count = 255;
+ assert_rnp_success(rnp_key_get_signature_count(key, &count));
+ assert_int_equal(count, 0);
+ rnp_key_handle_t bkey = NULL;
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Basil <basil@rnp>", &bkey));
+ count = 255;
+ assert_rnp_success(rnp_key_get_signature_count(bkey, &count));
+ assert_int_equal(count, 0);
+
+ rnp_uid_handle_t uid = NULL;
+ assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid));
+ rnp_signature_handle_t sig = NULL;
+ assert_rnp_success(rnp_uid_get_signature_count(uid, &count));
+ assert_int_equal(count, 8);
+ /* signature 1: valid signature by Basil */
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 1, &sig));
+ assert_rnp_failure(rnp_signature_remove(NULL, sig));
+ assert_rnp_failure(rnp_signature_remove(key, NULL));
+ /* attempt to delete signature from the wrong key */
+ assert_int_equal(rnp_signature_remove(bkey, sig), RNP_ERROR_NO_SIGNATURES_FOUND);
+ assert_rnp_success(rnp_signature_remove(key, sig));
+ rnp_signature_handle_destroy(sig);
+ assert_rnp_success(rnp_uid_get_signature_count(uid, &count));
+ assert_int_equal(count, 7);
+ /* signature 2: must be moved to position 1 */
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 1, &sig));
+ char *sigtype = NULL;
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "standalone");
+ rnp_buffer_destroy(sigtype);
+ assert_rnp_success(rnp_signature_remove(key, sig));
+ rnp_signature_handle_destroy(sig);
+ assert_rnp_success(rnp_uid_get_signature_count(uid, &count));
+ assert_int_equal(count, 6);
+ /* signature 7: must be moved to position 5 */
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 5, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "third-party");
+ rnp_buffer_destroy(sigtype);
+ assert_rnp_success(rnp_signature_remove(key, sig));
+ rnp_signature_handle_destroy(sig);
+ assert_rnp_success(rnp_uid_get_signature_count(uid, &count));
+ assert_int_equal(count, 5);
+ /* check that key and userid are still valid */
+ bool valid = false;
+ assert_rnp_success(rnp_key_is_valid(key, &valid));
+ assert_true(valid);
+ valid = false;
+ assert_rnp_success(rnp_uid_is_valid(uid, &valid));
+ assert_true(valid);
+ rnp_uid_handle_destroy(uid);
+ rnp_key_handle_destroy(key);
+ rnp_key_handle_destroy(bkey);
+
+ /* Export key and reload */
+ reload_pubring(&ffi);
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key));
+ count = 255;
+ assert_rnp_success(rnp_key_get_signature_count(key, &count));
+ assert_int_equal(count, 0);
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Basil <basil@rnp>", &bkey));
+ count = 255;
+ assert_rnp_success(rnp_key_get_signature_count(bkey, &count));
+ assert_int_equal(count, 0);
+ assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid));
+ assert_rnp_success(rnp_uid_get_signature_count(uid, &count));
+ assert_int_equal(count, 5);
+ /* delete self-certification and make sure that key/uid become invalid */
+ valid = false;
+ assert_rnp_success(rnp_key_is_valid(key, &valid));
+ assert_true(valid);
+ valid = false;
+ assert_rnp_success(rnp_uid_is_valid(uid, &valid));
+ assert_true(valid);
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 0, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "certification (positive)");
+ rnp_buffer_destroy(sigtype);
+ assert_rnp_success(rnp_signature_remove(key, sig));
+ rnp_signature_handle_destroy(sig);
+ valid = true;
+ assert_rnp_success(rnp_key_is_valid(key, &valid));
+ assert_false(valid);
+ valid = true;
+ assert_rnp_success(rnp_uid_is_valid(uid, &valid));
+ assert_false(valid);
+ rnp_uid_handle_destroy(uid);
+ rnp_key_handle_destroy(key);
+ rnp_key_handle_destroy(bkey);
+
+ /* Remove subkey's signature */
+ assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC));
+ assert_true(load_keys_gpg(ffi, "data/test_key_validity/case7/pubring.gpg"));
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key));
+ count = 0;
+ assert_rnp_success(rnp_key_get_subkey_count(key, &count));
+ assert_int_equal(count, 1);
+ rnp_key_handle_t sub = NULL;
+ assert_rnp_success(rnp_key_get_subkey_at(key, 0, &sub));
+ count = 255;
+ assert_rnp_success(rnp_key_get_signature_count(sub, &count));
+ assert_int_equal(count, 2);
+ /* check whether key and sub valid: [true, false] since sub is revoked */
+ valid = false;
+ assert_rnp_success(rnp_key_is_valid(key, &valid));
+ assert_true(valid);
+ assert_rnp_success(rnp_key_is_valid(sub, &valid));
+ assert_false(valid);
+ assert_rnp_success(rnp_key_get_signature_at(sub, 0, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "subkey revocation");
+ rnp_buffer_destroy(sigtype);
+ assert_rnp_success(rnp_signature_remove(sub, sig));
+ rnp_signature_handle_destroy(sig);
+ /* now subkey must become valid with 1 signature */
+ assert_rnp_success(rnp_key_get_signature_count(sub, &count));
+ assert_int_equal(count, 1);
+ valid = false;
+ assert_rnp_success(rnp_key_is_valid(sub, &valid));
+ assert_true(valid);
+ rnp_key_handle_destroy(key);
+ rnp_key_handle_destroy(sub);
+ /* reload keys */
+ reload_pubring(&ffi);
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key));
+ assert_rnp_success(rnp_key_get_subkey_at(key, 0, &sub));
+ valid = false;
+ assert_rnp_success(rnp_key_is_valid(sub, &valid));
+ assert_true(valid);
+ assert_rnp_success(rnp_key_get_signature_at(sub, 0, &sig));
+ assert_rnp_success(rnp_signature_get_type(sig, &sigtype));
+ assert_string_equal(sigtype, "subkey binding");
+ rnp_buffer_destroy(sigtype);
+ assert_rnp_failure(rnp_signature_remove(key, sig));
+ assert_rnp_success(rnp_signature_remove(sub, sig));
+ rnp_signature_handle_destroy(sig);
+ assert_rnp_success(rnp_key_is_valid(sub, &valid));
+ assert_false(valid);
+ assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid));
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 0, &sig));
+ assert_rnp_failure(rnp_signature_remove(sub, sig));
+ assert_rnp_success(rnp_signature_remove(key, sig));
+ rnp_signature_handle_destroy(sig);
+ rnp_uid_handle_destroy(uid);
+ rnp_key_handle_destroy(key);
+ rnp_key_handle_destroy(sub);
+ /* save and reload keys without sigs */
+ reload_pubring(&ffi);
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669ffde3c", &key));
+ count = 255;
+ assert_rnp_success(rnp_key_get_signature_count(key, &count));
+ assert_int_equal(count, 0);
+ assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid));
+ count = 255;
+ assert_rnp_success(rnp_uid_get_signature_count(uid, &count));
+ assert_int_equal(count, 0);
+ assert_rnp_success(rnp_key_get_subkey_at(key, 0, &sub));
+ count = 255;
+ assert_rnp_success(rnp_key_get_signature_count(sub, &count));
+ assert_int_equal(count, 0);
+ rnp_uid_handle_destroy(uid);
+ rnp_key_handle_destroy(key);
+ rnp_key_handle_destroy(sub);
+
+ /* Remove signature from the secret key/subkey */
+ assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC));
+ assert_true(load_keys_gpg(ffi,
+ "data/test_key_validity/alice-sub-pub.pgp",
+ "data/test_key_validity/alice-sub-sec.pgp"));
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "Alice <alice@rnp>", &key));
+ /* make sure they are actually secret */
+ bool secret = false;
+ assert_rnp_success(rnp_key_have_secret(key, &secret));
+ assert_true(secret);
+ assert_rnp_success(rnp_key_get_subkey_at(key, 0, &sub));
+ assert_rnp_success(rnp_key_have_secret(sub, &secret));
+ assert_true(secret);
+ /* remove both signatures and reload */
+ assert_rnp_success(rnp_key_get_signature_at(sub, 0, &sig));
+ assert_rnp_success(rnp_signature_remove(sub, sig));
+ rnp_signature_handle_destroy(sig);
+ assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid));
+ assert_rnp_success(rnp_uid_get_signature_at(uid, 0, &sig));
+ assert_rnp_success(rnp_signature_remove(key, sig));
+ rnp_signature_handle_destroy(sig);
+ rnp_uid_handle_destroy(uid);
+ rnp_key_handle_destroy(key);
+ rnp_key_handle_destroy(sub);
+ reload_keyrings(&ffi);
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669ffde3c", &key));
+ secret = false;
+ assert_rnp_success(rnp_key_have_secret(key, &secret));
+ assert_true(secret);
+ count = 255;
+ assert_rnp_success(rnp_key_get_signature_count(key, &count));
+ assert_int_equal(count, 0);
+ assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid));
+ count = 255;
+ assert_rnp_success(rnp_uid_get_signature_count(uid, &count));
+ assert_int_equal(count, 0);
+ assert_rnp_success(rnp_key_get_subkey_at(key, 0, &sub));
+ secret = false;
+ assert_rnp_success(rnp_key_have_secret(sub, &secret));
+ assert_true(secret);
+ count = 255;
+ assert_rnp_success(rnp_key_get_signature_count(sub, &count));
+ assert_int_equal(count, 0);
+ rnp_uid_handle_destroy(uid);
+ rnp_key_handle_destroy(key);
+ rnp_key_handle_destroy(sub);
+
+ assert_rnp_success(rnp_ffi_destroy(ffi));
+}
+
+static std::string
+key_info(rnp_key_handle_t key)
+{
+ bool sec = false;
+ rnp_key_have_secret(key, &sec);
+ bool primary = false;
+ rnp_key_is_primary(key, &primary);
+ std::string res = ":";
+ res += primary ? (sec ? "sec" : "pub") : (sec ? "ssb" : "sub");
+ char *keyid = NULL;
+ rnp_key_get_keyid(key, &keyid);
+ res += std::string("(") + std::string(keyid, 4) + std::string(")");
+ rnp_buffer_destroy(keyid);
+ return res;
+}
+
+static std::string
+sig_info(rnp_signature_handle_t sig)
+{
+ int type = sig->sig->sig.type();
+ char * keyid = NULL;
+ uint32_t sigid = sig->sig->sigid[0] + (sig->sig->sigid[1] << 8);
+ rnp_signature_get_keyid(sig, &keyid);
+ std::stringstream ss;
+ ss << ":sig(" << type << ", " << std::hex << sigid << ", " << std::string(keyid, 4) << ")";
+ rnp_buffer_destroy(keyid);
+ return ss.str();
+}
+
+static std::string
+uid_info(rnp_uid_handle_t uid)
+{
+ std::string res;
+ uint32_t type = 0;
+ rnp_uid_get_type(uid, &type);
+ if (type == RNP_USER_ATTR) {
+ res = ":uid(photo)";
+ } else {
+ char * uidstr = NULL;
+ size_t len = 0;
+ rnp_uid_get_data(uid, (void **) &uidstr, &len);
+ res = ":uid(" + std::string(uidstr, uidstr + len) + ")";
+ rnp_buffer_destroy(uidstr);
+ }
+
+ size_t sigs = 0;
+ rnp_uid_get_signature_count(uid, &sigs);
+ for (size_t i = 0; i < sigs; i++) {
+ rnp_signature_handle_t sig = NULL;
+ rnp_uid_get_signature_at(uid, i, &sig);
+ res += sig_info(sig);
+ rnp_signature_handle_destroy(sig);
+ }
+ return res;
+}
+
+static std::string
+key_packets(rnp_key_handle_t key)
+{
+ std::string res = key_info(key);
+ size_t sigs = 0;
+ rnp_key_get_signature_count(key, &sigs);
+ for (size_t i = 0; i < sigs; i++) {
+ rnp_signature_handle_t sig = NULL;
+ rnp_key_get_signature_at(key, i, &sig);
+ res += sig_info(sig);
+ rnp_signature_handle_destroy(sig);
+ }
+
+ bool primary = false;
+ rnp_key_is_primary(key, &primary);
+ if (!primary) {
+ return res;
+ }
+
+ size_t uids = 0;
+ rnp_key_get_uid_count(key, &uids);
+ for (size_t i = 0; i < uids; i++) {
+ rnp_uid_handle_t uid = NULL;
+ rnp_key_get_uid_handle_at(key, i, &uid);
+ res += uid_info(uid);
+ rnp_uid_handle_destroy(uid);
+ }
+
+ size_t subs = 0;
+ rnp_key_get_subkey_count(key, &subs);
+ for (size_t i = 0; i < subs; i++) {
+ rnp_key_handle_t sub = NULL;
+ rnp_key_get_subkey_at(key, i, &sub);
+ res += key_packets(sub);
+ rnp_key_handle_destroy(sub);
+ }
+ return res;
+}
+
+static void
+sigremove_leave(rnp_ffi_t ffi, void *app_ctx, rnp_signature_handle_t sig, uint32_t *action)
+{
+ assert_true((*(int *) app_ctx) == 48);
+ assert_non_null(sig);
+ assert_non_null(action);
+ assert_non_null(ffi);
+ *action = RNP_KEY_SIGNATURE_KEEP;
+}
+
+static void
+sigremove_unchanged(rnp_ffi_t ffi, void *app_ctx, rnp_signature_handle_t sig, uint32_t *action)
+{
+ assert_true((*(int *) app_ctx) == 48);
+ assert_non_null(sig);
+ assert_non_null(action);
+ assert_non_null(ffi);
+}
+
+static void
+sigremove_remove(rnp_ffi_t ffi, void *app_ctx, rnp_signature_handle_t sig, uint32_t *action)
+{
+ assert_true((*(int *) app_ctx) == 48);
+ *action = RNP_KEY_SIGNATURE_REMOVE;
+}
+
+static void
+sigremove_revocation(rnp_ffi_t ffi,
+ void * app_ctx,
+ rnp_signature_handle_t sig,
+ uint32_t * action)
+{
+ assert_true((*(int *) app_ctx) == 48);
+ char *type = NULL;
+ assert_rnp_success(rnp_signature_get_type(sig, &type));
+ if (std::string(type).find("revocation") != std::string::npos) {
+ *action = RNP_KEY_SIGNATURE_REMOVE;
+ } else {
+ *action = RNP_KEY_SIGNATURE_KEEP;
+ }
+ rnp_buffer_destroy(type);
+}
+
+TEST_F(rnp_tests, test_ffi_remove_signatures)
+{
+ rnp_ffi_t ffi = NULL;
+ assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));
+ /* case 1: key Alice with self-signature and certification from the Basil. */
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case1/pubring.gpg"));
+ rnp_key_handle_t key = NULL;
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669ffde3c", &key));
+ assert_string_equal(
+ key_packets(key).c_str(),
+ ":pub(0451):uid(Alice <alice@rnp>):sig(19, 8ba5, 0451):sig(16, de9d, 0B2B)");
+ /* rnp_key_remove_signatures corner cases */
+ assert_rnp_failure(rnp_key_remove_signatures(NULL, RNP_KEY_SIGNATURE_INVALID, NULL, NULL));
+ assert_rnp_failure(rnp_key_remove_signatures(NULL, 0, NULL, NULL));
+ assert_rnp_failure(rnp_key_remove_signatures(key, 0, NULL, ffi));
+ /* remove unknown signatures */
+ assert_rnp_success(
+ rnp_key_remove_signatures(key, RNP_KEY_SIGNATURE_UNKNOWN_KEY, NULL, NULL));
+ /* signature is deleted since we don't have Basil's key in the keyring */
+ assert_string_equal(key_packets(key).c_str(),
+ ":pub(0451):uid(Alice <alice@rnp>):sig(19, 8ba5, 0451)");
+ /* let's load key and try again */
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case1/pubring.gpg"));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/basil-pub.asc"));
+ assert_rnp_success(
+ rnp_key_remove_signatures(key, RNP_KEY_SIGNATURE_UNKNOWN_KEY, NULL, NULL));
+ /* now it is not removed */
+ assert_string_equal(
+ key_packets(key).c_str(),
+ ":pub(0451):uid(Alice <alice@rnp>):sig(19, 8ba5, 0451):sig(16, de9d, 0B2B)");
+ /* let's delete non-self sigs */
+ assert_rnp_success(
+ rnp_key_remove_signatures(key, RNP_KEY_SIGNATURE_NON_SELF_SIG, NULL, NULL));
+ assert_string_equal(key_packets(key).c_str(),
+ ":pub(0451):uid(Alice <alice@rnp>):sig(19, 8ba5, 0451)");
+ rnp_key_handle_destroy(key);
+ /* case 2: alice with corrupted self-signature */
+ assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case2/pubring.gpg"));
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669ffde3c", &key));
+ assert_string_equal(
+ key_packets(key).c_str(),
+ ":pub(0451):uid(Alice <alice@rnp>):sig(19, e530, 0451):sig(16, 2508, 0B2B)");
+ /* remove invalid signature */
+ assert_rnp_success(rnp_key_remove_signatures(key, RNP_KEY_SIGNATURE_INVALID, NULL, NULL));
+ assert_string_equal(key_packets(key).c_str(),
+ ":pub(0451):uid(Alice <alice@rnp>):sig(16, 2508, 0B2B)");
+ /* remove both invalid and non-self signatures */
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case2/pubring.gpg"));
+ assert_rnp_success(rnp_key_remove_signatures(
+ key, RNP_KEY_SIGNATURE_INVALID | RNP_KEY_SIGNATURE_NON_SELF_SIG, NULL, NULL));
+ assert_string_equal(key_packets(key).c_str(), ":pub(0451):uid(Alice <alice@rnp>)");
+
+ rnp_key_handle_destroy(key);
+ assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC));
+ /* load both keyrings and remove Basil's key */
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case1/pubring.gpg"));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case2/pubring.gpg"));
+
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "0B2B09F7D7EA6E0E", &key));
+ assert_string_equal(
+ key_packets(key).c_str(),
+ ":pub(0B2B):uid(Basil <basil@rnp>):sig(19, f083, 0B2B):sig(16, a7cd, 0451)");
+
+ assert_rnp_success(rnp_key_remove(key, RNP_KEY_REMOVE_PUBLIC | RNP_KEY_REMOVE_SUBKEYS));
+ rnp_key_handle_destroy(key);
+
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669FFDE3C", &key));
+ assert_string_equal(key_packets(key).c_str(),
+ ":pub(0451):uid(Alice <alice@rnp>):sig(19, 8ba5, 0451):sig(16, de9d, "
+ "0B2B):sig(19, e530, 0451):sig(16, 2508, 0B2B)");
+ assert_rnp_success(rnp_key_remove_signatures(
+ key, RNP_KEY_SIGNATURE_INVALID | RNP_KEY_SIGNATURE_UNKNOWN_KEY, NULL, NULL));
+ assert_string_equal(key_packets(key).c_str(),
+ ":pub(0451):uid(Alice <alice@rnp>):sig(19, 8ba5, 0451)");
+ rnp_key_handle_destroy(key);
+ assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC));
+
+ /* case 4: alice key with invalid subkey bindings (corrupted and non-self) */
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case4/pubring.gpg"));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case5/pubring.gpg"));
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669FFDE3C", &key));
+ assert_string_equal(key_packets(key).c_str(),
+ ":pub(0451):uid(Alice <alice@rnp>):sig(19, 8ba5, "
+ "0451):sub(DD23):sig(24, 89c0, 0451):sig(24, 1f6d, 0B2B)");
+ assert_rnp_success(rnp_key_remove_signatures(key, RNP_KEY_SIGNATURE_INVALID, NULL, NULL));
+ assert_string_equal(key_packets(key).c_str(),
+ ":pub(0451):uid(Alice <alice@rnp>):sig(19, 8ba5, 0451):sub(DD23)");
+ /* make sure non-self doesn't touch invalid self sigs */
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case4/pubring.gpg"));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case5/pubring.gpg"));
+ assert_string_equal(key_packets(key).c_str(),
+ ":pub(0451):uid(Alice <alice@rnp>):sig(19, 8ba5, "
+ "0451):sub(DD23):sig(24, 89c0, 0451):sig(24, 1f6d, 0B2B)");
+ assert_rnp_success(
+ rnp_key_remove_signatures(key, RNP_KEY_SIGNATURE_NON_SELF_SIG, NULL, NULL));
+ assert_string_equal(
+ key_packets(key).c_str(),
+ ":pub(0451):uid(Alice <alice@rnp>):sig(19, 8ba5, 0451):sub(DD23):sig(24, 89c0, 0451)");
+ /* add subkey with valid subkey binding */
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case4/pubring.gpg"));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case5/pubring.gpg"));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case8/pubring.gpg"));
+ assert_string_equal(
+ key_packets(key).c_str(),
+ ":pub(0451):uid(Alice <alice@rnp>):sig(19, 8ba5, 0451):sub(DD23):sig(24, 89c0, "
+ "0451):sig(24, 1f6d, 0B2B):sub(22F3):sig(24, 3766, 0451)");
+ assert_rnp_success(rnp_key_remove_signatures(key, RNP_KEY_SIGNATURE_INVALID, NULL, NULL));
+ assert_string_equal(key_packets(key).c_str(),
+ ":pub(0451):uid(Alice <alice@rnp>):sig(19, 8ba5, "
+ "0451):sub(DD23):sub(22F3):sig(24, 3766, 0451)");
+
+ /* load more keys and signatures and check callback usage */
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case1/pubring.gpg"));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case2/pubring.gpg"));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case5/pubring.gpg"));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case6/pubring.gpg"));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case7/pubring.gpg"));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case8/pubring.gpg"));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case9/pubring.gpg"));
+ assert_string_equal(
+ key_packets(key).c_str(),
+ ":pub(0451):sig(32, c76f, 0451):uid(Alice <alice@rnp>):sig(19, 8ba5, 0451):sig(16, "
+ "de9d, 0B2B):sig(19, e530, 0451):sig(16, 2508, 0B2B):sig(19, b22f, 0451):sig(19, 6cd1, "
+ "0451):sub(DD23):sig(24, 1f6d, 0B2B):sig(24, ea55, 0451):sig(40, f001, "
+ "0451):sub(22F3):sig(24, 3766, 0451)");
+ int param = 48;
+ assert_rnp_success(rnp_key_remove_signatures(key,
+ RNP_KEY_SIGNATURE_INVALID |
+ RNP_KEY_SIGNATURE_UNKNOWN_KEY |
+ RNP_KEY_SIGNATURE_NON_SELF_SIG,
+ sigremove_leave,
+ &param));
+ assert_string_equal(
+ key_packets(key).c_str(),
+ ":pub(0451):sig(32, c76f, 0451):uid(Alice <alice@rnp>):sig(19, 8ba5, 0451):sig(16, "
+ "de9d, 0B2B):sig(19, e530, 0451):sig(16, 2508, 0B2B):sig(19, b22f, 0451):sig(19, 6cd1, "
+ "0451):sub(DD23):sig(24, 1f6d, 0B2B):sig(24, ea55, 0451):sig(40, f001, "
+ "0451):sub(22F3):sig(24, 3766, 0451)");
+
+ assert_rnp_success(
+ rnp_key_remove_signatures(key, RNP_KEY_SIGNATURE_INVALID, sigremove_unchanged, &param));
+ assert_string_equal(
+ key_packets(key).c_str(),
+ ":pub(0451):sig(32, c76f, 0451):uid(Alice <alice@rnp>):sig(19, 8ba5, 0451):sig(16, "
+ "de9d, 0B2B):sig(16, 2508, 0B2B):sig(19, b22f, 0451):sig(19, 6cd1, "
+ "0451):sub(DD23):sig(24, ea55, 0451):sig(40, f001, 0451):sub(22F3):sig(24, 3766, 0451)");
+
+ assert_rnp_success(rnp_key_remove_signatures(
+ key, RNP_KEY_SIGNATURE_NON_SELF_SIG, sigremove_revocation, &param));
+ assert_string_equal(key_packets(key).c_str(),
+ ":pub(0451):uid(Alice <alice@rnp>):sig(19, 8ba5, 0451):sig(16, de9d, "
+ "0B2B):sig(16, 2508, 0B2B):sig(19, b22f, 0451):sig(19, 6cd1, "
+ "0451):sub(DD23):sig(24, ea55, 0451):sub(22F3):sig(24, 3766, 0451)");
+
+ /* make sure that signature will be removed from the secret key as well */
+ rnp_key_handle_destroy(key);
+ assert_true(import_sec_keys(ffi, "data/test_key_validity/alice-sign-sub-sec.pgp"));
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669FFDE3C", &key));
+ assert_string_equal(key_packets(key).c_str(),
+ ":sec(0451):uid(Alice <alice@rnp>):sig(19, 8ba5, 0451):sig(16, de9d, "
+ "0B2B):sig(16, 2508, 0B2B):sig(19, b22f, 0451):sig(19, 6cd1, "
+ "0451):sub(DD23):sig(24, ea55, 0451):ssb(22F3):sig(24, 3766, 0451)");
+ assert_rnp_success(
+ rnp_key_remove_signatures(key, RNP_KEY_SIGNATURE_INVALID, sigremove_remove, &param));
+ assert_string_equal(key_packets(key).c_str(),
+ ":sec(0451):uid(Alice <alice@rnp>):sub(DD23):ssb(22F3)");
+ rnp_key_handle_destroy(key);
+
+ /* reload keyring, making sure changes are saved */
+ reload_keyrings(&ffi);
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669FFDE3C", &key));
+ assert_string_equal(key_packets(key).c_str(),
+ ":sec(0451):uid(Alice <alice@rnp>):sub(DD23):ssb(22F3)");
+ rnp_key_handle_destroy(key);
+ /* load data and delete signatures on subkey */
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case6/pubring.gpg"));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case7/pubring.gpg"));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case8/pubring.gpg"));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/case9/pubring.gpg"));
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "DD23CEB7FEBEFF17", &key));
+ assert_string_equal(key_packets(key).c_str(),
+ ":sub(DD23):sig(24, ea55, 0451):sig(40, f001, 0451)");
+ assert_rnp_success(rnp_key_remove_signatures(key, 0, sigremove_revocation, &param));
+ assert_string_equal(key_packets(key).c_str(), ":sub(DD23):sig(24, ea55, 0451)");
+ rnp_key_handle_destroy(key);
+ assert_rnp_success(rnp_ffi_destroy(ffi));
+}
+
+TEST_F(rnp_tests, test_ffi_rsa_small_sig)
+{
+ rnp_ffi_t ffi = NULL;
+ assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));
+ assert_true(import_pub_keys(ffi, "data/test_key_validity/rsa_key_small_sig-pub.asc"));
+ rnp_key_handle_t key = NULL;
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "ED23B0105947F283", &key));
+ rnp_uid_handle_t uid = NULL;
+ assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid));
+ bool valid = false;
+ assert_rnp_success(rnp_uid_is_valid(uid, &valid));
+ assert_true(valid);
+ rnp_uid_handle_destroy(uid);
+ rnp_key_handle_destroy(key);
+ assert_rnp_success(rnp_ffi_destroy(ffi));
+}
+
+TEST_F(rnp_tests, test_ffi_key_critical_notations)
+{
+ rnp_ffi_t ffi = NULL;
+ assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));
+
+ /* Load key with 2 unknown critical notations in certification */
+ assert_true(import_all_keys(ffi, "data/test_key_edge_cases/key-critical-notations.pgp"));
+ rnp_key_handle_t key = NULL;
+ /* key is valid since it has valid subkey binding, but userid is not valid */
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "critical-key", &key));
+ assert_null(key);
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "ddc610bb7b8f689c", &key));
+ assert_non_null(key);
+ assert_true(check_key_valid(key, true));
+ /* uid is not valid, as certification has unknown critical notation */
+ assert_true(check_uid_valid(key, 0, false));
+ assert_true(check_sub_valid(key, 0, true));
+ rnp_key_handle_destroy(key);
+ rnp_ffi_destroy(ffi);
+
+ assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));
+ /* Load key with unknown critical notations in both certification and binding */
+ assert_true(import_all_keys(ffi, "data/test_key_edge_cases/key-sub-crit-note-pub.pgp"));
+ /* key is not valid, as well as sub and uid */
+ assert_rnp_success(rnp_locate_key(ffi, "userid", "critical_notation", &key));
+ assert_null(key);
+ assert_rnp_success(rnp_locate_key(ffi, "keyid", "9988c1bcb55391d6", &key));
+ assert_non_null(key);
+ assert_true(check_key_valid(key, false));
+ assert_true(check_uid_valid(key, 0, false));
+ assert_true(check_sub_valid(key, 0, false));
+ rnp_key_handle_destroy(key);
+ rnp_ffi_destroy(ffi);
+
+ /* Verify data signature with unknown critical notation */
+ assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));
+ assert_true(load_keys_gpg(ffi, "data/keyrings/1/pubring.gpg"));
+ rnp_input_t input = NULL;
+ assert_rnp_success(
+ rnp_input_from_path(&input, "data/test_messages/message.txt.signed.crit-notation"));
+ rnp_output_t output = NULL;
+ 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));
+ size_t sigcount = 255;
+ assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sigcount));
+ assert_int_equal(sigcount, 1);
+ rnp_op_verify_signature_t sig = NULL;
+ 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);
+ rnp_op_verify_destroy(verify);
+ rnp_input_destroy(input);
+ rnp_output_destroy(output);
+ rnp_ffi_destroy(ffi);
+}
+
+TEST_F(rnp_tests, test_ffi_key_import_invalid_issuer)
+{
+ rnp_ffi_t ffi = NULL;
+ assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG"));
+
+ /* public key + secret subkey with invalid signer's keyfp */
+ rnp_input_t input = NULL;
+ assert_rnp_success(
+ rnp_input_from_path(&input, "data/test_key_edge_cases/alice-sub-sig-fp.pgp"));
+ char * keys = NULL;
+ uint32_t flags =
+ RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS | RNP_LOAD_SAVE_SINGLE;
+ assert_rnp_success(rnp_import_keys(ffi, input, flags, &keys));
+ rnp_input_destroy(input);
+ rnp_buffer_destroy(keys);
+
+ /* public key + secret subkey with invalid signer's keyid */
+ assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET));
+ assert_rnp_success(
+ rnp_input_from_path(&input, "data/test_key_edge_cases/alice-sub-sig-keyid.pgp"));
+ assert_rnp_success(rnp_import_keys(ffi, input, flags, &keys));
+ rnp_input_destroy(input);
+ rnp_buffer_destroy(keys);
+
+ rnp_ffi_destroy(ffi);
+}