/* * 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); uint32_t features = 0; assert_rnp_failure(rnp_signature_get_features(NULL, &features)); assert_rnp_failure(rnp_signature_get_features(sig, NULL)); assert_rnp_success(rnp_signature_get_features(sig, &features)); assert_int_equal(features, 0); 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); }