From 8053187731ae8e3eb368d8360989cf5fd6eed9f7 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 05:32:49 +0200 Subject: Adding upstream version 0.17.0. Signed-off-by: Daniel Baumann --- src/tests/ffi-key-sig.cpp | 1626 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1626 insertions(+) create mode 100644 src/tests/ffi-key-sig.cpp (limited to 'src/tests/ffi-key-sig.cpp') 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 +#include +#include +#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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ", &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 ):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 ):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 ):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 ):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 ):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 ):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 )"); + + 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 ):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 ):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 ):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 ):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 ):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 ):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 ):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 ):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 ):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 ):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, + ¶m)); + assert_string_equal( + key_packets(key).c_str(), + ":pub(0451):sig(32, c76f, 0451):uid(Alice ):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, ¶m)); + assert_string_equal( + key_packets(key).c_str(), + ":pub(0451):sig(32, c76f, 0451):uid(Alice ):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, ¶m)); + assert_string_equal(key_packets(key).c_str(), + ":pub(0451):uid(Alice ):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 ):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, ¶m)); + assert_string_equal(key_packets(key).c_str(), + ":sec(0451):uid(Alice ):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 ):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, ¶m)); + 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); +} -- cgit v1.2.3