diff options
Diffstat (limited to '')
-rw-r--r-- | src/tests/ffi-key.cpp | 4442 |
1 files changed, 4442 insertions, 0 deletions
diff --git a/src/tests/ffi-key.cpp b/src/tests/ffi-key.cpp new file mode 100644 index 0000000..2933d68 --- /dev/null +++ b/src/tests/ffi-key.cpp @@ -0,0 +1,4442 @@ +/* + * Copyright (c) 2022 [Ribose Inc](https://www.ribose.com). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <rnp/rnp.h> +#include "rnp_tests.h" +#include "support.h" +#include <librepgp/stream-ctx.h> +#include "pgp-key.h" +#include "ffi-priv-types.h" +#include "str-utils.h" +#ifndef RNP_USE_STD_REGEX +#include <regex.h> +#else +#include <regex> +#endif + +static void +check_key_properties(rnp_key_handle_t key, + bool primary_exptected, + bool have_public_expected, + bool have_secret_expected) +{ + bool isprimary = !primary_exptected; + assert_rnp_success(rnp_key_is_primary(key, &isprimary)); + assert_true(isprimary == primary_exptected); + bool issub = primary_exptected; + assert_rnp_success(rnp_key_is_sub(key, &issub)); + assert_true(issub == !primary_exptected); + bool have_public = !have_public_expected; + assert_rnp_success(rnp_key_have_public(key, &have_public)); + assert_true(have_public == have_public_expected); + bool have_secret = !have_secret_expected; + assert_rnp_success(rnp_key_have_secret(key, &have_secret)); + assert_true(have_secret == have_secret_expected); +} + +TEST_F(rnp_tests, test_ffi_keygen_json_pair) +{ + rnp_ffi_t ffi = NULL; + char * results = NULL; + size_t count = 0; + + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_ffi_set_key_provider(ffi, unused_getkeycb, NULL)); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "abc")); + + // load our JSON + auto json = file_to_str("data/test_ffi_json/generate-pair.json"); + + // generate the keys + assert_rnp_success(rnp_generate_key_json(ffi, json.c_str(), &results)); + assert_non_null(results); + + // parse the results JSON + json_object *parsed_results = json_tokener_parse(results); + assert_non_null(parsed_results); + rnp_buffer_destroy(results); + results = NULL; + // get a handle for the primary + rnp_key_handle_t primary = NULL; + { + json_object *jsokey = NULL; + assert_int_equal(true, json_object_object_get_ex(parsed_results, "primary", &jsokey)); + assert_non_null(jsokey); + json_object *jsogrip = NULL; + assert_int_equal(true, json_object_object_get_ex(jsokey, "grip", &jsogrip)); + assert_non_null(jsogrip); + const char *grip = json_object_get_string(jsogrip); + assert_non_null(grip); + assert_rnp_success(rnp_locate_key(ffi, "grip", grip, &primary)); + assert_non_null(primary); + } + // get a handle for the sub + rnp_key_handle_t sub = NULL; + { + json_object *jsokey = NULL; + assert_int_equal(true, json_object_object_get_ex(parsed_results, "sub", &jsokey)); + assert_non_null(jsokey); + json_object *jsogrip = NULL; + assert_int_equal(true, json_object_object_get_ex(jsokey, "grip", &jsogrip)); + assert_non_null(jsogrip); + const char *grip = json_object_get_string(jsogrip); + assert_non_null(grip); + assert_rnp_success(rnp_locate_key(ffi, "grip", grip, &sub)); + assert_non_null(sub); + } + // cleanup + json_object_put(parsed_results); + + // check the key counts + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(2, count); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(2, count); + + // check some key properties + check_key_properties(primary, true, true, true); + check_key_properties(sub, false, true, true); + + // check sub bit length + uint32_t length = 0; + assert_rnp_success(rnp_key_get_bits(sub, &length)); + assert_int_equal(1024, length); + + // cleanup + rnp_key_handle_destroy(primary); + rnp_key_handle_destroy(sub); + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_keygen_json_pair_dsa_elg) +{ + rnp_ffi_t ffi = NULL; + char * results = NULL; + size_t count = 0; + + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_ffi_set_key_provider(ffi, unused_getkeycb, NULL)); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "abc")); + + // load our JSON + auto json = file_to_str("data/test_ffi_json/generate-pair-dsa-elg.json"); + + // generate the keys + assert_rnp_success(rnp_generate_key_json(ffi, json.c_str(), &results)); + assert_non_null(results); + + // parse the results JSON + json_object *parsed_results = json_tokener_parse(results); + assert_non_null(parsed_results); + rnp_buffer_destroy(results); + results = NULL; + // get a handle for the primary + rnp_key_handle_t primary = NULL; + { + json_object *jsokey = NULL; + assert_int_equal(true, json_object_object_get_ex(parsed_results, "primary", &jsokey)); + assert_non_null(jsokey); + json_object *jsogrip = NULL; + assert_int_equal(true, json_object_object_get_ex(jsokey, "grip", &jsogrip)); + assert_non_null(jsogrip); + const char *grip = json_object_get_string(jsogrip); + assert_non_null(grip); + assert_rnp_success(rnp_locate_key(ffi, "grip", grip, &primary)); + assert_non_null(primary); + } + // get a handle for the sub + rnp_key_handle_t sub = NULL; + { + json_object *jsokey = NULL; + assert_int_equal(true, json_object_object_get_ex(parsed_results, "sub", &jsokey)); + assert_non_null(jsokey); + json_object *jsogrip = NULL; + assert_int_equal(true, json_object_object_get_ex(jsokey, "grip", &jsogrip)); + assert_non_null(jsogrip); + const char *grip = json_object_get_string(jsogrip); + assert_non_null(grip); + assert_rnp_success(rnp_locate_key(ffi, "grip", grip, &sub)); + assert_non_null(sub); + } + // cleanup + json_object_put(parsed_results); + + // check the key counts + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(2, count); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(2, count); + + // check some key properties + check_key_properties(primary, true, true, true); + check_key_properties(sub, false, true, true); + + // check bit lengths + uint32_t length = 0; + assert_rnp_success(rnp_key_get_bits(primary, &length)); + assert_int_equal(length, 1024); + assert_rnp_success(rnp_key_get_bits(sub, &length)); + assert_int_equal(length, 1536); + + // cleanup + rnp_key_handle_destroy(primary); + rnp_key_handle_destroy(sub); + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_keygen_json_primary) +{ + rnp_ffi_t ffi = NULL; + char * results = NULL; + size_t count = 0; + + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_ffi_set_key_provider(ffi, unused_getkeycb, NULL)); + assert_rnp_success(rnp_ffi_set_pass_provider(ffi, unused_getpasscb, NULL)); + + // load our JSON + auto json = file_to_str("data/test_ffi_json/generate-primary.json"); + + // generate the keys + assert_rnp_success(rnp_generate_key_json(ffi, json.c_str(), &results)); + assert_non_null(results); + + // parse the results JSON + json_object *parsed_results = json_tokener_parse(results); + assert_non_null(parsed_results); + rnp_buffer_destroy(results); + results = NULL; + // get a handle for the primary + rnp_key_handle_t primary = NULL; + { + json_object *jsokey = NULL; + assert_int_equal(true, json_object_object_get_ex(parsed_results, "primary", &jsokey)); + assert_non_null(jsokey); + json_object *jsogrip = NULL; + assert_int_equal(true, json_object_object_get_ex(jsokey, "grip", &jsogrip)); + assert_non_null(jsogrip); + const char *grip = json_object_get_string(jsogrip); + assert_non_null(grip); + assert_rnp_success(rnp_locate_key(ffi, "grip", grip, &primary)); + assert_non_null(primary); + } + // cleanup + json_object_put(parsed_results); + parsed_results = NULL; + + // check the key counts + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(1, count); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(1, count); + + // check some key properties + check_key_properties(primary, true, true, true); + + // cleanup + rnp_key_handle_destroy(primary); + rnp_ffi_destroy(ffi); +} + +/* This test generates a primary key, and then a subkey (separately). + */ +TEST_F(rnp_tests, test_ffi_keygen_json_sub) +{ + char * results = NULL; + size_t count = 0; + rnp_ffi_t ffi = NULL; + + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_ffi_set_key_provider(ffi, unused_getkeycb, NULL)); + assert_rnp_success(rnp_ffi_set_pass_provider(ffi, unused_getpasscb, NULL)); + + // generate our primary key + auto json = file_to_str("data/test_ffi_json/generate-primary.json"); + assert_rnp_success(rnp_generate_key_json(ffi, json.c_str(), &results)); + // check key counts + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(1, count); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(1, count); + + // parse the results JSON + json_object *parsed_results = json_tokener_parse(results); + assert_non_null(parsed_results); + rnp_buffer_destroy(results); + results = NULL; + // get a handle+grip for the primary + rnp_key_handle_t primary = NULL; + char * primary_grip = NULL; + { + json_object *jsokey = NULL; + assert_int_equal(true, json_object_object_get_ex(parsed_results, "primary", &jsokey)); + assert_non_null(jsokey); + json_object *jsogrip = NULL; + assert_int_equal(true, json_object_object_get_ex(jsokey, "grip", &jsogrip)); + assert_non_null(jsogrip); + primary_grip = strdup(json_object_get_string(jsogrip)); + assert_non_null(primary_grip); + assert_rnp_success(rnp_locate_key(ffi, "grip", primary_grip, &primary)); + assert_non_null(primary); + } + // cleanup + json_object_put(parsed_results); + parsed_results = NULL; + + // load our JSON template + json = file_to_str("data/test_ffi_json/generate-sub.json"); + // modify our JSON + { + // parse + json_object *jso = json_tokener_parse(json.c_str()); + assert_non_null(jso); + // find the relevant fields + json_object *jsosub = NULL; + json_object *jsoprimary = NULL; + assert_true(json_object_object_get_ex(jso, "sub", &jsosub)); + assert_non_null(jsosub); + assert_true(json_object_object_get_ex(jsosub, "primary", &jsoprimary)); + assert_non_null(jsoprimary); + // replace the placeholder grip with the correct one + json_object_object_del(jsoprimary, "grip"); + json_object_object_add(jsoprimary, "grip", json_object_new_string(primary_grip)); + assert_int_equal(1, json_object_object_length(jsoprimary)); + json = json_object_to_json_string_ext(jso, JSON_C_TO_STRING_PRETTY); + assert_false(json.empty()); + json_object_put(jso); + } + // cleanup + rnp_buffer_destroy(primary_grip); + primary_grip = NULL; + + // generate the subkey + assert_rnp_success(rnp_generate_key_json(ffi, json.c_str(), &results)); + assert_non_null(results); + + // parse the results JSON + parsed_results = json_tokener_parse(results); + assert_non_null(parsed_results); + rnp_buffer_destroy(results); + results = NULL; + // get a handle for the sub + rnp_key_handle_t sub = NULL; + { + json_object *jsokey = NULL; + assert_int_equal(true, json_object_object_get_ex(parsed_results, "sub", &jsokey)); + assert_non_null(jsokey); + json_object *jsogrip = NULL; + assert_int_equal(true, json_object_object_get_ex(jsokey, "grip", &jsogrip)); + assert_non_null(jsogrip); + const char *grip = json_object_get_string(jsogrip); + assert_non_null(grip); + assert_rnp_success(rnp_locate_key(ffi, "grip", grip, &sub)); + assert_non_null(sub); + } + // cleanup + json_object_put(parsed_results); + parsed_results = NULL; + + // check the key counts + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(2, count); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(2, count); + + // check some key properties + check_key_properties(primary, true, true, true); + check_key_properties(sub, false, true, true); + + // check sub bit length + uint32_t length = 0; + assert_rnp_success(rnp_key_get_bits(sub, &length)); + assert_int_equal(length, 1024); + + // cleanup + rnp_key_handle_destroy(primary); + rnp_key_handle_destroy(sub); + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_keygen_json_edge_cases) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + + /* Attempt to generate with invalid parameters */ + std::string json = ""; + char * results = NULL; + assert_rnp_failure(rnp_generate_key_json(NULL, json.c_str(), &results)); + assert_rnp_failure(rnp_generate_key_json(ffi, NULL, &results)); + assert_rnp_failure(rnp_generate_key_json(ffi, "{ something, wrong }", &results)); + assert_rnp_failure(rnp_generate_key_json(ffi, "{ }", &results)); + assert_rnp_failure( + rnp_generate_key_json(ffi, "{ \"primary\": { }, \"wrong\": {} }", &results)); + assert_rnp_failure( + rnp_generate_key_json(ffi, "{ \"primary\": { }, \"PRIMARY\": { } }", &results)); + /* Json-C puts stuff under the same key into the single object */ + assert_rnp_success( + rnp_generate_key_json(ffi, "{ \"primary\": { }, \"primary\": { } }", &results)); + rnp_buffer_destroy(results); + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET)); + /* Generate key with an empty description */ + assert_rnp_success(rnp_generate_key_json(ffi, "{ \"priMary\": {} }", &results)); + assert_non_null(results); + rnp_buffer_destroy(results); + size_t count = 0; + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(count, 1); + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET)); + /* Generate with wrong preferences */ + json = file_to_str("data/test_ffi_json/generate-eddsa-wrong-prefs.json"); + assert_rnp_failure(rnp_generate_key_json(ffi, json.c_str(), &results)); + /* Generate with wrong PK algorithm */ + json = file_to_str("data/test_ffi_json/generate-bad-pk-alg.json"); + results = NULL; + assert_rnp_failure(rnp_generate_key_json(ffi, json.c_str(), &results)); + assert_null(results); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(count, 0); + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(count, 0); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_key_generate_misc) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_ffi_set_key_provider(ffi, unused_getkeycb, NULL)); + + /* make sure we do not leak key handle and do not access NULL */ + assert_rnp_success(rnp_generate_key_rsa(ffi, 1024, 1024, "rsa", NULL, NULL)); + + /* make sure we do not leak password on failed key generation */ + rnp_key_handle_t key = NULL; + assert_rnp_failure(rnp_generate_key_rsa(ffi, 768, 2048, "rsa_768", "password", &key)); + assert_rnp_failure(rnp_generate_key_rsa(ffi, 1024, 768, "rsa_768", "password", &key)); + + /* make sure we behave correctly and do not leak data on wrong parameters to _generate_ex + * function */ + assert_rnp_failure(rnp_generate_key_ex( + ffi, "RSA", "RSA", 1024, 1024, "Curve", NULL, "userid", "password", &key)); + assert_rnp_failure(rnp_generate_key_ex( + ffi, "RSA", "RSA", 1024, 1024, "Curve", NULL, NULL, "password", &key)); + assert_rnp_failure(rnp_generate_key_ex( + ffi, "RSA", "RSA", 1024, 768, NULL, "Curve", NULL, "password", &key)); + assert_rnp_failure(rnp_generate_key_ex( + ffi, "ECDSA", "ECDH", 1024, 0, "Unknown", "Curve", NULL, NULL, &key)); + assert_rnp_failure(rnp_generate_key_ex( + ffi, "ECDSA", "ECDH", 0, 1024, "Unknown", "Curve", NULL, "password", &key)); + + /* generate RSA-RSA key without password */ + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "abc")); + assert_rnp_success(rnp_generate_key_rsa(ffi, 1024, 1024, "rsa_1024", NULL, &key)); + assert_non_null(key); + bool locked = false; + assert_rnp_success(rnp_key_is_locked(key, &locked)); + assert_false(locked); + /* check key and subkey flags */ + bool flag = false; + assert_rnp_success(rnp_key_allows_usage(key, "sign", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(key, "certify", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(key, "encrypt", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(key, "authenticate", &flag)); + assert_false(flag); + uint32_t expiration = 0; + assert_rnp_success(rnp_key_get_expiration(key, &expiration)); + assert_int_equal(expiration, 2 * 365 * 24 * 60 * 60); + uint32_t creation = 0; + assert_rnp_success(rnp_key_get_creation(key, &creation)); + uint32_t till = 0; + assert_rnp_success(rnp_key_valid_till(key, &till)); + assert_int_equal(till, creation + expiration); + rnp_key_handle_t subkey = NULL; + assert_rnp_success(rnp_key_get_subkey_at(key, 0, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_key_allows_usage(subkey, "sign", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "certify", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "encrypt", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "authenticate", &flag)); + assert_false(flag); + expiration = 0; + assert_rnp_success(rnp_key_get_expiration(subkey, &expiration)); + assert_int_equal(expiration, 2 * 365 * 24 * 60 * 60); + creation = 0; + assert_rnp_success(rnp_key_get_creation(key, &creation)); + till = 0; + assert_rnp_success(rnp_key_valid_till(key, &till)); + assert_int_equal(till, creation + expiration); + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + /* generate encrypted RSA-RSA key */ + assert_rnp_success(rnp_generate_key_rsa(ffi, 1024, 1024, "rsa_1024", "123", &key)); + assert_non_null(key); + assert_rnp_success(rnp_key_is_locked(key, &locked)); + assert_true(locked); + /* make sure it can be unlocked with correct password */ + assert_rnp_success(rnp_key_unlock(key, "123")); + /* do the same for subkey */ + assert_rnp_success(rnp_key_get_subkey_at(key, 0, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_key_is_locked(subkey, &locked)); + assert_true(locked); + assert_rnp_success(rnp_key_unlock(subkey, "123")); + /* cleanup */ + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + /* generate encrypted RSA key (primary only) */ + key = NULL; + assert_rnp_success( + rnp_generate_key_ex(ffi, "RSA", NULL, 1024, 0, NULL, NULL, "rsa_1024", "123", &key)); + assert_non_null(key); + assert_rnp_success(rnp_key_is_locked(key, &locked)); + assert_true(locked); + bool prot = false; + assert_rnp_success(rnp_key_is_protected(key, &prot)); + assert_true(prot); + /* cleanup */ + rnp_key_handle_destroy(key); + + /* generate key with signing subkey */ + rnp_op_generate_t op = NULL; + assert_rnp_success(rnp_op_generate_create(&op, ffi, "ECDSA")); + assert_rnp_success(rnp_op_generate_set_curve(op, "secp256k1")); + assert_rnp_success(rnp_op_generate_set_userid(op, "ecdsa_ecdsa")); + assert_rnp_success(rnp_op_generate_add_usage(op, "sign")); + assert_rnp_success(rnp_op_generate_add_usage(op, "certify")); + assert_rnp_success(rnp_op_generate_set_expiration(op, 0)); + assert_rnp_success(rnp_op_generate_execute(op)); + rnp_key_handle_t primary = NULL; + assert_rnp_success(rnp_op_generate_get_key(op, &primary)); + rnp_op_generate_destroy(op); + char *keyid = NULL; + assert_rnp_success(rnp_key_get_keyid(primary, &keyid)); + + rnp_op_generate_t subop = NULL; + assert_rnp_success(rnp_op_generate_subkey_create(&subop, ffi, primary, "ECDSA")); + assert_rnp_success(rnp_op_generate_set_curve(subop, "NIST P-256")); + assert_rnp_success(rnp_op_generate_add_usage(subop, "sign")); + assert_rnp_success(rnp_op_generate_add_usage(subop, "certify")); + assert_rnp_success(rnp_op_generate_set_expiration(subop, 0)); + assert_rnp_success(rnp_op_generate_execute(subop)); + assert_rnp_success(rnp_op_generate_get_key(subop, &subkey)); + rnp_op_generate_destroy(subop); + char *subid = NULL; + assert_rnp_success(rnp_key_get_keyid(subkey, &subid)); + + rnp_output_t output = NULL; + rnp_output_to_memory(&output, 0); + assert_rnp_success( + rnp_key_export(primary, + output, + RNP_KEY_EXPORT_ARMORED | RNP_KEY_EXPORT_PUBLIC | RNP_KEY_EXPORT_SUBKEYS)); + rnp_key_handle_destroy(primary); + rnp_key_handle_destroy(subkey); + uint8_t *buf = NULL; + size_t len = 0; + rnp_output_memory_get_buf(output, &buf, &len, false); + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET)); + assert_true(import_pub_keys(ffi, buf, len)); + assert_rnp_success(rnp_locate_key(ffi, "keyid", keyid, &primary)); + assert_non_null(primary); + assert_true(primary->pub->valid()); + bool valid = false; + assert_rnp_failure(rnp_key_is_valid(primary, NULL)); + assert_rnp_failure(rnp_key_is_valid(NULL, &valid)); + assert_rnp_success(rnp_key_is_valid(primary, &valid)); + assert_true(valid); + till = 0; + assert_rnp_failure(rnp_key_valid_till(primary, NULL)); + assert_rnp_failure(rnp_key_valid_till(NULL, &till)); + assert_rnp_success(rnp_key_valid_till(primary, &till)); + assert_int_equal(till, 0xffffffff); + uint64_t till64 = 0; + assert_rnp_failure(rnp_key_valid_till64(primary, NULL)); + assert_rnp_failure(rnp_key_valid_till64(NULL, &till64)); + assert_rnp_success(rnp_key_valid_till64(primary, &till64)); + assert_int_equal(till64, UINT64_MAX); + rnp_key_handle_destroy(primary); + assert_rnp_success(rnp_locate_key(ffi, "keyid", subid, &subkey)); + assert_non_null(subkey); + assert_true(subkey->pub->valid()); + valid = false; + assert_rnp_success(rnp_key_is_valid(subkey, &valid)); + assert_true(valid); + till = 0; + assert_rnp_success(rnp_key_valid_till(subkey, &till)); + assert_int_equal(till, 0xffffffff); + assert_rnp_success(rnp_key_valid_till64(subkey, &till64)); + assert_int_equal(till64, UINT64_MAX); + rnp_key_handle_destroy(subkey); + rnp_buffer_destroy(keyid); + rnp_buffer_destroy(subid); + rnp_output_destroy(output); + + /* cleanup */ + assert_rnp_success(rnp_ffi_destroy(ffi)); +} + +TEST_F(rnp_tests, test_ffi_sec_key_offline_operations) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + + /* generate subkey for offline secret key */ + assert_true(import_all_keys(ffi, "data/test_key_edge_cases/alice-s2k-101-1-subs.pgp")); + rnp_key_handle_t primary = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669FFDE3C", &primary)); + rnp_op_generate_t subop = NULL; + assert_rnp_failure(rnp_op_generate_subkey_create(&subop, ffi, primary, "ECDSA")); + /* unlock/unprotect offline secret key */ + assert_rnp_failure(rnp_key_unlock(primary, "password")); + assert_rnp_failure(rnp_key_unprotect(primary, "password")); + /* add userid */ + assert_int_equal(rnp_key_add_uid(primary, "new_uid", "SHA256", 2147317200, 0x00, false), + RNP_ERROR_NO_SUITABLE_KEY); + rnp_key_handle_destroy(primary); + /* generate subkey for offline secret key on card */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET)); + assert_true(import_all_keys(ffi, "data/test_key_edge_cases/alice-s2k-101-2-card.pgp")); + primary = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669FFDE3C", &primary)); + subop = NULL; + assert_rnp_failure(rnp_op_generate_subkey_create(&subop, ffi, primary, "ECDSA")); + rnp_key_handle_destroy(primary); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_key_generate_rsa) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_ffi_set_key_provider(ffi, unused_getkeycb, NULL)); + /* make sure we fail to generate too small and too large keys/subkeys */ + rnp_key_handle_t key = NULL; + assert_rnp_failure(rnp_generate_key_rsa(ffi, 768, 2048, "rsa_768", NULL, &key)); + assert_rnp_failure(rnp_generate_key_rsa(ffi, 1024, 768, "rsa_768", NULL, &key)); + assert_rnp_failure(rnp_generate_key_rsa(ffi, 20480, 1024, "rsa_20480", NULL, &key)); + assert_rnp_failure(rnp_generate_key_rsa(ffi, 1024, 20480, "rsa_20480", NULL, &key)); + /* generate RSA-RSA key */ + assert_rnp_success(rnp_generate_key_rsa(ffi, 1024, 2048, "rsa_1024", NULL, &key)); + assert_non_null(key); + /* check properties of the generated key */ + bool boolres = false; + assert_rnp_success(rnp_key_is_primary(key, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_have_public(key, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_have_secret(key, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_is_protected(key, &boolres)); + assert_false(boolres); + assert_rnp_success(rnp_key_is_locked(key, &boolres)); + assert_false(boolres); + /* algorithm */ + char *alg = NULL; + assert_rnp_success(rnp_key_get_alg(key, &alg)); + assert_int_equal(strcasecmp(alg, "RSA"), 0); + rnp_buffer_destroy(alg); + /* key bits */ + uint32_t bits = 0; + assert_rnp_failure(rnp_key_get_bits(key, NULL)); + assert_rnp_success(rnp_key_get_bits(key, &bits)); + assert_int_equal(bits, 1024); + assert_rnp_failure(rnp_key_get_dsa_qbits(key, &bits)); + /* key flags */ + bool flag = false; + assert_rnp_success(rnp_key_allows_usage(key, "sign", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(key, "certify", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(key, "encrypt", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(key, "authenticate", &flag)); + assert_false(flag); + /* curve - must fail */ + char *curve = NULL; + assert_rnp_failure(rnp_key_get_curve(key, NULL)); + assert_rnp_failure(rnp_key_get_curve(key, &curve)); + assert_null(curve); + /* user ids */ + size_t uids = 0; + char * uid = NULL; + assert_rnp_success(rnp_key_get_uid_count(key, &uids)); + assert_int_equal(uids, 1); + assert_rnp_failure(rnp_key_get_uid_at(key, 1, &uid)); + assert_null(uid); + assert_rnp_success(rnp_key_get_uid_at(key, 0, &uid)); + assert_string_equal(uid, "rsa_1024"); + rnp_buffer_destroy(uid); + /* subkey */ + size_t subkeys = 0; + assert_rnp_failure(rnp_key_get_subkey_count(key, NULL)); + assert_rnp_success(rnp_key_get_subkey_count(key, &subkeys)); + assert_int_equal(subkeys, 1); + rnp_key_handle_t subkey = NULL; + assert_rnp_failure(rnp_key_get_subkey_at(key, 1, &subkey)); + assert_rnp_failure(rnp_key_get_subkey_at(key, 0, NULL)); + assert_rnp_success(rnp_key_get_subkey_at(key, 0, &subkey)); + /* check properties of the generated subkey */ + assert_rnp_success(rnp_key_is_primary(subkey, &boolres)); + assert_false(boolres); + assert_rnp_success(rnp_key_have_public(subkey, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_have_secret(subkey, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_is_protected(subkey, &boolres)); + assert_false(boolres); + assert_rnp_success(rnp_key_is_locked(subkey, &boolres)); + assert_false(boolres); + /* algorithm */ + assert_rnp_success(rnp_key_get_alg(subkey, &alg)); + assert_int_equal(strcasecmp(alg, "RSA"), 0); + rnp_buffer_destroy(alg); + /* key bits */ + assert_rnp_success(rnp_key_get_bits(subkey, &bits)); + assert_int_equal(bits, 2048); + /* subkey flags */ + assert_rnp_success(rnp_key_allows_usage(subkey, "sign", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "certify", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "encrypt", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "authenticate", &flag)); + assert_false(flag); + /* cleanup */ + assert_rnp_success(rnp_key_handle_destroy(subkey)); + assert_rnp_success(rnp_key_handle_destroy(key)); + + /* generate RSA key without the subkey */ + assert_rnp_success(rnp_generate_key_rsa(ffi, 1024, 0, "rsa_1024", NULL, &key)); + assert_non_null(key); + assert_rnp_success(rnp_key_get_subkey_count(key, &subkeys)); + assert_int_equal(subkeys, 0); + /* cleanup */ + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success(rnp_ffi_destroy(ffi)); +} + +TEST_F(rnp_tests, test_ffi_key_generate_dsa) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_ffi_set_key_provider(ffi, unused_getkeycb, NULL)); + /* try to generate keys with invalid sizes */ + rnp_key_handle_t key = NULL; + assert_rnp_failure(rnp_generate_key_dsa_eg(ffi, 768, 2048, "dsa_768", NULL, &key)); + assert_rnp_failure(rnp_generate_key_dsa_eg(ffi, 1024, 768, "dsa_768", NULL, &key)); + assert_rnp_failure(rnp_generate_key_dsa_eg(ffi, 4096, 1024, "dsa_20480", NULL, &key)); + assert_rnp_failure(rnp_generate_key_dsa_eg(ffi, 1024, 20480, "dsa_20480", NULL, &key)); + /* generate DSA-ElGamal keypair */ + assert_rnp_success(rnp_generate_key_dsa_eg(ffi, 1024, 1024, "dsa_1024", NULL, &key)); + assert_non_null(key); + /* check properties of the generated key */ + bool boolres = false; + assert_rnp_success(rnp_key_is_primary(key, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_have_public(key, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_have_secret(key, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_is_protected(key, &boolres)); + assert_false(boolres); + assert_rnp_success(rnp_key_is_locked(key, &boolres)); + assert_false(boolres); + /* algorithm */ + char *alg = NULL; + assert_rnp_success(rnp_key_get_alg(key, &alg)); + assert_int_equal(strcasecmp(alg, "DSA"), 0); + rnp_buffer_destroy(alg); + /* key bits */ + uint32_t bits = 0; + assert_rnp_success(rnp_key_get_bits(key, &bits)); + assert_int_equal(bits, 1024); + assert_rnp_success(rnp_key_get_dsa_qbits(key, &bits)); + assert_int_equal(bits, 160); + /* key flags */ + bool flag = false; + assert_rnp_success(rnp_key_allows_usage(key, "sign", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(key, "certify", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(key, "encrypt", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(key, "authenticate", &flag)); + assert_false(flag); + /* user ids */ + size_t uids = 0; + char * uid = NULL; + assert_rnp_success(rnp_key_get_uid_count(key, &uids)); + assert_int_equal(uids, 1); + assert_rnp_success(rnp_key_get_uid_at(key, 0, &uid)); + assert_string_equal(uid, "dsa_1024"); + rnp_buffer_destroy(uid); + /* subkey */ + size_t subkeys = 0; + assert_rnp_success(rnp_key_get_subkey_count(key, &subkeys)); + assert_int_equal(subkeys, 1); + rnp_key_handle_t subkey = NULL; + assert_rnp_success(rnp_key_get_subkey_at(key, 0, &subkey)); + /* check properties of the generated subkey */ + assert_rnp_success(rnp_key_is_primary(subkey, &boolres)); + assert_false(boolres); + assert_rnp_success(rnp_key_have_public(subkey, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_have_secret(subkey, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_is_protected(subkey, &boolres)); + assert_false(boolres); + assert_rnp_success(rnp_key_is_locked(subkey, &boolres)); + assert_false(boolres); + /* algorithm */ + assert_rnp_success(rnp_key_get_alg(subkey, &alg)); + assert_int_equal(strcasecmp(alg, "ELGAMAL"), 0); + rnp_buffer_destroy(alg); + /* key bits */ + assert_rnp_success(rnp_key_get_bits(subkey, &bits)); + assert_int_equal(bits, 1024); + /* subkey flags */ + assert_rnp_success(rnp_key_allows_usage(subkey, "sign", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "certify", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "encrypt", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "authenticate", &flag)); + assert_false(flag); + /* cleanup */ + assert_rnp_success(rnp_key_handle_destroy(subkey)); + assert_rnp_success(rnp_key_handle_destroy(key)); + + /* generate DSA key without the subkey */ + assert_rnp_success(rnp_generate_key_dsa_eg(ffi, 1024, 0, "dsa_1024", NULL, &key)); + assert_non_null(key); + assert_rnp_success(rnp_key_get_subkey_count(key, &subkeys)); + assert_int_equal(subkeys, 0); + /* cleanup */ + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success(rnp_ffi_destroy(ffi)); +} + +TEST_F(rnp_tests, test_ffi_key_generate_ecdsa) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_ffi_set_key_provider(ffi, unused_getkeycb, NULL)); + /* try to generate key with invalid curve */ + rnp_key_handle_t key = NULL; + assert_rnp_failure(rnp_generate_key_ec(ffi, "curve_wrong", "wrong", NULL, &key)); + assert_null(key); + /* generate secp256k1 key */ + assert_rnp_success(rnp_generate_key_ec(ffi, "secp256k1", "ec_256k1", NULL, &key)); + assert_non_null(key); + /* check properties of the generated key */ + bool boolres = false; + assert_rnp_success(rnp_key_is_primary(key, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_have_public(key, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_have_secret(key, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_is_protected(key, &boolres)); + assert_false(boolres); + assert_rnp_success(rnp_key_is_locked(key, &boolres)); + assert_false(boolres); + /* algorithm */ + char *alg = NULL; + assert_rnp_success(rnp_key_get_alg(key, &alg)); + assert_int_equal(strcasecmp(alg, "ECDSA"), 0); + rnp_buffer_destroy(alg); + /* key bits */ + uint32_t bits = 0; + assert_rnp_success(rnp_key_get_bits(key, &bits)); + assert_int_equal(bits, 256); + assert_rnp_failure(rnp_key_get_dsa_qbits(key, &bits)); + /* curve */ + char *curve = NULL; + assert_rnp_failure(rnp_key_get_curve(key, NULL)); + assert_rnp_success(rnp_key_get_curve(key, &curve)); + assert_int_equal(strcasecmp(curve, "secp256k1"), 0); + rnp_buffer_destroy(curve); + /* key flags */ + bool flag = false; + assert_rnp_success(rnp_key_allows_usage(key, "sign", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(key, "certify", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(key, "encrypt", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(key, "authenticate", &flag)); + assert_false(flag); + /* user ids */ + size_t uids = 0; + char * uid = NULL; + assert_rnp_success(rnp_key_get_uid_count(key, &uids)); + assert_int_equal(uids, 1); + assert_rnp_success(rnp_key_get_uid_at(key, 0, &uid)); + assert_string_equal(uid, "ec_256k1"); + rnp_buffer_destroy(uid); + /* subkey */ + size_t subkeys = 0; + assert_rnp_success(rnp_key_get_subkey_count(key, &subkeys)); + assert_int_equal(subkeys, 1); + rnp_key_handle_t subkey = NULL; + assert_rnp_success(rnp_key_get_subkey_at(key, 0, &subkey)); + /* check properties of the generated subkey */ + assert_rnp_success(rnp_key_is_primary(subkey, &boolres)); + assert_false(boolres); + assert_rnp_success(rnp_key_have_public(subkey, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_have_secret(subkey, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_is_protected(subkey, &boolres)); + assert_false(boolres); + assert_rnp_success(rnp_key_is_locked(subkey, &boolres)); + assert_false(boolres); + /* algorithm */ + assert_rnp_success(rnp_key_get_alg(subkey, &alg)); + assert_int_equal(strcasecmp(alg, "ECDH"), 0); + rnp_buffer_destroy(alg); + /* bits */ + assert_rnp_success(rnp_key_get_bits(subkey, &bits)); + assert_int_equal(bits, 256); + /* curve */ + curve = NULL; + assert_rnp_success(rnp_key_get_curve(subkey, &curve)); + assert_int_equal(strcasecmp(curve, "secp256k1"), 0); + rnp_buffer_destroy(curve); + /* subkey flags */ + assert_rnp_success(rnp_key_allows_usage(subkey, "sign", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "certify", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "encrypt", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "authenticate", &flag)); + assert_false(flag); + + assert_rnp_success(rnp_key_handle_destroy(subkey)); + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success(rnp_ffi_destroy(ffi)); +} + +TEST_F(rnp_tests, test_ffi_key_generate_eddsa) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_ffi_set_key_provider(ffi, unused_getkeycb, NULL)); + /* generate key with subkey */ + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_generate_key_25519(ffi, "eddsa_25519", NULL, &key)); + assert_non_null(key); + /* check properties of the generated key */ + bool boolres = false; + assert_rnp_success(rnp_key_is_primary(key, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_have_public(key, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_have_secret(key, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_is_protected(key, &boolres)); + assert_false(boolres); + assert_rnp_success(rnp_key_is_locked(key, &boolres)); + assert_false(boolres); + /* algorithm */ + char *alg = NULL; + assert_rnp_success(rnp_key_get_alg(key, &alg)); + assert_int_equal(strcasecmp(alg, "EDDSA"), 0); + rnp_buffer_destroy(alg); + /* key bits */ + uint32_t bits = 0; + assert_rnp_success(rnp_key_get_bits(key, &bits)); + assert_int_equal(bits, 255); + /* curve */ + char *curve = NULL; + assert_rnp_success(rnp_key_get_curve(key, &curve)); + assert_int_equal(strcasecmp(curve, "ed25519"), 0); + rnp_buffer_destroy(curve); + /* key flags */ + bool flag = false; + assert_rnp_success(rnp_key_allows_usage(key, "sign", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(key, "certify", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(key, "encrypt", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(key, "authenticate", &flag)); + assert_false(flag); + /* user ids */ + size_t uids = 0; + char * uid = NULL; + assert_rnp_success(rnp_key_get_uid_count(key, &uids)); + assert_int_equal(uids, 1); + assert_rnp_success(rnp_key_get_uid_at(key, 0, &uid)); + assert_string_equal(uid, "eddsa_25519"); + rnp_buffer_destroy(uid); + /* subkey */ + size_t subkeys = 0; + assert_rnp_success(rnp_key_get_subkey_count(key, &subkeys)); + assert_int_equal(subkeys, 1); + rnp_key_handle_t subkey = NULL; + assert_rnp_success(rnp_key_get_subkey_at(key, 0, &subkey)); + /* check properties of the generated subkey */ + assert_rnp_success(rnp_key_is_primary(subkey, &boolres)); + assert_false(boolres); + assert_rnp_success(rnp_key_have_public(subkey, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_have_secret(subkey, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_is_protected(subkey, &boolres)); + assert_false(boolres); + assert_rnp_success(rnp_key_is_locked(subkey, &boolres)); + assert_false(boolres); + /* algorithm */ + assert_rnp_success(rnp_key_get_alg(subkey, &alg)); + assert_int_equal(strcasecmp(alg, "ECDH"), 0); + rnp_buffer_destroy(alg); + /* key bits */ + assert_rnp_success(rnp_key_get_bits(subkey, &bits)); + assert_int_equal(bits, 255); + /* curve */ + curve = NULL; + assert_rnp_success(rnp_key_get_curve(subkey, &curve)); + assert_int_equal(strcasecmp(curve, "Curve25519"), 0); + rnp_buffer_destroy(curve); + /* subkey flags */ + assert_rnp_success(rnp_key_allows_usage(subkey, "sign", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "certify", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "encrypt", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "authenticate", &flag)); + assert_false(flag); + + assert_rnp_success(rnp_key_handle_destroy(subkey)); + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success(rnp_ffi_destroy(ffi)); +} + +TEST_F(rnp_tests, test_ffi_key_generate_sm2) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_ffi_set_key_provider(ffi, unused_getkeycb, NULL)); + + /* generate sm2 key */ + rnp_key_handle_t key = NULL; + if (!sm2_enabled()) { + assert_rnp_failure(rnp_generate_key_sm2(ffi, "sm2", NULL, &key)); + assert_rnp_success(rnp_ffi_destroy(ffi)); + return; + } + assert_rnp_success(rnp_generate_key_sm2(ffi, "sm2", NULL, &key)); + assert_non_null(key); + /* check properties of the generated key */ + bool boolres = false; + assert_rnp_success(rnp_key_is_primary(key, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_have_public(key, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_have_secret(key, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_is_protected(key, &boolres)); + assert_false(boolres); + assert_rnp_success(rnp_key_is_locked(key, &boolres)); + assert_false(boolres); + /* algorithm */ + char *alg = NULL; + assert_rnp_success(rnp_key_get_alg(key, &alg)); + assert_int_equal(strcasecmp(alg, "SM2"), 0); + rnp_buffer_destroy(alg); + /* key bits */ + uint32_t bits = 0; + assert_rnp_success(rnp_key_get_bits(key, &bits)); + assert_int_equal(bits, 256); + /* curve */ + char *curve = NULL; + assert_rnp_success(rnp_key_get_curve(key, &curve)); + assert_int_equal(strcasecmp(curve, "SM2 P-256"), 0); + rnp_buffer_destroy(curve); + /* key flags */ + bool flag = false; + assert_rnp_success(rnp_key_allows_usage(key, "sign", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(key, "certify", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(key, "encrypt", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(key, "authenticate", &flag)); + assert_false(flag); + /* user ids */ + size_t uids = 0; + char * uid = NULL; + assert_rnp_success(rnp_key_get_uid_count(key, &uids)); + assert_int_equal(uids, 1); + assert_rnp_success(rnp_key_get_uid_at(key, 0, &uid)); + assert_string_equal(uid, "sm2"); + rnp_buffer_destroy(uid); + /* subkey */ + size_t subkeys = 0; + assert_rnp_success(rnp_key_get_subkey_count(key, &subkeys)); + assert_int_equal(subkeys, 1); + rnp_key_handle_t subkey = NULL; + assert_rnp_success(rnp_key_get_subkey_at(key, 0, &subkey)); + /* check properties of the generated subkey */ + assert_rnp_success(rnp_key_is_primary(subkey, &boolres)); + assert_false(boolres); + assert_rnp_success(rnp_key_have_public(subkey, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_have_secret(subkey, &boolres)); + assert_true(boolres); + assert_rnp_success(rnp_key_is_protected(subkey, &boolres)); + assert_false(boolres); + assert_rnp_success(rnp_key_is_locked(subkey, &boolres)); + assert_false(boolres); + /* algorithm */ + assert_rnp_success(rnp_key_get_alg(subkey, &alg)); + assert_int_equal(strcasecmp(alg, "SM2"), 0); + rnp_buffer_destroy(alg); + /* key bits */ + assert_rnp_success(rnp_key_get_bits(subkey, &bits)); + assert_int_equal(bits, 256); + /* curve */ + curve = NULL; + assert_rnp_success(rnp_key_get_curve(subkey, &curve)); + assert_int_equal(strcasecmp(curve, "SM2 P-256"), 0); + rnp_buffer_destroy(curve); + /* subkey flags */ + assert_rnp_success(rnp_key_allows_usage(subkey, "sign", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "certify", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "encrypt", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "authenticate", &flag)); + assert_false(flag); + + assert_rnp_success(rnp_key_handle_destroy(subkey)); + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success(rnp_ffi_destroy(ffi)); +} + +TEST_F(rnp_tests, test_ffi_key_generate_ex) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_ffi_set_key_provider(ffi, unused_getkeycb, NULL)); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "123")); + + /* Generate RSA key with misc options set */ + rnp_op_generate_t keygen = NULL; + assert_rnp_success(rnp_op_generate_create(&keygen, ffi, "RSA")); + assert_rnp_success(rnp_op_generate_set_bits(keygen, 1024)); + assert_rnp_failure(rnp_op_generate_set_dsa_qbits(keygen, 256)); + /* key usage */ + assert_rnp_success(rnp_op_generate_clear_usage(keygen)); + assert_rnp_failure(rnp_op_generate_add_usage(keygen, "usage")); + assert_rnp_success(rnp_op_generate_add_usage(keygen, "sign")); + assert_rnp_success(rnp_op_generate_add_usage(keygen, "encrypt")); + /* preferred ciphers */ + assert_rnp_success(rnp_op_generate_clear_pref_ciphers(keygen)); + assert_rnp_failure(rnp_op_generate_add_pref_cipher(keygen, "unknown")); + assert_true(!rnp_op_generate_add_pref_cipher(keygen, "BLOWFISH") == blowfish_enabled()); + assert_rnp_success(rnp_op_generate_clear_pref_ciphers(keygen)); + assert_rnp_success(rnp_op_generate_add_pref_cipher(keygen, "CAMELLIA256")); + assert_rnp_success(rnp_op_generate_add_pref_cipher(keygen, "AES256")); + /* preferred compression algorithms */ + assert_rnp_success(rnp_op_generate_clear_pref_compression(keygen)); + assert_rnp_failure(rnp_op_generate_add_pref_compression(keygen, "unknown")); + assert_rnp_success(rnp_op_generate_add_pref_compression(keygen, "zlib")); + assert_rnp_success(rnp_op_generate_clear_pref_compression(keygen)); + assert_rnp_success(rnp_op_generate_add_pref_compression(keygen, "zip")); + assert_rnp_success(rnp_op_generate_add_pref_compression(keygen, "zlib")); + /* preferred hash algorithms */ + assert_rnp_success(rnp_op_generate_clear_pref_hashes(keygen)); + assert_rnp_failure(rnp_op_generate_add_pref_hash(keygen, "unknown")); + assert_rnp_success(rnp_op_generate_add_pref_hash(keygen, "SHA1")); + assert_rnp_success(rnp_op_generate_clear_pref_hashes(keygen)); + assert_rnp_success(rnp_op_generate_add_pref_hash(keygen, "SHA512")); + assert_rnp_success(rnp_op_generate_add_pref_hash(keygen, "SHA256")); + /* key expiration */ + assert_rnp_success(rnp_op_generate_set_expiration(keygen, 60 * 60 * 24 * 100)); + assert_rnp_success(rnp_op_generate_set_expiration(keygen, 60 * 60 * 24 * 300)); + /* preferred key server */ + assert_rnp_success(rnp_op_generate_set_pref_keyserver(keygen, NULL)); + assert_rnp_success(rnp_op_generate_set_pref_keyserver(keygen, "hkp://first.server/")); + assert_rnp_success(rnp_op_generate_set_pref_keyserver(keygen, "hkp://second.server/")); + /* user id */ + assert_rnp_failure(rnp_op_generate_set_userid(keygen, NULL)); + assert_rnp_success(rnp_op_generate_set_userid(keygen, "userid_cleared")); + assert_rnp_success(rnp_op_generate_set_userid(keygen, "userid")); + /* protection */ + assert_rnp_failure(rnp_op_generate_set_protection_cipher(keygen, NULL)); + assert_rnp_failure(rnp_op_generate_set_protection_cipher(keygen, "unknown")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "AES256")); + assert_rnp_failure(rnp_op_generate_set_protection_hash(keygen, NULL)); + assert_rnp_failure(rnp_op_generate_set_protection_hash(keygen, "unknown")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "SHA256")); + assert_rnp_success(rnp_op_generate_set_protection_iterations(keygen, 65536)); + assert_rnp_failure(rnp_op_generate_set_protection_mode(keygen, NULL)); + assert_rnp_failure(rnp_op_generate_set_protection_mode(keygen, "unknown")); + assert_rnp_success(rnp_op_generate_set_protection_mode(keygen, "cfb")); + /* now execute keygen operation */ + assert_rnp_success(rnp_op_generate_set_request_password(keygen, true)); + assert_rnp_success(rnp_op_generate_execute(keygen)); + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_op_generate_get_key(keygen, &key)); + assert_non_null(key); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + /* now check key usage */ + bool flag = false; + assert_rnp_success(rnp_key_allows_usage(key, "sign", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(key, "certify", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(key, "encrypt", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(key, "authenticate", &flag)); + assert_false(flag); + /* check key creation and expiration */ + uint32_t create = 0; + assert_rnp_success(rnp_key_get_creation(key, &create)); + assert_true((create != 0) && (create <= time(NULL))); + uint32_t expiry = 0; + assert_rnp_success(rnp_key_get_expiration(key, &expiry)); + assert_true(expiry == 60 * 60 * 24 * 300); + uint32_t till = 0; + assert_rnp_success(rnp_key_valid_till(key, &till)); + assert_int_equal(till, create + expiry); + /* check whether key is encrypted */ + assert_rnp_success(rnp_key_is_protected(key, &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_is_locked(key, &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_unlock(key, "123")); + assert_rnp_success(rnp_key_is_locked(key, &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_lock(key)); + + /* generate DSA subkey */ + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "DSA")); + assert_rnp_success(rnp_op_generate_set_bits(keygen, 1536)); + assert_rnp_success(rnp_op_generate_set_dsa_qbits(keygen, 224)); + /* key flags */ + assert_rnp_failure(rnp_op_generate_add_usage(keygen, "encrypt")); + assert_rnp_success(rnp_op_generate_add_usage(keygen, "certify")); + /* these should not work for subkey */ + assert_rnp_failure(rnp_op_generate_clear_pref_ciphers(keygen)); + assert_rnp_failure(rnp_op_generate_add_pref_cipher(keygen, "AES256")); + assert_rnp_failure(rnp_op_generate_clear_pref_compression(keygen)); + assert_rnp_failure(rnp_op_generate_add_pref_compression(keygen, "zlib")); + assert_rnp_failure(rnp_op_generate_clear_pref_hashes(keygen)); + assert_rnp_failure(rnp_op_generate_add_pref_hash(keygen, "unknown")); + assert_rnp_failure(rnp_op_generate_set_pref_keyserver(keygen, "hkp://first.server/")); + assert_rnp_failure(rnp_op_generate_set_userid(keygen, "userid")); + /* key expiration */ + assert_rnp_success(rnp_op_generate_set_expiration(keygen, 60 * 60 * 24 * 300)); + /* key protection */ + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "AES256")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "SHA256")); + assert_rnp_success(rnp_op_generate_set_protection_iterations(keygen, 65536)); + assert_rnp_success(rnp_op_generate_set_request_password(keygen, true)); + /* now generate the subkey */ + assert_rnp_success(rnp_op_generate_execute(keygen)); + rnp_key_handle_t subkey = NULL; + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + /* now check subkey usage */ + assert_rnp_success(rnp_key_allows_usage(subkey, "sign", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "certify", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "encrypt", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "authenticate", &flag)); + assert_false(flag); + /* check subkey creation and expiration */ + create = 0; + assert_rnp_success(rnp_key_get_creation(subkey, &create)); + assert_true((create != 0) && (create <= time(NULL))); + expiry = 0; + assert_rnp_success(rnp_key_get_expiration(subkey, &expiry)); + assert_true(expiry == 60 * 60 * 24 * 300); + /* check whether subkey is encrypted */ + assert_rnp_success(rnp_key_is_protected(subkey, &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_is_locked(subkey, &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_unlock(subkey, "123")); + assert_rnp_success(rnp_key_is_locked(subkey, &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_lock(subkey)); + /* destroy key handle */ + assert_rnp_success(rnp_key_handle_destroy(subkey)); + + /* generate RSA sign/encrypt subkey */ + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "RSA")); + assert_rnp_success(rnp_op_generate_set_bits(keygen, 1024)); + assert_rnp_success(rnp_op_generate_add_usage(keygen, "sign")); + assert_rnp_success(rnp_op_generate_add_usage(keygen, "encrypt")); + assert_rnp_success(rnp_op_generate_set_expiration(keygen, 0)); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "AES128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "SHA1")); + /* set bits for iterations instead of exact iterations number */ + assert_rnp_success(rnp_op_generate_set_protection_iterations(keygen, 12)); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + /* now check subkey usage */ + assert_rnp_success(rnp_key_allows_usage(subkey, "sign", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "certify", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "encrypt", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "authenticate", &flag)); + assert_false(flag); + /* check whether subkey is encrypted - it should not */ + assert_rnp_success(rnp_key_is_protected(subkey, &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + + /* generate ElGamal subkey */ + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ELGAMAL")); + assert_rnp_success(rnp_op_generate_set_bits(keygen, 1024)); + assert_rnp_failure(rnp_op_generate_add_usage(keygen, "sign")); + assert_rnp_success(rnp_op_generate_add_usage(keygen, "encrypt")); + assert_rnp_success(rnp_op_generate_set_expiration(keygen, 0)); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "AES128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "SHA1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + /* now check subkey usage */ + assert_rnp_success(rnp_key_allows_usage(subkey, "sign", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "certify", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "encrypt", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "authenticate", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + + /* generate ECDSA subkeys for each curve */ + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ECDSA")); + assert_rnp_failure(rnp_op_generate_set_bits(keygen, 1024)); + assert_rnp_failure(rnp_op_generate_set_dsa_qbits(keygen, 1024)); + assert_rnp_success(rnp_op_generate_add_usage(keygen, "sign")); + assert_rnp_failure(rnp_op_generate_add_usage(keygen, "encrypt")); + assert_rnp_success(rnp_op_generate_set_curve(keygen, "NIST P-256")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "AES128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "SHA1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + /* now check subkey usage */ + assert_rnp_success(rnp_key_allows_usage(subkey, "sign", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "certify", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "encrypt", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "authenticate", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ECDSA")); + assert_rnp_success(rnp_op_generate_set_curve(keygen, "NIST P-384")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "AES128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "SHA1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ECDSA")); + assert_rnp_success(rnp_op_generate_set_curve(keygen, "NIST P-521")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "AES128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "SHA1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ECDSA")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "AES128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "SHA1")); + if (brainpool_enabled()) { + assert_rnp_success(rnp_op_generate_set_curve(keygen, "brainpoolP256r1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + } else { + assert_rnp_failure(rnp_op_generate_set_curve(keygen, "brainpoolP256r1")); + assert_rnp_failure(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + } + + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ECDSA")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "AES128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "SHA1")); + if (brainpool_enabled()) { + assert_rnp_success(rnp_op_generate_set_curve(keygen, "brainpoolP384r1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + } else { + assert_rnp_failure(rnp_op_generate_set_curve(keygen, "brainpoolP384r1")); + assert_rnp_failure(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + } + + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ECDSA")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "AES128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "SHA1")); + if (brainpool_enabled()) { + assert_rnp_success(rnp_op_generate_set_curve(keygen, "brainpoolP512r1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + } else { + assert_rnp_failure(rnp_op_generate_set_curve(keygen, "brainpoolP512r1")); + assert_rnp_failure(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + } + + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ECDSA")); + assert_rnp_success(rnp_op_generate_set_curve(keygen, "secp256k1")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "AES128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "SHA1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + + /* These curves will not work with ECDSA*/ + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ECDSA")); + assert_rnp_success(rnp_op_generate_set_curve(keygen, "Ed25519")); + assert_rnp_failure(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ECDSA")); + assert_rnp_success(rnp_op_generate_set_curve(keygen, "Curve25519")); + assert_rnp_failure(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ECDSA")); + if (!sm2_enabled()) { + assert_rnp_failure(rnp_op_generate_set_curve(keygen, "SM2 P-256")); + } else { + assert_rnp_success(rnp_op_generate_set_curve(keygen, "SM2 P-256")); + assert_rnp_failure(rnp_op_generate_execute(keygen)); + } + assert_rnp_success(rnp_op_generate_destroy(keygen)); + + /* Add EDDSA subkey */ + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "EDDSA")); + assert_rnp_failure(rnp_op_generate_set_curve(keygen, "secp256k1")); + assert_rnp_success(rnp_op_generate_add_usage(keygen, "sign")); + assert_rnp_failure(rnp_op_generate_add_usage(keygen, "encrypt")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "AES128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "SHA1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + /* now check subkey usage */ + assert_rnp_success(rnp_key_allows_usage(subkey, "sign", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "certify", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "encrypt", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "authenticate", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + + /* Add ECDH subkey */ + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ECDH")); + assert_rnp_success(rnp_op_generate_set_curve(keygen, "NIST P-256")); + assert_rnp_failure(rnp_op_generate_add_usage(keygen, "sign")); + assert_rnp_success(rnp_op_generate_add_usage(keygen, "encrypt")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "AES128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "SHA1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + /* now check subkey usage */ + assert_rnp_success(rnp_key_allows_usage(subkey, "sign", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "certify", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "encrypt", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "authenticate", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + + /* Add ECDH x25519 subkey */ + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ECDH")); + assert_rnp_success(rnp_op_generate_set_curve(keygen, "Curve25519")); + assert_rnp_failure(rnp_op_generate_add_usage(keygen, "sign")); + assert_rnp_success(rnp_op_generate_add_usage(keygen, "encrypt")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "AES128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "SHA1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + /* now check subkey usage */ + assert_rnp_success(rnp_key_allows_usage(subkey, "sign", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "certify", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "encrypt", &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_allows_usage(subkey, "authenticate", &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + + /* Add SM2 subkey */ + if (!sm2_enabled()) { + keygen = NULL; + assert_rnp_failure(rnp_op_generate_subkey_create(&keygen, ffi, key, "SM2")); + } else { + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "SM2")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "AES128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "SHA1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + } + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success(rnp_ffi_destroy(ffi)); +} + +TEST_F(rnp_tests, test_ffi_key_generate_expiry_32bit) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_ffi_set_key_provider(ffi, unused_getkeycb, NULL)); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "123")); + + /* Generate RSA key with creation + expiration > 32 bit */ + rnp_op_generate_t keygen = NULL; + assert_rnp_success(rnp_op_generate_create(&keygen, ffi, "RSA")); + assert_rnp_success(rnp_op_generate_set_bits(keygen, 1024)); + /* key expiration */ + assert_rnp_success(rnp_op_generate_set_expiration(keygen, UINT32_MAX)); + /* now execute keygen operation */ + assert_rnp_success(rnp_op_generate_set_request_password(keygen, true)); + assert_rnp_success(rnp_op_generate_execute(keygen)); + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_op_generate_get_key(keygen, &key)); + assert_non_null(key); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + /* check key creation and expiration */ + uint32_t create = 0; + assert_rnp_success(rnp_key_get_creation(key, &create)); + assert_true((create != 0) && (create <= time(NULL))); + uint32_t expiry = 0; + assert_rnp_success(rnp_key_get_expiration(key, &expiry)); + assert_true(expiry == UINT32_MAX); + uint32_t till = 0; + assert_rnp_success(rnp_key_valid_till(key, &till)); + assert_int_equal(till, UINT32_MAX - 1); + uint64_t till64 = 0; + assert_rnp_success(rnp_key_valid_till64(key, &till64)); + assert_int_equal(till64, (uint64_t) create + expiry); + assert_rnp_success(rnp_key_handle_destroy(key)); + + /* Load key with creation + expiration == UINT32_MAX */ + assert_true(import_pub_keys(ffi, "data/test_key_edge_cases/key-create-expiry-32bit.asc")); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "60eac9ddf0d9ac9f", &key)); + /* check key creation and expiration */ + create = 0; + assert_rnp_success(rnp_key_get_creation(key, &create)); + assert_int_equal(create, 1619611313); + expiry = 0; + assert_rnp_success(rnp_key_get_expiration(key, &expiry)); + assert_true(expiry == UINT32_MAX - create); + till = 0; + assert_rnp_success(rnp_key_valid_till(key, &till)); + assert_int_equal(till, UINT32_MAX - 1); + till64 = 0; + assert_rnp_success(rnp_key_valid_till64(key, &till64)); + assert_int_equal(till64, UINT32_MAX); + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success(rnp_ffi_destroy(ffi)); +} + +TEST_F(rnp_tests, test_ffi_key_generate_algnamecase) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_ffi_set_key_provider(ffi, unused_getkeycb, NULL)); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "123")); + + /* Generate RSA key with misc options set */ + rnp_op_generate_t keygen = NULL; + assert_rnp_success(rnp_op_generate_create(&keygen, ffi, "rsa")); + assert_rnp_success(rnp_op_generate_set_bits(keygen, 1024)); + assert_rnp_success(rnp_op_generate_execute(keygen)); + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_op_generate_get_key(keygen, &key)); + assert_non_null(key); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + + /* generate DSA subkey */ + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "dsa")); + assert_rnp_success(rnp_op_generate_set_bits(keygen, 1536)); + /* now generate the subkey */ + assert_rnp_success(rnp_op_generate_execute(keygen)); + rnp_key_handle_t subkey = NULL; + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + /* destroy key handle */ + assert_rnp_success(rnp_key_handle_destroy(subkey)); + + /* generate ElGamal subkey */ + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "elgamal")); + assert_rnp_success(rnp_op_generate_set_bits(keygen, 1024)); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + + /* generate ECDSA subkeys for each curve */ + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ecdsa")); + assert_rnp_failure(rnp_op_generate_set_bits(keygen, 1024)); + assert_rnp_success(rnp_op_generate_set_curve(keygen, "NIST P-256")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "aes128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "sha1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ecdsa")); + assert_rnp_success(rnp_op_generate_set_curve(keygen, "NIST P-384")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "aes128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "sha1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ecdsa")); + assert_rnp_success(rnp_op_generate_set_curve(keygen, "NIST P-521")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "aes128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "sha1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ecdsa")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "aes128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "sha1")); + if (brainpool_enabled()) { + assert_rnp_success(rnp_op_generate_set_curve(keygen, "brainpoolP256r1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + } else { + assert_rnp_failure(rnp_op_generate_set_curve(keygen, "brainpoolP256r1")); + assert_rnp_failure(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + } + + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ecdsa")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "aes128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "sha1")); + if (brainpool_enabled()) { + assert_rnp_success(rnp_op_generate_set_curve(keygen, "brainpoolP384r1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + } else { + assert_rnp_failure(rnp_op_generate_set_curve(keygen, "brainpoolP384r1")); + assert_rnp_failure(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + } + + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ecdsa")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "aes128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "sha1")); + if (brainpool_enabled()) { + assert_rnp_success(rnp_op_generate_set_curve(keygen, "brainpoolP512r1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + } else { + assert_rnp_failure(rnp_op_generate_set_curve(keygen, "brainpoolP512r1")); + assert_rnp_failure(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + } + + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ecdsa")); + assert_rnp_success(rnp_op_generate_set_curve(keygen, "secp256k1")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "aes128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "sha1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + + /* These curves will not work with ECDSA */ + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ecdsa")); + assert_rnp_success(rnp_op_generate_set_curve(keygen, "Ed25519")); + assert_rnp_failure(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ecdsa")); + assert_rnp_success(rnp_op_generate_set_curve(keygen, "Curve25519")); + assert_rnp_failure(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ecdsa")); + if (!sm2_enabled()) { + assert_rnp_failure(rnp_op_generate_set_curve(keygen, "SM2 P-256")); + } else { + assert_rnp_success(rnp_op_generate_set_curve(keygen, "SM2 P-256")); + assert_rnp_failure(rnp_op_generate_execute(keygen)); + } + assert_rnp_success(rnp_op_generate_destroy(keygen)); + + /* Add EDDSA subkey */ + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "eddsa")); + assert_rnp_failure(rnp_op_generate_set_curve(keygen, "secp256k1")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "aes128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "sha1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + + /* Add ECDH subkey */ + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ecdh")); + assert_rnp_success(rnp_op_generate_set_curve(keygen, "NIST P-256")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "aes128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "sha1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + + /* Add ECDH x25519 subkey */ + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "ecdh")); + assert_rnp_success(rnp_op_generate_set_curve(keygen, "Curve25519")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "aes128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "sha1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + + /* Add SM2 subkey */ + if (!sm2_enabled()) { + keygen = NULL; + assert_rnp_failure(rnp_op_generate_subkey_create(&keygen, ffi, key, "sm2")); + } else { + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "sm2")); + assert_rnp_success(rnp_op_generate_set_protection_cipher(keygen, "aes128")); + assert_rnp_success(rnp_op_generate_set_protection_hash(keygen, "sha1")); + assert_rnp_success(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + } + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success(rnp_ffi_destroy(ffi)); +} + +TEST_F(rnp_tests, test_ffi_key_generate_protection) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_ffi_set_key_provider(ffi, unused_getkeycb, NULL)); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "123")); + + /* Generate key and subkey without protection */ + rnp_op_generate_t keygen = NULL; + assert_rnp_success(rnp_op_generate_create(&keygen, ffi, "RSA")); + assert_rnp_success(rnp_op_generate_set_bits(keygen, 1024)); + assert_rnp_success(rnp_op_generate_execute(keygen)); + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_op_generate_get_key(keygen, &key)); + assert_non_null(key); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + /* check whether key is encrypted */ + bool flag = true; + assert_rnp_success(rnp_key_is_protected(key, &flag)); + assert_false(flag); + /* generate subkey */ + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "RSA")); + assert_rnp_success(rnp_op_generate_set_bits(keygen, 1024)); + assert_rnp_success(rnp_op_generate_execute(keygen)); + rnp_key_handle_t subkey = NULL; + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_is_protected(subkey, &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + assert_rnp_success(rnp_key_handle_destroy(key)); + + /* Generate RSA key with password */ + assert_rnp_success(rnp_op_generate_create(&keygen, ffi, "RSA")); + assert_rnp_success(rnp_op_generate_set_bits(keygen, 1024)); + assert_rnp_success(rnp_op_generate_set_protection_password(keygen, "password")); + /* Line below should not change password from 'password' to '123' */ + assert_rnp_success(rnp_op_generate_set_request_password(keygen, true)); + assert_rnp_success(rnp_op_generate_execute(keygen)); + key = NULL; + assert_rnp_success(rnp_op_generate_get_key(keygen, &key)); + assert_non_null(key); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + /* check whether key is encrypted */ + assert_rnp_success(rnp_key_is_protected(key, &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_is_locked(key, &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_unlock(key, "password")); + assert_rnp_success(rnp_key_is_locked(key, &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_lock(key)); + /* generate subkey */ + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "RSA")); + assert_rnp_success(rnp_op_generate_set_bits(keygen, 1024)); + assert_rnp_success(rnp_op_generate_set_protection_password(keygen, "password")); + /* this should fail since primary key is locked */ + assert_rnp_failure(rnp_op_generate_execute(keygen)); + assert_rnp_success(rnp_key_unlock(key, "password")); + /* now it should work */ + assert_rnp_success(rnp_op_generate_execute(keygen)); + subkey = NULL; + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_is_protected(subkey, &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_is_locked(subkey, &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_unlock(subkey, "password")); + assert_rnp_success(rnp_key_is_locked(subkey, &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + assert_rnp_success(rnp_key_handle_destroy(key)); + + /* Generate RSA key via password request */ + assert_rnp_success(rnp_op_generate_create(&keygen, ffi, "RSA")); + assert_rnp_success(rnp_op_generate_set_bits(keygen, 1024)); + assert_rnp_success(rnp_op_generate_set_request_password(keygen, true)); + assert_rnp_success(rnp_op_generate_execute(keygen)); + key = NULL; + assert_rnp_success(rnp_op_generate_get_key(keygen, &key)); + assert_non_null(key); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + /* check whether key is encrypted */ + assert_rnp_success(rnp_key_is_protected(key, &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_is_locked(key, &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_unlock(key, "123")); + assert_rnp_success(rnp_key_is_locked(key, &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_lock(key)); + /* generate subkey */ + assert_rnp_success(rnp_op_generate_subkey_create(&keygen, ffi, key, "RSA")); + assert_rnp_success(rnp_op_generate_set_bits(keygen, 1024)); + assert_rnp_success(rnp_op_generate_set_request_password(keygen, true)); + /* this should succeed since password for primary key is returned via provider */ + assert_rnp_success(rnp_op_generate_execute(keygen)); + subkey = NULL; + assert_rnp_success(rnp_op_generate_get_key(keygen, &subkey)); + assert_non_null(subkey); + assert_rnp_success(rnp_op_generate_destroy(keygen)); + assert_rnp_success(rnp_key_is_protected(subkey, &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_is_locked(subkey, &flag)); + assert_true(flag); + assert_rnp_success(rnp_key_unlock(subkey, "123")); + assert_rnp_success(rnp_key_is_locked(subkey, &flag)); + assert_false(flag); + assert_rnp_success(rnp_key_handle_destroy(subkey)); + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success(rnp_ffi_destroy(ffi)); +} + +TEST_F(rnp_tests, test_ffi_keygen_json_sub_pass_required) +{ + char * results = NULL; + size_t count = 0; + rnp_ffi_t ffi = NULL; + + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_ffi_set_key_provider(ffi, unused_getkeycb, NULL)); + assert_rnp_success(rnp_ffi_set_pass_provider(ffi, unused_getpasscb, NULL)); + + // generate our primary key + auto json = file_to_str("data/test_ffi_json/generate-primary.json"); + assert_rnp_success(rnp_generate_key_json(ffi, json.c_str(), &results)); + assert_non_null(results); + // check key counts + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(1, count); + assert_rnp_success(rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(1, count); + + // parse the results JSON + json_object *parsed_results = json_tokener_parse(results); + assert_non_null(parsed_results); + rnp_buffer_destroy(results); + results = NULL; + // get a handle+grip for the primary + rnp_key_handle_t primary = NULL; + char * primary_grip = NULL; + { + json_object *jsokey = NULL; + assert_int_equal(true, json_object_object_get_ex(parsed_results, "primary", &jsokey)); + assert_non_null(jsokey); + json_object *jsogrip = NULL; + assert_int_equal(true, json_object_object_get_ex(jsokey, "grip", &jsogrip)); + assert_non_null(jsogrip); + primary_grip = strdup(json_object_get_string(jsogrip)); + assert_non_null(primary_grip); + assert_rnp_success(rnp_locate_key(ffi, "grip", primary_grip, &primary)); + assert_non_null(primary); + } + // cleanup + json_object_put(parsed_results); + parsed_results = NULL; + + // protect+lock the primary key + assert_rnp_success(rnp_key_protect(primary, "pass123", NULL, NULL, NULL, 0)); + assert_rnp_success(rnp_key_lock(primary)); + rnp_key_handle_destroy(primary); + primary = NULL; + + // load our JSON template + json = file_to_str("data/test_ffi_json/generate-sub.json"); + // modify our JSON + { + // parse + json_object *jso = json_tokener_parse(json.c_str()); + assert_non_null(jso); + // find the relevant fields + json_object *jsosub = NULL; + json_object *jsoprimary = NULL; + assert_true(json_object_object_get_ex(jso, "sub", &jsosub)); + assert_non_null(jsosub); + assert_true(json_object_object_get_ex(jsosub, "primary", &jsoprimary)); + assert_non_null(jsoprimary); + // replace the placeholder grip with the correct one + json_object_object_del(jsoprimary, "grip"); + json_object_object_add(jsoprimary, "grip", json_object_new_string(primary_grip)); + assert_int_equal(1, json_object_object_length(jsoprimary)); + json = json_object_to_json_string_ext(jso, JSON_C_TO_STRING_PRETTY); + assert_false(json.empty()); + json_object_put(jso); + } + // cleanup + rnp_buffer_destroy(primary_grip); + primary_grip = NULL; + + // generate the subkey (no ffi_string_password_provider, should fail) + assert_rnp_success(rnp_ffi_set_key_provider(ffi, unused_getkeycb, NULL)); + assert_rnp_success(rnp_ffi_set_pass_provider(ffi, NULL, NULL)); + assert_rnp_failure(rnp_generate_key_json(ffi, json.c_str(), &results)); + + // generate the subkey (wrong pass, should fail) + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "wrong")); + assert_rnp_failure(rnp_generate_key_json(ffi, json.c_str(), &results)); + + // generate the subkey + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "pass123")); + assert_rnp_success(rnp_generate_key_json(ffi, json.c_str(), &results)); + assert_non_null(results); + + // parse the results JSON + parsed_results = json_tokener_parse(results); + assert_non_null(parsed_results); + rnp_buffer_destroy(results); + results = NULL; + // get a handle for the sub + rnp_key_handle_t sub = NULL; + { + json_object *jsokey = NULL; + assert_int_equal(true, json_object_object_get_ex(parsed_results, "sub", &jsokey)); + assert_non_null(jsokey); + json_object *jsogrip = NULL; + assert_int_equal(true, json_object_object_get_ex(jsokey, "grip", &jsogrip)); + assert_non_null(jsogrip); + const char *grip = json_object_get_string(jsogrip); + assert_non_null(grip); + assert_rnp_success(rnp_locate_key(ffi, "grip", grip, &sub)); + assert_non_null(sub); + } + // cleanup + json_object_put(parsed_results); + parsed_results = NULL; + + // check the key counts + assert_int_equal(RNP_SUCCESS, rnp_get_public_key_count(ffi, &count)); + assert_int_equal(2, count); + assert_int_equal(RNP_SUCCESS, rnp_get_secret_key_count(ffi, &count)); + assert_int_equal(2, count); + + // check some key properties + check_key_properties(sub, false, true, true); + + // cleanup + rnp_key_handle_destroy(primary); + rnp_key_handle_destroy(sub); + rnp_ffi_destroy(ffi); +} + +/** get the value of a (potentially nested) field in a json object + * + * Note that this does not support JSON arrays, only objects. + * + * @param jso the json object to search within. This should be an object, not a string, + * array, etc. + * @param field the field to retrieve. The format is "first.second.third". + * @return a pointer to the located json object, or NULL + **/ +static json_object * +get_json_obj(json_object *jso, const char *field) +{ + const char *start = field; + const char *end; + char buf[32]; + + do { + end = strchr(start, '.'); + + size_t len = end ? (end - start) : strlen(start); + if (len >= sizeof(buf)) { + return NULL; + } + memcpy(buf, start, len); + buf[len] = '\0'; + + if (!json_object_object_get_ex(jso, buf, &jso)) { + return NULL; + } + + start = end + 1; + } while (end); + return jso; +} + +/* This test loads a keyring and converts the keys to JSON, + * then validates some properties. + * + * We could just do a simple strcmp, but that would depend + * on json-c sorting the keys consistently, across versions, + * etc. + */ +TEST_F(rnp_tests, test_ffi_key_to_json) +{ + rnp_ffi_t ffi = NULL; + char * pub_format = NULL; + char * pub_path = NULL; + char * sec_format = NULL; + char * sec_path = NULL; + rnp_key_handle_t key = NULL; + char * json = NULL; + json_object * jso = NULL; + + // detect the formats+paths + assert_rnp_success(rnp_detect_homedir_info( + "data/keyrings/5", &pub_format, &pub_path, &sec_format, &sec_path)); + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, pub_format, sec_format)); + // load our keyrings + assert_true(load_keys_gpg(ffi, pub_path, sec_path)); + // free formats+paths + rnp_buffer_destroy(pub_format); + pub_format = NULL; + rnp_buffer_destroy(pub_path); + pub_path = NULL; + rnp_buffer_destroy(sec_format); + sec_format = NULL; + rnp_buffer_destroy(sec_path); + sec_path = NULL; + + // locate key (primary) + key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "0E33FD46FF10F19C", &key)); + assert_non_null(key); + // convert to JSON + json = NULL; + assert_rnp_success(rnp_key_to_json(key, 0xff, &json)); + assert_non_null(json); + // parse it back in + jso = json_tokener_parse(json); + assert_non_null(jso); + // validate some properties + assert_true(rnp::str_case_eq(json_object_get_string(get_json_obj(jso, "type")), "ECDSA")); + assert_int_equal(json_object_get_int(get_json_obj(jso, "length")), 256); + assert_true( + rnp::str_case_eq(json_object_get_string(get_json_obj(jso, "curve")), "NIST P-256")); + assert_true(rnp::str_case_eq(json_object_get_string(get_json_obj(jso, "keyid")), + "0E33FD46FF10F19C")); + assert_true(rnp::str_case_eq(json_object_get_string(get_json_obj(jso, "fingerprint")), + "B6B5E497A177551ECB8862200E33FD46FF10F19C")); + assert_true(rnp::str_case_eq(json_object_get_string(get_json_obj(jso, "grip")), + "20A48B3C61525DCDF8B3B9D82C6BBCF4D8BFB5E5")); + assert_int_equal(json_object_get_boolean(get_json_obj(jso, "revoked")), false); + assert_int_equal(json_object_get_int64(get_json_obj(jso, "creation time")), 1511313500); + assert_int_equal(json_object_get_int64(get_json_obj(jso, "expiration")), 0); + // usage + assert_int_equal(json_object_array_length(get_json_obj(jso, "usage")), 2); + assert_true(rnp::str_case_eq( + json_object_get_string(json_object_array_get_idx(get_json_obj(jso, "usage"), 0)), + "sign")); + assert_true(rnp::str_case_eq( + json_object_get_string(json_object_array_get_idx(get_json_obj(jso, "usage"), 1)), + "certify")); + // primary key grip + assert_null(get_json_obj(jso, "primary key grip")); + // subkey grips + assert_int_equal(json_object_array_length(get_json_obj(jso, "subkey grips")), 1); + assert_true(rnp::str_case_eq( + json_object_get_string(json_object_array_get_idx(get_json_obj(jso, "subkey grips"), 0)), + "FFFA72FC225214DC712D0127172EE13E88AF93B4")); + // public key + assert_int_equal(json_object_get_boolean(get_json_obj(jso, "public key.present")), true); + assert_true( + rnp::str_case_eq(json_object_get_string(get_json_obj(jso, "public key.mpis.point")), + "04B0C6F2F585C1EEDF805C4492CB683839D5EAE6246420780F063D558" + "A33F607876BE6F818A665722F8204653CC4DCFAD4F4765521AC8A6E9F" + "793CEBAE8600BEEF")); + // secret key + assert_int_equal(json_object_get_boolean(get_json_obj(jso, "secret key.present")), true); + assert_true( + rnp::str_case_eq(json_object_get_string(get_json_obj(jso, "secret key.mpis.x")), + "46DE93CA439735F36B9CF228F10D8586DA824D88BBF4E24566D5312D061802C8")); + assert_int_equal(json_object_get_boolean(get_json_obj(jso, "secret key.locked")), false); + assert_int_equal(json_object_get_boolean(get_json_obj(jso, "secret key.protected")), + false); + // userids + assert_int_equal(json_object_array_length(get_json_obj(jso, "userids")), 1); + assert_true(rnp::str_case_eq( + json_object_get_string(json_object_array_get_idx(get_json_obj(jso, "userids"), 0)), + "test0")); + // signatures + assert_int_equal(json_object_array_length(get_json_obj(jso, "signatures")), 1); + json_object *jsosig = json_object_array_get_idx(get_json_obj(jso, "signatures"), 0); + assert_int_equal(json_object_get_int(get_json_obj(jsosig, "userid")), 0); + // TODO: other properties of signature + // cleanup + json_object_put(jso); + rnp_key_handle_destroy(key); + key = NULL; + rnp_buffer_destroy(json); + json = NULL; + + // locate key (sub) + assert_rnp_success(rnp_locate_key(ffi, "keyid", "074131BC8D16C5C9", &key)); + assert_non_null(key); + // convert to JSON + assert_rnp_success(rnp_key_to_json(key, 0xff, &json)); + assert_non_null(json); + // parse it back in + jso = json_tokener_parse(json); + assert_non_null(jso); + // validate some properties + assert_true(rnp::str_case_eq(json_object_get_string(get_json_obj(jso, "type")), "ECDH")); + assert_int_equal(json_object_get_int(get_json_obj(jso, "length")), 256); + assert_true( + rnp::str_case_eq(json_object_get_string(get_json_obj(jso, "curve")), "NIST P-256")); + assert_true(rnp::str_case_eq(json_object_get_string(get_json_obj(jso, "keyid")), + "074131BC8D16C5C9")); + assert_true(rnp::str_case_eq(json_object_get_string(get_json_obj(jso, "fingerprint")), + "481E6A41B10ECD71A477DB02074131BC8D16C5C9")); + // ECDH-specific + assert_true( + rnp::str_case_eq(json_object_get_string(get_json_obj(jso, "kdf hash")), "SHA256")); + assert_true(rnp::str_case_eq(json_object_get_string(get_json_obj(jso, "key wrap cipher")), + "AES128")); + assert_true(rnp::str_case_eq(json_object_get_string(get_json_obj(jso, "grip")), + "FFFA72FC225214DC712D0127172EE13E88AF93B4")); + assert_int_equal(json_object_get_boolean(get_json_obj(jso, "revoked")), false); + assert_int_equal(json_object_get_int64(get_json_obj(jso, "creation time")), 1511313500); + assert_int_equal(json_object_get_int64(get_json_obj(jso, "expiration")), 0); + // usage + assert_int_equal(json_object_array_length(get_json_obj(jso, "usage")), 1); + assert_true(rnp::str_case_eq( + json_object_get_string(json_object_array_get_idx(get_json_obj(jso, "usage"), 0)), + "encrypt")); + // primary key grip + assert_true(rnp::str_case_eq(json_object_get_string(get_json_obj(jso, "primary key grip")), + "20A48B3C61525DCDF8B3B9D82C6BBCF4D8BFB5E5")); + // subkey grips + assert_null(get_json_obj(jso, "subkey grips")); + // public key + assert_int_equal(json_object_get_boolean(get_json_obj(jso, "public key.present")), true); + assert_true(rnp::str_case_eq( + json_object_get_string(get_json_obj(jso, "public key.mpis.point")), + "04E2746BA4D180011B17A6909EABDBF2F3733674FBE00B20A3B857C2597233651544150B" + "896BCE7DCDF47C49FC1E12D5AD86384D26336A48A18845940A3F65F502")); + // secret key + assert_int_equal(json_object_get_boolean(get_json_obj(jso, "secret key.present")), true); + assert_true( + rnp::str_case_eq(json_object_get_string(get_json_obj(jso, "secret key.mpis.x")), + "DF8BEB7272117AD7AFE2B7E882453113059787FBC785C82F78624EE7EF2117FB")); + assert_int_equal(json_object_get_boolean(get_json_obj(jso, "secret key.locked")), false); + assert_int_equal(json_object_get_boolean(get_json_obj(jso, "secret key.protected")), + false); + // userids + assert_null(get_json_obj(jso, "userids")); + // signatures + assert_int_equal(json_object_array_length(get_json_obj(jso, "signatures")), 1); + jsosig = json_object_array_get_idx(get_json_obj(jso, "signatures"), 0); + assert_null(get_json_obj(jsosig, "userid")); + // TODO: other properties of signature + // cleanup + json_object_put(jso); + rnp_key_handle_destroy(key); + rnp_buffer_destroy(json); + + // cleanup + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_key_iter) +{ + rnp_ffi_t ffi = NULL; + char * pub_format = NULL; + char * pub_path = NULL; + char * sec_format = NULL; + char * sec_path = NULL; + + // detect the formats+paths + assert_rnp_success(rnp_detect_homedir_info( + "data/keyrings/1", &pub_format, &pub_path, &sec_format, &sec_path)); + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, pub_format, sec_format)); + + // test invalid identifier type + { + rnp_identifier_iterator_t it = NULL; + assert_rnp_failure(rnp_identifier_iterator_create(ffi, &it, "keyidz")); + assert_null(it); + } + + // test empty rings + // keyid + { + rnp_identifier_iterator_t it = NULL; + assert_rnp_success(rnp_identifier_iterator_create(ffi, &it, "keyid")); + assert_non_null(it); + const char *ident = NULL; + assert_rnp_success(rnp_identifier_iterator_next(it, &ident)); + assert_null(ident); + assert_rnp_success(rnp_identifier_iterator_destroy(it)); + } + // grip + { + rnp_identifier_iterator_t it = NULL; + assert_rnp_success(rnp_identifier_iterator_create(ffi, &it, "grip")); + assert_non_null(it); + const char *ident = NULL; + assert_rnp_success(rnp_identifier_iterator_next(it, &ident)); + assert_null(ident); + assert_rnp_success(rnp_identifier_iterator_destroy(it)); + } + // userid + { + rnp_identifier_iterator_t it = NULL; + assert_rnp_success(rnp_identifier_iterator_create(ffi, &it, "userid")); + assert_non_null(it); + const char *ident = NULL; + assert_rnp_success(rnp_identifier_iterator_next(it, &ident)); + assert_null(ident); + assert_rnp_success(rnp_identifier_iterator_destroy(it)); + } + // fingerprint + { + rnp_identifier_iterator_t it = NULL; + assert_rnp_success(rnp_identifier_iterator_create(ffi, &it, "fingerprint")); + assert_non_null(it); + const char *ident = NULL; + assert_rnp_success(rnp_identifier_iterator_next(it, &ident)); + assert_null(ident); + assert_rnp_success(rnp_identifier_iterator_destroy(it)); + } + + // load our keyrings + assert_true(load_keys_gpg(ffi, pub_path, sec_path)); + // free formats+paths + rnp_buffer_destroy(pub_format); + pub_format = NULL; + rnp_buffer_destroy(pub_path); + pub_path = NULL; + rnp_buffer_destroy(sec_format); + sec_format = NULL; + rnp_buffer_destroy(sec_path); + sec_path = NULL; + + // keyid + { + rnp_identifier_iterator_t it = NULL; + assert_rnp_success(rnp_identifier_iterator_create(ffi, &it, "keyid")); + assert_non_null(it); + { + static const char *expected[] = {"7BC6709B15C23A4A", + "1ED63EE56FADC34D", + "1D7E8A5393C997A8", + "8A05B89FAD5ADED1", + "2FCADF05FFA501BB", + "54505A936A4A970E", + "326EF111425D14A5"}; + size_t i = 0; + const char * ident = NULL; + do { + ident = NULL; + assert_rnp_success(rnp_identifier_iterator_next(it, &ident)); + if (ident) { + assert_true(rnp::str_case_eq(expected[i], ident)); + i++; + } + } while (ident); + assert_int_equal(i, ARRAY_SIZE(expected)); + } + assert_rnp_success(rnp_identifier_iterator_destroy(it)); + } + + // grip + { + rnp_identifier_iterator_t it = NULL; + assert_rnp_success(rnp_identifier_iterator_create(ffi, &it, "grip")); + assert_non_null(it); + { + static const char *expected[] = {"66D6A0800A3FACDE0C0EB60B16B3669ED380FDFA", + "D9839D61EDAF0B3974E0A4A341D6E95F3479B9B7", + "B1CC352FEF9A6BD4E885B5351840EF9306D635F0", + "E7C8860B70DC727BED6DB64C633683B41221BB40", + "B2A7F6C34AA2C15484783E9380671869A977A187", + "43C01D6D96BE98C3C87FE0F175870ED92DE7BE45", + "8082FE753013923972632550838A5F13D81F43B9"}; + size_t i = 0; + const char * ident = NULL; + do { + ident = NULL; + assert_rnp_success(rnp_identifier_iterator_next(it, &ident)); + if (ident) { + assert_true(rnp::str_case_eq(expected[i], ident)); + i++; + } + } while (ident); + assert_int_equal(i, ARRAY_SIZE(expected)); + } + assert_rnp_success(rnp_identifier_iterator_destroy(it)); + } + + // userid + { + rnp_identifier_iterator_t it = NULL; + assert_rnp_success(rnp_identifier_iterator_create(ffi, &it, "userid")); + assert_non_null(it); + { + static const char *expected[] = { + "key0-uid0", "key0-uid1", "key0-uid2", "key1-uid0", "key1-uid2", "key1-uid1"}; + size_t i = 0; + const char *ident = NULL; + do { + ident = NULL; + assert_rnp_success(rnp_identifier_iterator_next(it, &ident)); + if (ident) { + assert_true(rnp::str_case_eq(expected[i], ident)); + i++; + } + } while (ident); + assert_int_equal(i, ARRAY_SIZE(expected)); + } + assert_rnp_success(rnp_identifier_iterator_destroy(it)); + } + + // fingerprint + { + rnp_identifier_iterator_t it = NULL; + assert_rnp_success(rnp_identifier_iterator_create(ffi, &it, "fingerprint")); + assert_non_null(it); + { + static const char *expected[] = {"E95A3CBF583AA80A2CCC53AA7BC6709B15C23A4A", + "E332B27CAF4742A11BAA677F1ED63EE56FADC34D", + "C5B15209940A7816A7AF3FB51D7E8A5393C997A8", + "5CD46D2A0BD0B8CFE0B130AE8A05B89FAD5ADED1", + "BE1C4AB951F4C2F6B604C7F82FCADF05FFA501BB", + "A3E94DE61A8CB229413D348E54505A936A4A970E", + "57F8ED6E5C197DB63C60FFAF326EF111425D14A5"}; + size_t i = 0; + const char * ident = NULL; + do { + ident = NULL; + assert_rnp_success(rnp_identifier_iterator_next(it, &ident)); + if (ident) { + assert_true(rnp::str_case_eq(expected[i], ident)); + i++; + } + } while (ident); + assert_int_equal(i, ARRAY_SIZE(expected)); + } + assert_rnp_success(rnp_identifier_iterator_destroy(it)); + } + + // cleanup + rnp_ffi_destroy(ffi); +} + +void +check_loaded_keys(const char * format, + bool armored, + uint8_t * buf, + size_t buf_len, + const char * id_type, + const std::vector<std::string> &expected_ids, + bool secret) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_identifier_iterator_t it = NULL; + const char * identifier = NULL; + + if (armored) { + assert_memory_equal("-----", buf, 5); + } else { + assert_memory_not_equal("-----", buf, 5); + } + + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, format, format)); + + // load our keyrings + assert_rnp_success(rnp_input_from_memory(&input, buf, buf_len, true)); + assert_rnp_success(rnp_load_keys( + ffi, format, input, secret ? RNP_LOAD_SAVE_SECRET_KEYS : RNP_LOAD_SAVE_PUBLIC_KEYS)); + rnp_input_destroy(input); + input = NULL; + + std::vector<std::string> ids; + assert_rnp_success(rnp_identifier_iterator_create(ffi, &it, id_type)); + do { + identifier = NULL; + assert_rnp_success(rnp_identifier_iterator_next(it, &identifier)); + if (identifier) { + rnp_key_handle_t key = NULL; + bool expected_secret = secret; + bool expected_public = !secret; + bool result; + assert_rnp_success(rnp_locate_key(ffi, id_type, identifier, &key)); + assert_non_null(key); + assert_rnp_success(rnp_key_have_secret(key, &result)); + assert_int_equal(result, expected_secret); + assert_rnp_success(rnp_key_have_public(key, &result)); + assert_int_equal(result, expected_public); + assert_rnp_success(rnp_key_handle_destroy(key)); + ids.push_back(identifier); + } + } while (identifier); + assert_true(ids == expected_ids); + rnp_identifier_iterator_destroy(it); + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_key_export) +{ + rnp_ffi_t ffi = NULL; + rnp_output_t output = NULL; + rnp_key_handle_t key = NULL; + uint8_t * buf = NULL; + size_t buf_len = 0; + + // setup FFI + test_ffi_init(&ffi); + + // primary pub only + { + // locate key + key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "2FCADF05FFA501BB", &key)); + assert_non_null(key); + + // create output + output = NULL; + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_non_null(output); + + // export + assert_rnp_success(rnp_key_export(key, output, RNP_KEY_EXPORT_PUBLIC)); + + // get output + buf = NULL; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &buf_len, false)); + assert_non_null(buf); + + // check results + check_loaded_keys("GPG", false, buf, buf_len, "keyid", {"2FCADF05FFA501BB"}, false); + + // cleanup + rnp_output_destroy(output); + rnp_key_handle_destroy(key); + } + + // primary sec only (armored) + { + // locate key + key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "2FCADF05FFA501BB", &key)); + assert_non_null(key); + + // create output + output = NULL; + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_non_null(output); + + // export + assert_rnp_success( + rnp_key_export(key, output, RNP_KEY_EXPORT_SECRET | RNP_KEY_EXPORT_ARMORED)); + + // get output + buf = NULL; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &buf_len, false)); + assert_non_null(buf); + + // check results + check_loaded_keys("GPG", true, buf, buf_len, "keyid", {"2FCADF05FFA501BB"}, true); + + // cleanup + rnp_output_destroy(output); + rnp_key_handle_destroy(key); + } + + // primary pub and subs + { + // locate key + key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "2FCADF05FFA501BB", &key)); + assert_non_null(key); + + // create output + output = NULL; + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_non_null(output); + + // export + assert_rnp_success( + rnp_key_export(key, output, RNP_KEY_EXPORT_PUBLIC | RNP_KEY_EXPORT_SUBKEYS)); + + // get output + buf = NULL; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &buf_len, false)); + assert_non_null(buf); + + // check results + check_loaded_keys("GPG", + false, + buf, + buf_len, + "keyid", + {"2FCADF05FFA501BB", "54505A936A4A970E", "326EF111425D14A5"}, + false); + + // cleanup + rnp_output_destroy(output); + rnp_key_handle_destroy(key); + } + + // primary sec and subs (armored) + { + // locate key + key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "2FCADF05FFA501BB", &key)); + assert_non_null(key); + + // create output + output = NULL; + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_non_null(output); + + // export + assert_rnp_success(rnp_key_export(key, + output, + RNP_KEY_EXPORT_SECRET | RNP_KEY_EXPORT_SUBKEYS | + RNP_KEY_EXPORT_ARMORED)); + + // get output + buf = NULL; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &buf_len, false)); + assert_non_null(buf); + + // check results + check_loaded_keys("GPG", + true, + buf, + buf_len, + "keyid", + {"2FCADF05FFA501BB", "54505A936A4A970E", "326EF111425D14A5"}, + true); + + // cleanup + rnp_output_destroy(output); + rnp_key_handle_destroy(key); + } + + // sub pub + { + // locate key + key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "54505A936A4A970E", &key)); + assert_non_null(key); + + // create output + output = NULL; + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_non_null(output); + + // export + assert_rnp_success( + rnp_key_export(key, output, RNP_KEY_EXPORT_PUBLIC | RNP_KEY_EXPORT_ARMORED)); + + // get output + buf = NULL; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &buf_len, false)); + assert_non_null(buf); + + // check results + check_loaded_keys( + "GPG", true, buf, buf_len, "keyid", {"2FCADF05FFA501BB", "54505A936A4A970E"}, false); + + // cleanup + rnp_output_destroy(output); + rnp_key_handle_destroy(key); + } + + // sub sec + { + // locate key + key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "54505A936A4A970E", &key)); + assert_non_null(key); + + // create output + output = NULL; + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_non_null(output); + + // export + assert_rnp_success( + rnp_key_export(key, output, RNP_KEY_EXPORT_SECRET | RNP_KEY_EXPORT_ARMORED)); + + // get output + buf = NULL; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &buf_len, false)); + assert_non_null(buf); + + // check results + check_loaded_keys( + "GPG", true, buf, buf_len, "keyid", {"2FCADF05FFA501BB", "54505A936A4A970E"}, true); + + // cleanup + rnp_output_destroy(output); + rnp_key_handle_destroy(key); + } + + // cleanup + rnp_ffi_destroy(ffi); +} + +static bool +check_import_keys_ex(rnp_ffi_t ffi, + json_object **jso, + uint32_t flags, + rnp_input_t input, + size_t rescount, + size_t pubcount, + size_t seccount) +{ + bool res = false; + char * keys = NULL; + size_t keycount = 0; + json_object *keyarr = NULL; + *jso = NULL; + + if (rnp_import_keys(ffi, input, flags, &keys)) { + goto done; + } + if (rnp_get_public_key_count(ffi, &keycount) || (keycount != pubcount)) { + goto done; + } + if (rnp_get_secret_key_count(ffi, &keycount) || (keycount != seccount)) { + goto done; + } + if (!keys) { + goto done; + } + + *jso = json_tokener_parse(keys); + if (!jso) { + goto done; + } + if (!json_object_is_type(*jso, json_type_object)) { + goto done; + } + if (!json_object_object_get_ex(*jso, "keys", &keyarr)) { + goto done; + } + if (!json_object_is_type(keyarr, json_type_array)) { + goto done; + } + if ((size_t) json_object_array_length(keyarr) != rescount) { + goto done; + } + res = true; +done: + if (!res) { + json_object_put(*jso); + *jso = NULL; + } + rnp_buffer_destroy(keys); + return res; +} + +static bool +check_import_keys(rnp_ffi_t ffi, + json_object **jso, + const char * keypath, + size_t rescount, + size_t pubcount, + size_t seccount) +{ + rnp_input_t input = NULL; + + if (rnp_input_from_path(&input, keypath)) { + return false; + } + bool res = check_import_keys_ex(ffi, + jso, + RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS, + input, + rescount, + pubcount, + seccount); + rnp_input_destroy(input); + return res; +} + +static bool +check_key_status( + json_object *jso, size_t idx, const char *pub, const char *sec, const char *fp) +{ + if (!jso) { + return false; + } + if (!json_object_is_type(jso, json_type_object)) { + return false; + } + json_object *keys = NULL; + if (!json_object_object_get_ex(jso, "keys", &keys)) { + return false; + } + if (!json_object_is_type(keys, json_type_array)) { + return false; + } + json_object *key = json_object_array_get_idx(keys, idx); + if (!json_object_is_type(key, json_type_object)) { + return false; + } + json_object *fld = NULL; + if (!json_object_object_get_ex(key, "public", &fld)) { + return false; + } + if (strcmp(json_object_get_string(fld), pub) != 0) { + return false; + } + if (!json_object_object_get_ex(key, "secret", &fld)) { + return false; + } + if (strcmp(json_object_get_string(fld), sec) != 0) { + return false; + } + if (!json_object_object_get_ex(key, "fingerprint", &fld)) { + return false; + } + if (strcmp(json_object_get_string(fld), fp) != 0) { + return false; + } + return true; +} + +TEST_F(rnp_tests, test_ffi_keys_import) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + // some edge cases + assert_rnp_success(rnp_input_from_path(&input, "data/test_stream_key_merge/key-both.asc")); + assert_rnp_failure(rnp_import_keys(NULL, input, RNP_LOAD_SAVE_PUBLIC_KEYS, NULL)); + assert_rnp_failure(rnp_import_keys(ffi, NULL, RNP_LOAD_SAVE_PUBLIC_KEYS, NULL)); + assert_rnp_failure(rnp_import_keys(ffi, input, 0, NULL)); + assert_rnp_failure(rnp_import_keys(ffi, input, 0x31, NULL)); + // load just public keys + assert_rnp_success(rnp_import_keys(ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS, NULL)); + rnp_input_destroy(input); + size_t keycount = 0; + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 3); + assert_rnp_success(rnp_get_secret_key_count(ffi, &keycount)); + assert_int_equal(keycount, 0); + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET)); + // load just secret keys from pubkey file + assert_true(import_sec_keys(ffi, "data/test_stream_key_merge/key-pub.asc")); + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 0); + assert_rnp_success(rnp_get_secret_key_count(ffi, &keycount)); + assert_int_equal(keycount, 0); + // load both public and secret keys by specifying just secret (it will create pub part) + assert_true(import_sec_keys(ffi, "data/test_stream_key_merge/key-sec.asc")); + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 3); + assert_rnp_success(rnp_get_secret_key_count(ffi, &keycount)); + assert_int_equal(keycount, 3); + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET)); + // import just a public key without subkeys + json_object *jso = NULL; + assert_true(check_import_keys( + ffi, &jso, "data/test_stream_key_merge/key-pub-just-key.pgp", 1, 1, 0)); + assert_true( + check_key_status(jso, 0, "new", "none", "090bd712a1166be572252c3c9747d2a6b3a63124")); + json_object_put(jso); + // import just subkey 1 + assert_true(check_import_keys( + ffi, &jso, "data/test_stream_key_merge/key-pub-just-subkey-1.pgp", 1, 2, 0)); + assert_true( + check_key_status(jso, 0, "new", "none", "51b45a4c74917272e4e34180af1114a47f5f5b28")); + json_object_put(jso); + // import just subkey 2 without sigs + assert_true(check_import_keys( + ffi, &jso, "data/test_stream_key_merge/key-pub-just-subkey-2-no-sigs.pgp", 1, 3, 0)); + assert_true( + check_key_status(jso, 0, "new", "none", "5fe514a54816e1b331686c2c16cd16f267ccdd4f")); + json_object_put(jso); + // import subkey 2 with sigs + assert_true(check_import_keys( + ffi, &jso, "data/test_stream_key_merge/key-pub-just-subkey-2.pgp", 1, 3, 0)); + assert_true( + check_key_status(jso, 0, "updated", "none", "5fe514a54816e1b331686c2c16cd16f267ccdd4f")); + json_object_put(jso); + // import first uid + assert_true( + check_import_keys(ffi, &jso, "data/test_stream_key_merge/key-pub-uid-1.pgp", 1, 3, 0)); + assert_true( + check_key_status(jso, 0, "updated", "none", "090bd712a1166be572252c3c9747d2a6b3a63124")); + json_object_put(jso); + // import the whole key + assert_true( + check_import_keys(ffi, &jso, "data/test_stream_key_merge/key-pub.pgp", 3, 3, 0)); + assert_true( + check_key_status(jso, 0, "updated", "none", "090bd712a1166be572252c3c9747d2a6b3a63124")); + assert_true(check_key_status( + jso, 1, "unchanged", "none", "51b45a4c74917272e4e34180af1114a47f5f5b28")); + assert_true(check_key_status( + jso, 2, "unchanged", "none", "5fe514a54816e1b331686c2c16cd16f267ccdd4f")); + json_object_put(jso); + // import the first secret subkey + assert_true(check_import_keys( + ffi, &jso, "data/test_stream_key_merge/key-sec-just-subkey-1.pgp", 1, 3, 1)); + assert_true(check_key_status( + jso, 0, "unchanged", "new", "51b45a4c74917272e4e34180af1114a47f5f5b28")); + json_object_put(jso); + // import the second secret subkey + assert_true(check_import_keys( + ffi, &jso, "data/test_stream_key_merge/key-sec-just-subkey-2-no-sigs.pgp", 1, 3, 2)); + assert_true(check_key_status( + jso, 0, "unchanged", "new", "5fe514a54816e1b331686c2c16cd16f267ccdd4f")); + json_object_put(jso); + // import the whole secret key + assert_true( + check_import_keys(ffi, &jso, "data/test_stream_key_merge/key-sec.pgp", 3, 3, 3)); + assert_true(check_key_status( + jso, 0, "unchanged", "new", "090bd712a1166be572252c3c9747d2a6b3a63124")); + assert_true(check_key_status( + jso, 1, "unchanged", "unchanged", "51b45a4c74917272e4e34180af1114a47f5f5b28")); + assert_true(check_key_status( + jso, 2, "unchanged", "unchanged", "5fe514a54816e1b331686c2c16cd16f267ccdd4f")); + json_object_put(jso); + // cleanup + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_elgamal4096) +{ + rnp_ffi_t ffi = NULL; + + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + /* load public key */ + json_object *jso = NULL; + assert_true( + check_import_keys(ffi, &jso, "data/test_key_edge_cases/key-eg-4096-pub.pgp", 2, 2, 0)); + assert_true( + check_key_status(jso, 0, "new", "none", "6541db10cdfcdba89db2dffea8f0408eb3369d8e")); + assert_true( + check_key_status(jso, 1, "new", "none", "c402a09b74acd0c11efc0527a3d630b457a0b15b")); + json_object_put(jso); + /* load secret key */ + assert_true( + check_import_keys(ffi, &jso, "data/test_key_edge_cases/key-eg-4096-sec.pgp", 2, 2, 2)); + assert_true(check_key_status( + jso, 0, "unchanged", "new", "6541db10cdfcdba89db2dffea8f0408eb3369d8e")); + assert_true(check_key_status( + jso, 1, "unchanged", "new", "c402a09b74acd0c11efc0527a3d630b457a0b15b")); + json_object_put(jso); + // cleanup + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_malformed_keys_import) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + /* import keys with bad key0-uid0 certification, first without flag */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_edge_cases/pubring-malf-cert.pgp")); + assert_rnp_failure(rnp_import_keys(ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS, NULL)); + rnp_input_destroy(input); + size_t keycount = 255; + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 0); + /* now try with RNP_LOAD_SAVE_PERMISSIVE */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_edge_cases/pubring-malf-cert.pgp")); + assert_rnp_success( + rnp_import_keys(ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_PERMISSIVE, NULL)); + rnp_input_destroy(input); + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 7); + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "7bc6709b15c23a4a", &key)); + assert_non_null(key); + size_t uidcount = 255; + assert_rnp_success(rnp_key_get_uid_count(key, &uidcount)); + assert_int_equal(uidcount, 3); + size_t subcount = 255; + assert_rnp_success(rnp_key_get_subkey_count(key, &subcount)); + assert_int_equal(subcount, 3); + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "2fcadf05ffa501bb", &key)); + assert_non_null(key); + assert_rnp_success(rnp_key_handle_destroy(key)); + + /* import keys with bad key0-sub0 binding */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC)); + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 0); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_edge_cases/pubring-malf-key0-sub0-bind.pgp")); + assert_rnp_success( + rnp_import_keys(ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_PERMISSIVE, NULL)); + rnp_input_destroy(input); + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 7); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "7bc6709b15c23a4a", &key)); + assert_non_null(key); + uidcount = 255; + assert_rnp_success(rnp_key_get_uid_count(key, &uidcount)); + assert_int_equal(uidcount, 3); + subcount = 255; + assert_rnp_success(rnp_key_get_subkey_count(key, &subcount)); + assert_int_equal(subcount, 3); + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "2fcadf05ffa501bb", &key)); + assert_non_null(key); + assert_rnp_success(rnp_key_handle_destroy(key)); + + /* import keys with bad key0-sub0 packet */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC)); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_edge_cases/pubring-malf-key0-sub0.pgp")); + assert_rnp_success( + rnp_import_keys(ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_PERMISSIVE, NULL)); + rnp_input_destroy(input); + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 6); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "7bc6709b15c23a4a", &key)); + assert_non_null(key); + uidcount = 255; + assert_rnp_success(rnp_key_get_uid_count(key, &uidcount)); + assert_int_equal(uidcount, 3); + subcount = 255; + assert_rnp_success(rnp_key_get_subkey_count(key, &subcount)); + assert_int_equal(subcount, 2); + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "2fcadf05ffa501bb", &key)); + assert_non_null(key); + assert_rnp_success(rnp_key_handle_destroy(key)); + + /* import keys with bad key0 packet */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC)); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_edge_cases/pubring-malf-key0.pgp")); + assert_rnp_success( + rnp_import_keys(ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_PERMISSIVE, NULL)); + rnp_input_destroy(input); + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 3); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "7bc6709b15c23a4a", &key)); + assert_null(key); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "2fcadf05ffa501bb", &key)); + assert_non_null(key); + assert_rnp_success(rnp_key_handle_destroy(key)); + + /* import secret keys with bad key1 packet - public should be added as well */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_edge_cases/secring-malf-key1.pgp")); + assert_rnp_success( + rnp_import_keys(ffi, input, RNP_LOAD_SAVE_SECRET_KEYS | RNP_LOAD_SAVE_PERMISSIVE, NULL)); + rnp_input_destroy(input); + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 7); + assert_rnp_success(rnp_get_secret_key_count(ffi, &keycount)); + assert_int_equal(keycount, 4); + + assert_rnp_success(rnp_locate_key(ffi, "keyid", "7bc6709b15c23a4a", &key)); + assert_non_null(key); + bool secret = false; + assert_rnp_success(rnp_key_have_secret(key, &secret)); + assert_true(secret); + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "326ef111425d14a5", &key)); + assert_non_null(key); + assert_rnp_success(rnp_key_have_secret(key, &secret)); + assert_false(secret); + assert_rnp_success(rnp_key_handle_destroy(key)); + + /* import secret keys with bad key0 packet */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_edge_cases/secring-malf-key0.pgp")); + assert_rnp_success( + rnp_import_keys(ffi, input, RNP_LOAD_SAVE_SECRET_KEYS | RNP_LOAD_SAVE_PERMISSIVE, NULL)); + rnp_input_destroy(input); + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 7); + assert_rnp_success(rnp_get_secret_key_count(ffi, &keycount)); + assert_int_equal(keycount, 7); + + assert_rnp_success(rnp_locate_key(ffi, "keyid", "7bc6709b15c23a4a", &key)); + assert_non_null(key); + assert_rnp_success(rnp_key_have_secret(key, &secret)); + assert_true(secret); + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success(rnp_locate_key(ffi, "userid", "key1-uid2", &key)); + assert_non_null(key); + assert_rnp_success(rnp_key_have_secret(key, &secret)); + assert_true(secret); + assert_rnp_success(rnp_key_handle_destroy(key)); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "326ef111425d14a5", &key)); + assert_non_null(key); + assert_rnp_success(rnp_key_have_secret(key, &secret)); + assert_true(secret); + assert_rnp_success(rnp_key_handle_destroy(key)); + + /* import unprotected secret key with wrong crc */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET)); + assert_false( + import_sec_keys(ffi, "data/test_key_edge_cases/key-25519-tweaked-wrong-crc.asc")); + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 0); + assert_rnp_success(rnp_get_secret_key_count(ffi, &keycount)); + assert_int_equal(keycount, 0); + + /* cleanup */ + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_iterated_key_import) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + uint32_t flags = + RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS | RNP_LOAD_SAVE_SINGLE; + + /* two primary keys with attached subkeys in binary keyring */ + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success(rnp_input_from_path(&input, "data/keyrings/1/pubring.gpg")); + json_object *jso = NULL; + assert_true(check_import_keys_ex(ffi, &jso, flags, input, 4, 4, 0)); + assert_true( + check_key_status(jso, 0, "new", "none", "e95a3cbf583aa80a2ccc53aa7bc6709b15c23a4a")); + assert_true( + check_key_status(jso, 1, "new", "none", "e332b27caf4742a11baa677f1ed63ee56fadc34d")); + assert_true( + check_key_status(jso, 2, "new", "none", "c5b15209940a7816a7af3fb51d7e8a5393c997a8")); + assert_true( + check_key_status(jso, 3, "new", "none", "5cd46d2a0bd0b8cfe0b130ae8a05b89fad5aded1")); + json_object_put(jso); + + assert_true(check_import_keys_ex(ffi, &jso, flags, input, 3, 7, 0)); + assert_true( + check_key_status(jso, 0, "new", "none", "be1c4ab951f4c2f6b604c7f82fcadf05ffa501bb")); + assert_true( + check_key_status(jso, 1, "new", "none", "a3e94de61a8cb229413d348e54505a936a4a970e")); + assert_true( + check_key_status(jso, 2, "new", "none", "57f8ed6e5c197db63c60ffaf326ef111425d14a5")); + json_object_put(jso); + + char *results = NULL; + assert_int_equal(RNP_ERROR_EOF, rnp_import_keys(ffi, input, flags, &results)); + assert_null(results); + rnp_input_destroy(input); + + /* public + secret key, armored separately */ + 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_stream_key_merge/key-both.asc")); + assert_true(check_import_keys_ex(ffi, &jso, flags, input, 3, 3, 0)); + assert_true( + check_key_status(jso, 0, "new", "none", "090bd712a1166be572252c3c9747d2a6b3a63124")); + assert_true( + check_key_status(jso, 1, "new", "none", "51b45a4c74917272e4e34180af1114a47f5f5b28")); + assert_true( + check_key_status(jso, 2, "new", "none", "5fe514a54816e1b331686c2c16cd16f267ccdd4f")); + json_object_put(jso); + + assert_true(check_import_keys_ex(ffi, &jso, flags, input, 3, 3, 3)); + assert_true(check_key_status( + jso, 0, "unchanged", "new", "090bd712a1166be572252c3c9747d2a6b3a63124")); + assert_true(check_key_status( + jso, 1, "unchanged", "new", "51b45a4c74917272e4e34180af1114a47f5f5b28")); + assert_true(check_key_status( + jso, 2, "unchanged", "new", "5fe514a54816e1b331686c2c16cd16f267ccdd4f")); + json_object_put(jso); + + assert_int_equal(RNP_ERROR_EOF, rnp_import_keys(ffi, input, flags, &results)); + assert_null(results); + rnp_input_destroy(input); + + /* public keyring, enarmored */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET)); + assert_rnp_success(rnp_input_from_path(&input, "data/keyrings/1/pubring.gpg.asc")); + flags |= RNP_LOAD_SAVE_PERMISSIVE; + assert_true(check_import_keys_ex(ffi, &jso, flags, input, 4, 4, 0)); + assert_true( + check_key_status(jso, 0, "new", "none", "e95a3cbf583aa80a2ccc53aa7bc6709b15c23a4a")); + assert_true( + check_key_status(jso, 1, "new", "none", "e332b27caf4742a11baa677f1ed63ee56fadc34d")); + assert_true( + check_key_status(jso, 2, "new", "none", "c5b15209940a7816a7af3fb51d7e8a5393c997a8")); + assert_true( + check_key_status(jso, 3, "new", "none", "5cd46d2a0bd0b8cfe0b130ae8a05b89fad5aded1")); + json_object_put(jso); + + assert_true(check_import_keys_ex(ffi, &jso, flags, input, 3, 7, 0)); + assert_true( + check_key_status(jso, 0, "new", "none", "be1c4ab951f4c2f6b604c7f82fcadf05ffa501bb")); + assert_true( + check_key_status(jso, 1, "new", "none", "a3e94de61a8cb229413d348e54505a936a4a970e")); + assert_true( + check_key_status(jso, 2, "new", "none", "57f8ed6e5c197db63c60ffaf326ef111425d14a5")); + json_object_put(jso); + + results = NULL; + assert_int_equal(RNP_ERROR_EOF, rnp_import_keys(ffi, input, flags, &results)); + assert_null(results); + rnp_input_destroy(input); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_stripped_keys_import) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + /* load stripped key as keyring */ + assert_true(load_keys_gpg(ffi, "data/test_key_validity/case8/pubring.gpg")); + /* validate signatures - must succeed */ + rnp_op_verify_t verify = NULL; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_validity/case8/message.txt.asc")); + rnp_output_t output = NULL; + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + rnp_input_destroy(input); + rnp_output_destroy(output); + rnp_op_verify_signature_t sig; + /* signature 1 - by primary key */ + assert_rnp_success(rnp_op_verify_get_signature_at(verify, 0, &sig)); + assert_rnp_success(rnp_op_verify_signature_get_status(sig)); + /* signature 2 - by subkey */ + assert_rnp_success(rnp_op_verify_get_signature_at(verify, 1, &sig)); + assert_rnp_success(rnp_op_verify_signature_get_status(sig)); + rnp_op_verify_destroy(verify); + + /* load stripped key by parts via import */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC)); + assert_true(import_pub_keys(ffi, "data/test_key_validity/case8/primary.pgp")); + assert_true(import_pub_keys(ffi, "data/test_key_validity/case8/subkey.pgp")); + /* validate signatures - must be valid */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_validity/case8/message.txt.asc")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + rnp_input_destroy(input); + rnp_output_destroy(output); + /* signature 1 - by primary key */ + assert_rnp_success(rnp_op_verify_get_signature_at(verify, 0, &sig)); + assert_rnp_success(rnp_op_verify_signature_get_status(sig)); + /* signature 2 - by subkey */ + assert_rnp_success(rnp_op_verify_get_signature_at(verify, 1, &sig)); + assert_rnp_success(rnp_op_verify_signature_get_status(sig)); + rnp_op_verify_destroy(verify); + + /* load stripped key with subkey first */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC)); + assert_true(import_pub_keys(ffi, "data/test_key_validity/case8/subkey.pgp")); + assert_true(import_pub_keys(ffi, "data/test_key_validity/case8/primary.pgp")); + /* validate signatures - must be valid */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_validity/case8/message.txt.asc")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + rnp_input_destroy(input); + rnp_output_destroy(output); + /* signature 1 - by primary key */ + assert_rnp_success(rnp_op_verify_get_signature_at(verify, 0, &sig)); + assert_rnp_success(rnp_op_verify_signature_get_status(sig)); + /* signature 2 - by subkey */ + assert_rnp_success(rnp_op_verify_get_signature_at(verify, 1, &sig)); + assert_rnp_success(rnp_op_verify_signature_get_status(sig)); + rnp_op_verify_destroy(verify); + + /* load stripped key without subkey binding */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC)); + assert_true(import_pub_keys(ffi, "data/test_key_validity/case8/primary.pgp")); + assert_true(import_pub_keys(ffi, "data/test_key_validity/case8/subkey-no-sig.pgp")); + /* validate signatures - must be invalid */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_validity/case8/message.txt.asc")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_int_equal(rnp_op_verify_execute(verify), RNP_ERROR_SIGNATURE_INVALID); + rnp_input_destroy(input); + rnp_output_destroy(output); + /* signature 1 - by primary key */ + 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); + /* signature 2 - by subkey */ + assert_rnp_success(rnp_op_verify_get_signature_at(verify, 1, &sig)); + assert_int_equal(rnp_op_verify_signature_get_status(sig), RNP_ERROR_SIGNATURE_INVALID); + rnp_op_verify_destroy(verify); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_key_import_edge_cases) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + + /* key with empty packets - must fail with bad format */ + rnp_input_t input = NULL; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_edge_cases/key-empty-packets.pgp")); + char *results = NULL; + assert_int_equal(rnp_import_keys(ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS, &results), + RNP_ERROR_BAD_FORMAT); + assert_null(results); + rnp_input_destroy(input); + + /* key with empty uid - must succeed */ + json_object *jso = NULL; + assert_true( + check_import_keys(ffi, &jso, "data/test_key_edge_cases/key-empty-uid.pgp", 1, 1, 0)); + assert_true( + check_key_status(jso, 0, "new", "none", "753d5b947e9a2b2e01147c1fc972affd358bf887")); + json_object_put(jso); + + /* key with experimental signature subpackets - must succeed and append uid and signature + */ + assert_true(check_import_keys( + ffi, &jso, "data/test_key_edge_cases/key-subpacket-101-110.pgp", 1, 1, 0)); + assert_true( + check_key_status(jso, 0, "updated", "none", "753d5b947e9a2b2e01147c1fc972affd358bf887")); + json_object_put(jso); + + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "C972AFFD358BF887", &key)); + size_t count = 0; + assert_rnp_success(rnp_key_get_uid_count(key, &count)); + assert_int_equal(count, 2); + char *uid = NULL; + assert_rnp_success(rnp_key_get_uid_at(key, 0, &uid)); + assert_string_equal(uid, ""); + rnp_buffer_destroy(uid); + assert_rnp_success(rnp_key_get_uid_at(key, 1, &uid)); + assert_string_equal(uid, "NoUID"); + rnp_buffer_destroy(uid); + rnp_key_handle_destroy(key); + + /* key with malformed signature - must fail */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_edge_cases/key-malf-sig.pgp")); + assert_int_equal(rnp_import_keys(ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS, &results), + RNP_ERROR_BAD_FORMAT); + assert_null(results); + rnp_input_destroy(input); + + /* revoked key without revocation reason signature subpacket */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_edge_cases/alice-rev-no-reason.pgp")); + assert_rnp_success(rnp_import_keys(ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS, &results)); + rnp_input_destroy(input); + assert_non_null(results); + rnp_buffer_destroy(results); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669FFDE3C", &key)); + assert_rnp_success(rnp_key_get_revocation_reason(key, &results)); + assert_string_equal(results, "No reason specified"); + rnp_buffer_destroy(results); + bool revoked = false; + assert_rnp_success(rnp_key_is_revoked(key, &revoked)); + assert_true(revoked); + rnp_key_handle_destroy(key); + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC)); + + /* revoked subkey without revocation reason signature subpacket */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_edge_cases/alice-sub-rev-no-reason.pgp")); + assert_rnp_success(rnp_import_keys(ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS, &results)); + rnp_input_destroy(input); + assert_non_null(results); + rnp_buffer_destroy(results); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669FFDE3C", &key)); + assert_int_equal(rnp_key_get_revocation_reason(key, &results), RNP_ERROR_BAD_PARAMETERS); + revoked = true; + assert_rnp_success(rnp_key_is_revoked(key, &revoked)); + assert_false(revoked); + rnp_key_handle_destroy(key); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "DD23CEB7FEBEFF17", &key)); + assert_rnp_success(rnp_key_get_revocation_reason(key, &results)); + assert_string_equal(results, "No reason specified"); + rnp_buffer_destroy(results); + revoked = false; + assert_rnp_success(rnp_key_is_revoked(key, &revoked)); + assert_true(revoked); + rnp_key_handle_destroy(key); + + /* key with two subkeys with same material but different creation time */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_edge_cases/alice-2-subs-same-grip.pgp")); + assert_rnp_success(rnp_import_keys(ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS, &results)); + rnp_input_destroy(input); + assert_non_null(results); + rnp_buffer_destroy(results); + count = 0; + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(count, 3); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669FFDE3C", &key)); + assert_rnp_success(rnp_key_get_subkey_count(key, &count)); + assert_int_equal(count, 2); + rnp_key_handle_t sub = NULL; + assert_rnp_success(rnp_key_get_subkey_at(key, 0, &sub)); + char *keyid = NULL; + assert_rnp_success(rnp_key_get_keyid(sub, &keyid)); + assert_string_equal(keyid, "DD23CEB7FEBEFF17"); + rnp_buffer_destroy(keyid); + char *fp = NULL; + assert_rnp_success(rnp_key_get_primary_fprint(sub, &fp)); + assert_string_equal(fp, "73EDCC9119AFC8E2DBBDCDE50451409669FFDE3C"); + rnp_buffer_destroy(fp); + rnp_key_handle_destroy(sub); + assert_rnp_success(rnp_key_get_subkey_at(key, 1, &sub)); + assert_rnp_success(rnp_key_get_keyid(sub, &keyid)); + assert_string_equal(keyid, "C2E7FDCC9CD59FB5"); + rnp_buffer_destroy(keyid); + assert_rnp_success(rnp_key_get_primary_fprint(sub, &fp)); + assert_string_equal(fp, "73EDCC9119AFC8E2DBBDCDE50451409669FFDE3C"); + rnp_buffer_destroy(fp); + rnp_key_handle_destroy(sub); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "DD23CEB7FEBEFF17", &sub)); + assert_rnp_success(rnp_key_get_primary_fprint(sub, &fp)); + assert_string_equal(fp, "73EDCC9119AFC8E2DBBDCDE50451409669FFDE3C"); + rnp_buffer_destroy(fp); + rnp_key_handle_destroy(sub); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "C2E7FDCC9CD59FB5", &sub)); + assert_rnp_success(rnp_key_get_primary_fprint(sub, &fp)); + assert_string_equal(fp, "73EDCC9119AFC8E2DBBDCDE50451409669FFDE3C"); + rnp_buffer_destroy(fp); + rnp_key_handle_destroy(sub); + rnp_key_handle_destroy(key); + + /* two keys with subkeys with same material but different creation time */ + assert_true(import_pub_keys(ffi, "data/test_key_edge_cases/alice-2-keys-same-grip.pgp")); + assert_rnp_success(rnp_get_public_key_count(ffi, &count)); + assert_int_equal(count, 4); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669FFDE3C", &key)); + assert_rnp_success(rnp_key_get_subkey_count(key, &count)); + assert_int_equal(count, 2); + assert_rnp_success(rnp_key_get_subkey_at(key, 0, &sub)); + assert_rnp_success(rnp_key_get_keyid(sub, &keyid)); + assert_string_equal(keyid, "DD23CEB7FEBEFF17"); + rnp_buffer_destroy(keyid); + assert_rnp_success(rnp_key_get_primary_fprint(sub, &fp)); + assert_string_equal(fp, "73EDCC9119AFC8E2DBBDCDE50451409669FFDE3C"); + rnp_buffer_destroy(fp); + rnp_key_handle_destroy(sub); + assert_rnp_success(rnp_key_get_subkey_at(key, 1, &sub)); + assert_rnp_success(rnp_key_get_keyid(sub, &keyid)); + assert_string_equal(keyid, "C2E7FDCC9CD59FB5"); + rnp_buffer_destroy(keyid); + assert_rnp_success(rnp_key_get_primary_fprint(sub, &fp)); + assert_string_equal(fp, "73EDCC9119AFC8E2DBBDCDE50451409669FFDE3C"); + rnp_buffer_destroy(fp); + rnp_key_handle_destroy(sub); + rnp_key_handle_destroy(key); + /* subkey should belong to original key */ + assert_rnp_success(rnp_locate_key(ffi, "keyid", "467A2DE826ABA0DB", &key)); + assert_rnp_success(rnp_key_get_subkey_count(key, &count)); + assert_int_equal(count, 0); + rnp_key_handle_destroy(key); + + /* key with signing subkey, where primary binding has different from subkey binding hash + * algorithm */ + assert_true(import_pub_keys(ffi, "data/test_key_edge_cases/key-binding-hash-alg.asc")); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "F81A30AA5DCBD01E", &key)); + bool valid = false; + assert_rnp_success(rnp_key_is_valid(key, &valid)); + assert_true(valid); + assert_true(key->pub->valid()); + rnp_key_handle_destroy(key); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "DD716516A7249711", &sub)); + assert_rnp_success(rnp_key_is_valid(sub, &valid)); + assert_true(valid); + assert_true(sub->pub->valid()); + rnp_key_handle_destroy(sub); + + /* key and subkey both has 0 key expiration with corresponding subpacket */ + assert_true(import_pub_keys(ffi, "data/test_key_edge_cases/key-sub-0-expiry.pgp")); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "6EFF45F2201AC5F8", &key)); + assert_rnp_success(rnp_key_is_valid(key, &valid)); + assert_true(valid); + assert_true(key->pub->valid()); + uint32_t expiry = 0; + assert_rnp_success(rnp_key_valid_till(key, &expiry)); + assert_int_equal(expiry, 0xffffffff); + uint64_t expiry64 = 0; + assert_rnp_success(rnp_key_valid_till64(key, &expiry64)); + assert_int_equal(expiry64, UINT64_MAX); + + rnp_key_handle_destroy(key); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "74F971795A5DDBC9", &sub)); + assert_rnp_success(rnp_key_is_valid(sub, &valid)); + assert_true(valid); + assert_true(sub->pub->valid()); + assert_rnp_success(rnp_key_valid_till(sub, &expiry)); + assert_int_equal(expiry, 0xffffffff); + assert_rnp_success(rnp_key_valid_till64(sub, &expiry64)); + assert_int_equal(expiry64, UINT64_MAX); + rnp_key_handle_destroy(sub); + + /* key/subkey with expiration times in unhashed subpackets */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET)); + assert_true(import_pub_keys(ffi, "data/test_key_edge_cases/key-unhashed-subpkts.pgp")); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "7BC6709B15C23A4A", &key)); + assert_rnp_success(rnp_key_is_valid(key, &valid)); + assert_true(valid); + assert_true(key->pub->valid()); + assert_rnp_success(rnp_key_get_expiration(key, &expiry)); + assert_int_equal(expiry, 0); + assert_rnp_success(rnp_key_valid_till(key, &expiry)); + assert_int_equal(expiry, 0xffffffff); + assert_rnp_success(rnp_key_valid_till64(key, &expiry64)); + assert_int_equal(expiry64, UINT64_MAX); + rnp_key_handle_destroy(key); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "1ED63EE56FADC34D", &sub)); + assert_true(sub->pub->valid()); + expiry = 100; + assert_rnp_success(rnp_key_get_expiration(sub, &expiry)); + assert_int_equal(expiry, 0); + rnp_key_handle_destroy(sub); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_key_import_gpg_s2k) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + + /* secret subkeys, exported via gpg --export-secret-subkeys (no primary secret key data) */ + rnp_input_t input = NULL; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_key_edge_cases/alice-s2k-101-1-subs.pgp")); + assert_rnp_success(rnp_import_keys( + ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS, NULL)); + rnp_input_destroy(input); + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669FFDE3C", &key)); + bool secret = false; + assert_rnp_success(rnp_key_have_secret(key, &secret)); + assert_true(secret); + bool locked = false; + assert_rnp_success(rnp_key_is_locked(key, &locked)); + assert_true(locked); + char *type = NULL; + assert_rnp_success(rnp_key_get_protection_type(key, &type)); + assert_string_equal(type, "GPG-None"); + rnp_buffer_destroy(type); + assert_rnp_failure(rnp_key_unlock(key, "password")); + size_t count = 0; + assert_rnp_success(rnp_key_get_subkey_count(key, &count)); + assert_int_equal(count, 2); + /* signing secret subkey */ + rnp_key_handle_t sub = NULL; + assert_rnp_success(rnp_key_get_subkey_at(key, 0, &sub)); + char *keyid = NULL; + assert_rnp_success(rnp_key_get_keyid(sub, &keyid)); + assert_string_equal(keyid, "22F3A217C0E439CB"); + rnp_buffer_destroy(keyid); + secret = false; + assert_rnp_success(rnp_key_have_secret(sub, &secret)); + assert_true(secret); + locked = false; + assert_rnp_success(rnp_key_is_locked(sub, &locked)); + assert_true(locked); + assert_rnp_success(rnp_key_get_protection_type(sub, &type)); + assert_string_equal(type, "Encrypted-Hashed"); + rnp_buffer_destroy(type); + assert_rnp_success(rnp_key_unlock(sub, "password")); + assert_rnp_success(rnp_key_is_locked(sub, &locked)); + assert_false(locked); + rnp_key_handle_destroy(sub); + /* encrypting secret subkey */ + assert_rnp_success(rnp_key_get_subkey_at(key, 1, &sub)); + assert_rnp_success(rnp_key_get_keyid(sub, &keyid)); + assert_string_equal(keyid, "DD23CEB7FEBEFF17"); + rnp_buffer_destroy(keyid); + secret = false; + assert_rnp_success(rnp_key_have_secret(sub, &secret)); + assert_true(secret); + locked = false; + assert_rnp_success(rnp_key_is_locked(sub, &locked)); + assert_true(locked); + assert_rnp_success(rnp_key_get_protection_type(sub, &type)); + assert_string_equal(type, "Encrypted-Hashed"); + rnp_buffer_destroy(type); + assert_rnp_success(rnp_key_unlock(sub, "password")); + assert_rnp_success(rnp_key_is_locked(sub, &locked)); + assert_false(locked); + rnp_key_handle_destroy(sub); + rnp_key_handle_destroy(key); + + /* save keyrings and reload */ + reload_keyrings(&ffi); + + key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669FFDE3C", &key)); + secret = false; + assert_rnp_success(rnp_key_have_secret(key, &secret)); + assert_true(secret); + locked = false; + assert_rnp_success(rnp_key_is_locked(key, &locked)); + assert_true(locked); + assert_rnp_success(rnp_key_get_protection_type(key, &type)); + assert_string_equal(type, "GPG-None"); + rnp_buffer_destroy(type); + count = 0; + assert_rnp_success(rnp_key_get_subkey_count(key, &count)); + assert_int_equal(count, 2); + /* signing secret subkey */ + sub = NULL; + assert_rnp_success(rnp_key_get_subkey_at(key, 0, &sub)); + keyid = NULL; + assert_rnp_success(rnp_key_get_keyid(sub, &keyid)); + assert_string_equal(keyid, "22F3A217C0E439CB"); + rnp_buffer_destroy(keyid); + secret = false; + assert_rnp_success(rnp_key_have_secret(sub, &secret)); + assert_true(secret); + locked = false; + assert_rnp_success(rnp_key_is_locked(sub, &locked)); + assert_true(locked); + assert_rnp_success(rnp_key_get_protection_type(sub, &type)); + assert_string_equal(type, "Encrypted-Hashed"); + rnp_buffer_destroy(type); + rnp_key_handle_destroy(sub); + /* encrypting secret subkey */ + assert_rnp_success(rnp_key_get_subkey_at(key, 1, &sub)); + assert_rnp_success(rnp_key_get_keyid(sub, &keyid)); + assert_string_equal(keyid, "DD23CEB7FEBEFF17"); + rnp_buffer_destroy(keyid); + secret = false; + assert_rnp_success(rnp_key_have_secret(sub, &secret)); + assert_true(secret); + locked = false; + assert_rnp_success(rnp_key_is_locked(sub, &locked)); + assert_true(locked); + assert_rnp_success(rnp_key_get_protection_type(sub, &type)); + assert_string_equal(type, "Encrypted-Hashed"); + rnp_buffer_destroy(type); + rnp_key_handle_destroy(sub); + rnp_key_handle_destroy(key); + + /* secret subkeys, and primary key stored on the smartcard by gpg */ + 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-s2k-101-2-card.pgp")); + assert_rnp_success(rnp_import_keys( + ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS, NULL)); + rnp_input_destroy(input); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669FFDE3C", &key)); + secret = false; + assert_rnp_success(rnp_key_have_secret(key, &secret)); + assert_true(secret); + locked = false; + assert_rnp_success(rnp_key_is_locked(key, &locked)); + assert_true(locked); + assert_rnp_success(rnp_key_get_protection_type(key, &type)); + assert_string_equal(type, "GPG-Smartcard"); + rnp_buffer_destroy(type); + assert_rnp_failure(rnp_key_unlock(key, "password")); + count = 0; + assert_rnp_success(rnp_key_get_subkey_count(key, &count)); + assert_int_equal(count, 2); + /* signing secret subkey */ + assert_rnp_success(rnp_locate_key(ffi, "keyid", "22F3A217C0E439CB", &sub)); + secret = false; + assert_rnp_success(rnp_key_have_secret(key, &secret)); + assert_true(secret); + locked = false; + assert_rnp_success(rnp_key_is_locked(sub, &locked)); + assert_true(locked); + assert_rnp_success(rnp_key_get_protection_type(sub, &type)); + assert_string_equal(type, "Encrypted-Hashed"); + rnp_buffer_destroy(type); + rnp_key_handle_destroy(sub); + /* encrypting secret subkey */ + assert_rnp_success(rnp_locate_key(ffi, "keyid", "DD23CEB7FEBEFF17", &sub)); + secret = false; + assert_rnp_success(rnp_key_have_secret(key, &secret)); + assert_true(secret); + locked = false; + assert_rnp_success(rnp_key_is_locked(sub, &locked)); + assert_true(locked); + assert_rnp_success(rnp_key_get_protection_type(sub, &type)); + assert_string_equal(type, "Encrypted-Hashed"); + rnp_buffer_destroy(type); + rnp_key_handle_destroy(sub); + rnp_key_handle_destroy(key); + + /* save keyrings and reload */ + reload_keyrings(&ffi); + + key = NULL; + 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 = 0; + assert_rnp_success(rnp_key_get_protection_type(key, &type)); + assert_string_equal(type, "GPG-Smartcard"); + rnp_buffer_destroy(type); + assert_rnp_success(rnp_key_get_subkey_count(key, &count)); + assert_int_equal(count, 2); + rnp_key_handle_destroy(key); + + /* load key with too large gpg_serial_len */ + 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-s2k-101-2-card-len.pgp")); + assert_rnp_success(rnp_import_keys( + ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS, NULL)); + rnp_input_destroy(input); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669FFDE3C", &key)); + secret = false; + assert_rnp_success(rnp_key_have_secret(key, &secret)); + assert_true(secret); + locked = false; + assert_rnp_success(rnp_key_is_locked(key, &locked)); + assert_true(locked); + assert_rnp_success(rnp_key_get_protection_type(key, &type)); + assert_string_equal(type, "GPG-Smartcard"); + rnp_buffer_destroy(type); + assert_rnp_failure(rnp_key_unlock(key, "password")); + rnp_key_handle_destroy(key); + + /* secret subkeys, and primary key stored with unknown gpg s2k */ + 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-s2k-101-3.pgp")); + assert_rnp_success(rnp_import_keys( + ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS, NULL)); + rnp_input_destroy(input); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669FFDE3C", &key)); + secret = false; + assert_rnp_success(rnp_key_have_secret(key, &secret)); + assert_true(secret); + locked = false; + assert_rnp_success(rnp_key_is_locked(key, &locked)); + assert_true(locked); + assert_rnp_success(rnp_key_get_protection_type(key, &type)); + assert_string_equal(type, "Unknown"); + rnp_buffer_destroy(type); + assert_rnp_failure(rnp_key_unlock(key, "password")); + count = 0; + assert_rnp_success(rnp_key_get_subkey_count(key, &count)); + assert_int_equal(count, 2); + rnp_key_handle_destroy(key); + + /* save keyrings and reload */ + reload_keyrings(&ffi); + + key = NULL; + 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 = 0; + assert_rnp_success(rnp_key_get_protection_type(key, &type)); + assert_string_equal(type, "Unknown"); + rnp_buffer_destroy(type); + assert_rnp_success(rnp_key_get_subkey_count(key, &count)); + assert_int_equal(count, 2); + rnp_key_handle_destroy(key); + + /* secret subkeys, and primary key stored with unknown s2k */ + 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-s2k-101-unknown.pgp")); + assert_rnp_success(rnp_import_keys( + ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS, NULL)); + rnp_input_destroy(input); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669FFDE3C", &key)); + secret = false; + assert_rnp_success(rnp_key_have_secret(key, &secret)); + assert_true(secret); + locked = false; + assert_rnp_success(rnp_key_is_locked(key, &locked)); + assert_true(locked); + assert_rnp_success(rnp_key_get_protection_type(key, &type)); + assert_string_equal(type, "Unknown"); + rnp_buffer_destroy(type); + assert_rnp_failure(rnp_key_unlock(key, "password")); + count = 0; + assert_rnp_success(rnp_key_get_subkey_count(key, &count)); + assert_int_equal(count, 2); + rnp_key_handle_destroy(key); + + /* save keyrings and reload */ + reload_keyrings(&ffi); + + key = NULL; + 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 = 0; + assert_rnp_success(rnp_key_get_protection_type(key, &type)); + assert_string_equal(type, "Unknown"); + rnp_buffer_destroy(type); + assert_rnp_success(rnp_key_get_subkey_count(key, &count)); + assert_int_equal(count, 2); + rnp_key_handle_destroy(key); + + rnp_ffi_destroy(ffi); +} + +static bool +check_key_autocrypt(rnp_output_t memout, + const std::string &keyid, + const std::string &subid, + const std::string &uid, + bool base64 = false) +{ + rnp_ffi_t ffi = NULL; + rnp_ffi_create(&ffi, "GPG", "GPG"); + + uint8_t *buf = NULL; + size_t len = 0; + if (rnp_output_memory_get_buf(memout, &buf, &len, false) || !buf || !len) { + return false; + } + if (!import_all_keys(ffi, buf, len, base64 ? RNP_LOAD_SAVE_BASE64 : 0)) { + return false; + } + size_t count = 0; + rnp_get_public_key_count(ffi, &count); + if (count != 2) { + return false; + } + rnp_get_secret_key_count(ffi, &count); + if (count != 0) { + return false; + } + rnp_key_handle_t key = NULL; + if (rnp_locate_key(ffi, "keyid", keyid.c_str(), &key) || !key) { + return false; + } + rnp_key_handle_t sub = NULL; + if (rnp_locate_key(ffi, "keyid", subid.c_str(), &sub) || !sub) { + return false; + } + if (!key->pub->valid() || !sub->pub->valid()) { + return false; + } + if ((key->pub->sig_count() != 1) || (sub->pub->sig_count() != 1)) { + return false; + } + if (!key->pub->can_sign() || !sub->pub->can_encrypt()) { + return false; + } + if ((key->pub->uid_count() != 1) || (key->pub->get_uid(0).str != uid)) { + return false; + } + rnp_key_handle_destroy(key); + rnp_key_handle_destroy(sub); + rnp_ffi_destroy(ffi); + return true; +} + +TEST_F(rnp_tests, test_ffi_key_export_autocrypt) +{ + rnp_ffi_t ffi = NULL; + test_ffi_init(&ffi); + + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "7bc6709b15c23a4a", &key)); + rnp_key_handle_t sub = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "8a05b89fad5aded1", &sub)); + + /* edge cases */ + assert_rnp_failure(rnp_key_export_autocrypt(key, NULL, NULL, NULL, 0)); + rnp_output_t output = NULL; + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_failure(rnp_key_export_autocrypt(key, sub, NULL, output, 17)); + assert_rnp_failure(rnp_key_export_autocrypt(NULL, sub, "key0-uid0", output, 0)); + assert_rnp_failure(rnp_key_export_autocrypt(key, sub, NULL, output, 0)); + assert_rnp_failure(rnp_key_export_autocrypt(key, key, NULL, output, 0)); + assert_rnp_failure(rnp_key_export_autocrypt(key, key, "key0-uid0", output, 0)); + assert_rnp_failure(rnp_key_export_autocrypt(sub, sub, "key0-uid0", output, 0)); + assert_rnp_failure(rnp_key_export_autocrypt(sub, key, "key0-uid0", output, 0)); + assert_int_equal(output->dst.writeb, 0); + + /* export key + uid1 + sub2 */ + assert_rnp_success(rnp_key_export_autocrypt(key, sub, "key0-uid1", output, 0)); + assert_true( + check_key_autocrypt(output, "7bc6709b15c23a4a", "8a05b89fad5aded1", "key0-uid1")); + rnp_output_destroy(output); + + /* export key + uid0 + sub1 (fail) */ + rnp_key_handle_destroy(sub); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "1d7e8a5393c997a8", &sub)); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_failure(rnp_key_export_autocrypt(key, sub, "key0-uid0", output, 0)); + assert_int_equal(output->dst.writeb, 0); + rnp_key_handle_destroy(sub); + + /* export key without specifying subkey */ + assert_rnp_success(rnp_key_export_autocrypt(key, NULL, "key0-uid2", output, 0)); + assert_true( + check_key_autocrypt(output, "7bc6709b15c23a4a", "8a05b89fad5aded1", "key0-uid2")); + rnp_output_destroy(output); + + /* export base64-encoded key */ + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success( + rnp_key_export_autocrypt(key, NULL, "key0-uid2", output, RNP_KEY_EXPORT_BASE64)); + /* Make sure it is base64-encoded */ + const std::string reg = "^[A-Za-z0-9\\+\\/]+={0,2}$"; + uint8_t * buf = NULL; + size_t len = 0; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + std::string val((char *) buf, (char *) buf + len); +#ifndef RNP_USE_STD_REGEX + static regex_t r; + regmatch_t matches[1]; + assert_int_equal(regcomp(&r, reg.c_str(), REG_EXTENDED), 0); + assert_int_equal(regexec(&r, val.c_str(), 1, matches, 0), 0); +#else + static std::regex re(reg, std::regex_constants::extended | std::regex_constants::icase); + std::smatch result; + assert_true(std::regex_search(val, result, re)); +#endif + /* Fails to load without base64 flag */ + assert_false(import_all_keys(ffi, buf, len)); + /* Now should succeed */ + assert_true( + check_key_autocrypt(output, "7bc6709b15c23a4a", "8a05b89fad5aded1", "key0-uid2", true)); + rnp_output_destroy(output); + + /* remove first subkey and export again */ + assert_rnp_success(rnp_locate_key(ffi, "keyid", "1ed63ee56fadc34d", &sub)); + assert_rnp_success(rnp_key_remove(sub, RNP_KEY_REMOVE_PUBLIC)); + rnp_key_handle_destroy(sub); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_key_export_autocrypt(key, NULL, "key0-uid0", output, 0)); + assert_true( + check_key_autocrypt(output, "7bc6709b15c23a4a", "8a05b89fad5aded1", "key0-uid0")); + rnp_output_destroy(output); + rnp_key_handle_destroy(key); + + /* primary key with encrypting capability, make sure subkey is exported */ + 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/encrypting-primary.pgp")); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "92091b7b76c50017", &key)); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_key_export_autocrypt( + key, NULL, "encrypting primary <encrypting_primary@rnp>", output, 0)); + assert_true(check_key_autocrypt(output, + "92091b7b76c50017", + "c2e243e872c1fe50", + "encrypting primary <encrypting_primary@rnp>")); + rnp_output_destroy(output); + rnp_key_handle_destroy(key); + + /* export key with single uid and subkey */ + 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-sub-pub.pgp")); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669ffde3c", &key)); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_key_export_autocrypt(key, NULL, NULL, output, 0)); + assert_true(check_key_autocrypt( + output, "0451409669ffde3c", "dd23ceb7febeff17", "Alice <alice@rnp>")); + rnp_output_destroy(output); + rnp_key_handle_destroy(key); + + /* export key with sign-only subkey: fail */ + 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-sign-sub-pub.pgp")); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669ffde3c", &key)); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "22f3a217c0e439cb", &sub)); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_failure(rnp_key_export_autocrypt(key, sub, NULL, output, 0)); + assert_int_equal(output->dst.writeb, 0); + assert_rnp_failure(rnp_key_export_autocrypt(key, NULL, NULL, output, 0)); + assert_int_equal(output->dst.writeb, 0); + rnp_output_destroy(output); + rnp_key_handle_destroy(key); + rnp_key_handle_destroy(sub); + + /* export key without subkey: fail */ + 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_locate_key(ffi, "keyid", "0451409669ffde3c", &key)); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_failure(rnp_key_export_autocrypt(key, NULL, NULL, output, 0)); + assert_int_equal(output->dst.writeb, 0); + rnp_output_destroy(output); + rnp_key_handle_destroy(key); + + /* export secret key: make sure public is exported */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET)); + assert_true(import_all_keys(ffi, "data/test_key_validity/alice-sub-sec.pgp")); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669ffde3c", &key)); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_key_export_autocrypt(key, NULL, NULL, output, 0)); + assert_true(check_key_autocrypt( + output, "0451409669ffde3c", "dd23ceb7febeff17", "Alice <alice@rnp>")); + rnp_output_destroy(output); + rnp_key_handle_destroy(key); + + /* make sure that only self-certification is exported */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET)); + /* load key alice with 2 self-sigs, one of those is expired */ + assert_true(import_pub_keys(ffi, "data/test_key_validity/case9/pubring.gpg")); + /* add one corrupted alice's signature and one valid from Basil */ + assert_true(import_pub_keys(ffi, "data/test_key_validity/case2/pubring.gpg")); + + assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669ffde3c", &key)); + assert_int_equal(key->pub->sig_count(), 4); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_key_export_autocrypt(key, NULL, NULL, output, 0)); + assert_true(check_key_autocrypt( + output, "0451409669ffde3c", "dd23ceb7febeff17", "Alice <alice@rnp>")); + rnp_output_destroy(output); + rnp_key_handle_destroy(key); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_keys_import_autocrypt) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + + rnp_input_t input = NULL; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_key_load/ecc-25519-pub.b64")); + /* no base64 flag */ + assert_rnp_failure(rnp_import_keys(ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS, NULL)); + rnp_input_destroy(input); + /* enable base64 flag */ + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_key_load/ecc-25519-pub.b64")); + assert_rnp_success( + rnp_import_keys(ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_BASE64, NULL)); + rnp_input_destroy(input); + size_t keycount = 0; + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 1); + /* load other files, with different base64 formatting */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC)); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_key_load/ecc-25519-pub-2.b64")); + assert_rnp_success( + rnp_import_keys(ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_BASE64, NULL)); + rnp_input_destroy(input); + keycount = 0; + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 1); + + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC)); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_key_load/ecc-25519-pub-3.b64")); + assert_rnp_success( + rnp_import_keys(ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_BASE64, NULL)); + rnp_input_destroy(input); + keycount = 0; + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 1); + + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC)); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_key_load/ecc-25519-pub-4.b64")); + assert_rnp_failure( + rnp_import_keys(ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_BASE64, NULL)); + rnp_input_destroy(input); + keycount = 0; + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 0); + + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_key_load/ecc-p256k1-pub.b64")); + assert_rnp_success( + rnp_import_keys(ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_BASE64, NULL)); + rnp_input_destroy(input); + keycount = 0; + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 2); + + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC)); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_stream_key_load/ecc-p256k1-pub-2.b64")); + assert_rnp_success( + rnp_import_keys(ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_BASE64, NULL)); + rnp_input_destroy(input); + keycount = 0; + assert_rnp_success(rnp_get_public_key_count(ffi, &keycount)); + assert_int_equal(keycount, 2); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_keys_load_armored_spaces) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + const char *key = R"key( + -----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEXLO69BYJKwYBBAHaRw8BAQdAWsoBwHOLMrbp7ykSSCD7FYG7tMYT74aLn5wh +Q63nmJC0BmVjZHNhMIiQBBMWCAA4FiEEMuxFQcPhApFLtGbaEJXD7W1DwDsFAlyz +uvQCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQEJXD7W1DwDs/cwD+PQt4 +GnDUFFW2omo7XJh6AUUC4eUnKQoMWoD3iwYetCwA/1leV7sUdsvs5wvkp+LJVDTW +dbpkwTCmBVbAmazgea0B +=omFJ +-----END PGP PUBLIC KEY BLOCK----- +)key"; + + rnp_input_t input = NULL; + assert_rnp_success(rnp_input_from_memory(&input, (uint8_t *) key, strlen(key), false)); + assert_rnp_success(rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_PUBLIC_KEYS)); + rnp_input_destroy(input); + size_t keys = 0; + assert_rnp_success(rnp_get_public_key_count(ffi, &keys)); + assert_int_equal(keys, 1); + rnp_ffi_destroy(ffi); +} + +/* Functions below are used to demonstrate how to check whether key has weak MD5/SHA1 + * signatures, and may be reused later in FFI code */ +static bool +is_self_signature(const char *keyid, rnp_signature_handle_t sig) +{ + char *signer = NULL; + rnp_signature_get_keyid(sig, &signer); + if (!signer) { + return false; + } + bool result = !strcmp(keyid, signer); + rnp_buffer_destroy(signer); + return result; +} + +static bool +is_weak_signature(rnp_ffi_t ffi, rnp_signature_handle_t sig) +{ + char * hash = NULL; + uint32_t creation = 0; + rnp_signature_get_hash_alg(sig, &hash); + rnp_signature_get_creation(sig, &creation); + /* This approach would be more general, however hardcoding MD5/SHA1 may be used as well */ + uint32_t flags = RNP_SECURITY_VERIFY_KEY; + uint32_t level = 0; + rnp_get_security_rule(ffi, RNP_FEATURE_HASH_ALG, hash, creation, &flags, NULL, &level); + bool res = level < RNP_SECURITY_DEFAULT; + if (res) { + printf( + "Detected weak signature with %s hash, created at %zu\n", hash, (size_t) creation); + } + rnp_buffer_destroy(hash); + return res; +} + +static const std::string +get_uid_str(rnp_uid_handle_t uid) +{ + uint32_t type = 0; + rnp_uid_get_type(uid, &type); + switch (type) { + case RNP_USER_ID: { + void * data = NULL; + size_t len = 0; + rnp_uid_get_data(uid, &data, &len); + std::string res((const char *) data, (const char *) data + len); + rnp_buffer_destroy(data); + return res; + } + case RNP_USER_ATTR: + return "photo"; + default: + return "Unknown"; + } +} + +static size_t +key_weak_self_signatures_count(rnp_ffi_t ffi, rnp_key_handle_t key) +{ + char *keyid = NULL; + rnp_key_get_keyid(key, &keyid); + bool valid = false; + rnp_key_is_valid(key, &valid); + printf( + "Key %s is %s, checking for the weak signatures.\n", keyid, valid ? "valid" : "invalid"); + /* Check direct-key signatures */ + size_t res = 0; + size_t count = 0; + rnp_key_get_signature_count(key, &count); + for (size_t i = 0; i < count; i++) { + rnp_signature_handle_t sig = NULL; + rnp_key_get_signature_at(key, i, &sig); + if (is_self_signature(keyid, sig) && is_weak_signature(ffi, sig)) { + printf("Key %s has weak direct-key signature at index %zu.\n", keyid, i); + res++; + } + rnp_signature_handle_destroy(sig); + } + /* Check certifications */ + size_t uidcount = 0; + rnp_key_get_uid_count(key, &uidcount); + for (size_t i = 0; i < uidcount; i++) { + rnp_uid_handle_t uid = NULL; + rnp_key_get_uid_handle_at(key, i, &uid); + count = 0; + rnp_uid_get_signature_count(uid, &count); + for (size_t j = 0; j < count; j++) { + rnp_signature_handle_t sig = NULL; + rnp_uid_get_signature_at(uid, j, &sig); + if (is_self_signature(keyid, sig) && is_weak_signature(ffi, sig)) { + auto uidstr = get_uid_str(uid); + printf("Uid %s of the key %s has weak self-certification at index %zu.\n", + uidstr.c_str(), + keyid, + j); + res++; + } + rnp_signature_handle_destroy(sig); + } + rnp_uid_handle_destroy(uid); + } + /* Check subkeys */ + size_t subcount = 0; + rnp_key_get_subkey_count(key, &subcount); + for (size_t i = 0; i < subcount; i++) { + rnp_key_handle_t subkey = NULL; + rnp_key_get_subkey_at(key, i, &subkey); + count = 0; + rnp_key_get_signature_count(subkey, &count); + for (size_t j = 0; j < count; j++) { + rnp_signature_handle_t sig = NULL; + rnp_key_get_signature_at(subkey, j, &sig); + if (is_self_signature(keyid, sig) && is_weak_signature(ffi, sig)) { + char *subid = NULL; + rnp_key_get_keyid(subkey, &subid); + printf("Subkey %s of the key %s has weak binding signature at index %zu.\n", + subid, + keyid, + j); + res++; + rnp_buffer_destroy(subid); + } + rnp_signature_handle_destroy(sig); + } + rnp_key_handle_destroy(subkey); + } + rnp_buffer_destroy(keyid); + return res; +} + +TEST_F(rnp_tests, test_ffi_sha1_self_signatures) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + /* This key has SHA1 self signature, made before the cut-off date */ + assert_true(import_pub_keys(ffi, "data/test_stream_key_load/rsa-rsa-pub.asc")); + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "2fb9179118898e8b", &key)); + /* Check key validity */ + bool valid = false; + assert_rnp_success(rnp_key_is_valid(key, &valid)); + assert_true(valid); + size_t count = 0; + /* Check uid validity */ + assert_rnp_success(rnp_key_get_uid_count(key, &count)); + assert_int_equal(count, 1); + rnp_uid_handle_t uid = NULL; + assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid)); + assert_rnp_success(rnp_uid_is_valid(uid, &valid)); + assert_true(valid); + rnp_uid_handle_destroy(uid); + /* Check subkey validity */ + 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)); + assert_rnp_success(rnp_key_is_valid(sub, &valid)); + assert_true(valid); + /* Check weak signature count */ + assert_int_equal(key_weak_self_signatures_count(ffi, key), 0); + rnp_key_handle_destroy(sub); + rnp_key_handle_destroy(key); + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC)); + + /* Check the key which has SHA1 self signature, made after the cut-off date */ + assert_rnp_success(rnp_set_timestamp(ffi, SHA1_KEY_FROM + 10)); + assert_true(import_pub_keys(ffi, "data/test_forged_keys/eddsa-2024-pub.pgp")); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "980e3741f632212c", &key)); + /* Check key validity */ + assert_rnp_success(rnp_key_is_valid(key, &valid)); + assert_false(valid); + /* Check uid validity */ + assert_rnp_success(rnp_key_get_uid_count(key, &count)); + assert_int_equal(count, 1); + assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid)); + assert_rnp_success(rnp_uid_is_valid(uid, &valid)); + assert_false(valid); + rnp_uid_handle_destroy(uid); + /* Check subkey validity */ + assert_rnp_success(rnp_key_get_subkey_count(key, &count)); + assert_int_equal(count, 1); + assert_rnp_success(rnp_key_get_subkey_at(key, 0, &sub)); + assert_rnp_success(rnp_key_is_valid(sub, &valid)); + assert_false(valid); + /* Check weak signature count */ + assert_int_equal(key_weak_self_signatures_count(ffi, key), 2); + rnp_key_handle_destroy(sub); + rnp_key_handle_destroy(key); + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC)); + /* Now allow the SHA1 hash */ + assert_rnp_success(rnp_add_security_rule(ffi, + RNP_FEATURE_HASH_ALG, + "SHA1", + RNP_SECURITY_OVERRIDE, + SHA1_KEY_FROM + 1, + RNP_SECURITY_DEFAULT)); + + assert_true(import_pub_keys(ffi, "data/test_forged_keys/eddsa-2024-pub.pgp")); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "980e3741f632212c", &key)); + /* Check key validity */ + assert_rnp_success(rnp_key_is_valid(key, &valid)); + assert_true(valid); + /* Check uid validity */ + assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid)); + assert_rnp_success(rnp_uid_is_valid(uid, &valid)); + assert_true(valid); + rnp_uid_handle_destroy(uid); + /* Check subkey validity */ + assert_rnp_success(rnp_key_get_subkey_at(key, 0, &sub)); + assert_rnp_success(rnp_key_is_valid(sub, &valid)); + assert_true(valid); + /* Check weak signature count */ + assert_int_equal(key_weak_self_signatures_count(ffi, key), 0); + rnp_key_handle_destroy(sub); + rnp_key_handle_destroy(key); + + /* Check the key which has MD5 self signature, made after the cut-off date */ + assert_true(import_pub_keys(ffi, "data/test_forged_keys/eddsa-2012-md5-pub.pgp")); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "8801eafbd906bd21", &key)); + /* Check key validity */ + assert_rnp_success(rnp_key_is_valid(key, &valid)); + assert_false(valid); + /* Check uid validity */ + assert_rnp_success(rnp_key_get_uid_handle_at(key, 0, &uid)); + assert_rnp_success(rnp_uid_is_valid(uid, &valid)); + assert_false(valid); + rnp_uid_handle_destroy(uid); + /* Check subkey validity */ + assert_rnp_success(rnp_key_get_subkey_at(key, 0, &sub)); + assert_rnp_success(rnp_key_is_valid(sub, &valid)); + assert_false(valid); + /* Check weak signature count */ + assert_int_equal(key_weak_self_signatures_count(ffi, key), 2); + rnp_key_handle_destroy(sub); + rnp_key_handle_destroy(key); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_reprotect_keys) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + /* Cast5-encrypted keys */ + assert_true( + load_keys_gpg(ffi, "data/keyrings/1/pubring.gpg", "data/keyrings/1/secring-cast5.gpg")); + + rnp_identifier_iterator_t it = NULL; + assert_rnp_success(rnp_identifier_iterator_create(ffi, &it, "fingerprint")); + assert_non_null(it); + const char *ident = NULL; + do { + ident = NULL; + assert_rnp_success(rnp_identifier_iterator_next(it, &ident)); + if (!ident) { + break; + } + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "fingerprint", ident, &key)); + if (cast5_enabled()) { + assert_rnp_success(rnp_key_unprotect(key, "password")); + assert_rnp_success(rnp_key_protect(key, "password", "AES256", NULL, NULL, 65536)); + } else { + assert_rnp_failure(rnp_key_unprotect(key, "password")); + } + rnp_key_handle_destroy(key); + } while (1); + assert_rnp_success(rnp_identifier_iterator_destroy(it)); + /* AES-encrypted keys */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET)); + assert_true( + load_keys_gpg(ffi, "data/keyrings/1/pubring.gpg", "data/keyrings/1/secring.gpg")); + assert_rnp_success(rnp_identifier_iterator_create(ffi, &it, "fingerprint")); + assert_non_null(it); + do { + ident = NULL; + assert_rnp_success(rnp_identifier_iterator_next(it, &ident)); + if (!ident) { + break; + } + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "fingerprint", ident, &key)); + assert_rnp_success(rnp_key_unprotect(key, "password")); + if (cast5_enabled()) { + assert_rnp_success(rnp_key_protect(key, "password", "CAST5", NULL, NULL, 65536)); + } else { + assert_rnp_success(rnp_key_protect(key, "password", "AES128", NULL, NULL, 65536)); + } + rnp_key_handle_destroy(key); + } while (1); + assert_rnp_success(rnp_identifier_iterator_destroy(it)); + rnp_ffi_destroy(ffi); +} |