Revision control

Copy as Markdown

Other Tools

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