/* * Copyright (c) 2018-2019 [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 "../librekey/key_store_pgp.h" #include "pgp-key.h" #include "rnp_tests.h" #include "support.h" #include "../librepgp/stream-packet.h" #include "../librepgp/stream-armor.h" static bool all_keys_valid(const rnp_key_store_t *keyring, pgp_key_t *except = NULL) { char keyid[PGP_KEY_ID_SIZE * 2 + 3] = {0}; for (auto &key : keyring->keys) { if ((!key.valid() || key.expired()) && (&key != except)) { if (!rnp::hex_encode(key.keyid().data(), key.keyid().size(), keyid, sizeof(keyid), rnp::HEX_LOWERCASE)) { throw std::exception(); } RNP_LOG("key %s is not valid", keyid); return false; } } return true; } TEST_F(rnp_tests, test_key_validate) { rnp_key_store_t *pubring; rnp_key_store_t *secring; pgp_key_t * key = NULL; pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, "data/keyrings/1/pubring.gpg", global_ctx); assert_true(rnp_key_store_load_from_path(pubring, NULL)); /* this keyring has one expired subkey */ assert_non_null(key = rnp_tests_get_key_by_id(pubring, "1d7e8a5393c997a8")); assert_false(key->valid()); assert_true(key->expired()); assert_true(all_keys_valid(pubring, key)); delete pubring; /* secret key is marked is expired as well */ secring = new rnp_key_store_t(PGP_KEY_STORE_GPG, "data/keyrings/1/secring.gpg", global_ctx); assert_true(rnp_key_store_load_from_path(secring, NULL)); assert_non_null(key = rnp_tests_get_key_by_id(secring, "1d7e8a5393c997a8")); assert_false(key->valid()); assert_true(key->expired()); assert_true(all_keys_valid(secring, key)); delete secring; pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, "data/keyrings/2/pubring.gpg", global_ctx); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_true(all_keys_valid(pubring)); /* secret keyring doesn't have signatures - so keys are marked as invalid */ secring = new rnp_key_store_t(PGP_KEY_STORE_GPG, "data/keyrings/2/secring.gpg", global_ctx); assert_true(rnp_key_store_load_from_path(secring, NULL)); assert_false(all_keys_valid(secring)); /* but after adding signatures from public it is marked as valid */ assert_non_null(key = rnp_tests_get_key_by_id(pubring, "dc70c124a50283f1")); assert_non_null(rnp_key_store_import_key(secring, key, true, NULL)); assert_true(all_keys_valid(secring)); delete pubring; delete secring; pubring = new rnp_key_store_t(PGP_KEY_STORE_KBX, "data/keyrings/3/pubring.kbx", global_ctx); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_true(all_keys_valid(pubring)); secring = new rnp_key_store_t(PGP_KEY_STORE_G10, "data/keyrings/3/private-keys-v1.d", global_ctx); pgp_key_provider_t key_provider(rnp_key_provider_store, pubring); assert_true(rnp_key_store_load_from_path(secring, &key_provider)); assert_true(all_keys_valid(secring)); delete pubring; delete secring; pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, "data/keyrings/4/pubring.pgp", global_ctx); assert_true(rnp_key_store_load_from_path(pubring, NULL)); /* certification has signature with MD5 hash algorithm */ assert_false(all_keys_valid(pubring)); rnp_key_store_clear(pubring); /* add rule which allows MD5 */ rnp::SecurityRule allow_md5( rnp::FeatureType::Hash, PGP_HASH_MD5, rnp::SecurityLevel::Default); allow_md5.override = true; global_ctx.profile.add_rule(allow_md5); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_true(all_keys_valid(pubring)); rnp_key_store_clear(pubring); /* remove rule */ assert_true(global_ctx.profile.del_rule(allow_md5)); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_false(all_keys_valid(pubring)); delete pubring; /* secret keyring doesn't have certifications - so marked as invalid */ secring = new rnp_key_store_t(PGP_KEY_STORE_GPG, "data/keyrings/4/secring.pgp", global_ctx); assert_true(rnp_key_store_load_from_path(secring, NULL)); assert_false(all_keys_valid(secring)); delete secring; pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, "data/keyrings/5/pubring.gpg", global_ctx); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_true(all_keys_valid(pubring)); delete pubring; secring = new rnp_key_store_t(PGP_KEY_STORE_GPG, "data/keyrings/5/secring.gpg", global_ctx); assert_true(rnp_key_store_load_from_path(secring, NULL)); assert_true(all_keys_valid(secring)); delete secring; } #define DATA_PATH "data/test_forged_keys/" static void key_store_add(rnp_key_store_t *keyring, const char *keypath) { pgp_source_t keysrc = {}; pgp_transferable_key_t tkey = {}; assert_rnp_success(init_file_src(&keysrc, keypath)); assert_rnp_success(process_pgp_key(keysrc, tkey, false)); assert_true(rnp_key_store_add_transferable_key(keyring, &tkey)); src_close(&keysrc); } static bool key_check(rnp_key_store_t *keyring, const std::string &keyid, bool valid, bool expired = false) { pgp_key_t *key = rnp_tests_get_key_by_id(keyring, keyid); return key && (key->validated()) && (key->valid() == valid) && (key->expired() == expired); } TEST_F(rnp_tests, test_forged_key_validate) { rnp_key_store_t *pubring; pgp_key_t * key = NULL; pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, "", global_ctx); /* load valid dsa-eg key */ key_store_add(pubring, DATA_PATH "dsa-eg-pub.pgp"); assert_true(key_check(pubring, "C8A10A7D78273E10", true)); rnp_key_store_clear(pubring); /* load dsa-eg key with forged self-signature and binding. Subkey will not be valid as * well. */ key_store_add(pubring, DATA_PATH "dsa-eg-pub-forged-key.pgp"); assert_true(key_check(pubring, "C8A10A7D78273E10", false)); assert_true(key_check(pubring, "02A5715C3537717E", false)); rnp_key_store_clear(pubring); /* load dsa-eg key with forged key material */ key_store_add(pubring, DATA_PATH "dsa-eg-pub-forged-material.pgp"); key = rnp_tests_get_key_by_id(pubring, "C8A10A7D78273E10"); assert_null(key); /* malformed key material causes keyid change */ key = rnp_tests_get_key_by_id(pubring, "C258AB3B54097B9B"); assert_non_null(key); assert_false(key->valid()); assert_false(key->expired()); rnp_key_store_clear(pubring); /* load dsa-eg keypair with forged subkey binding signature */ key_store_add(pubring, DATA_PATH "dsa-eg-pub-forged-subkey.pgp"); assert_true(key_check(pubring, "02A5715C3537717E", false)); assert_true(key_check(pubring, "C8A10A7D78273E10", true)); rnp_key_store_clear(pubring); /* load valid eddsa key */ key_store_add(pubring, DATA_PATH "ecc-25519-pub.pgp"); assert_true(key_check(pubring, "CC786278981B0728", true)); rnp_key_store_clear(pubring); /* load eddsa key with forged self-signature */ key_store_add(pubring, DATA_PATH "ecc-25519-pub-forged-key.pgp"); assert_true(key_check(pubring, "CC786278981B0728", false)); rnp_key_store_clear(pubring); /* load eddsa key with forged key material */ key_store_add(pubring, DATA_PATH "ecc-25519-pub-forged-material.pgp"); key = rnp_tests_get_key_by_id(pubring, "1BEF78DF765B79A2"); assert_non_null(key); assert_false(key->valid()); assert_false(key->expired()); rnp_key_store_clear(pubring); /* load valid ecdsa/ecdh p-256 keypair */ key_store_add(pubring, DATA_PATH "ecc-p256-pub.pgp"); assert_true(key_check(pubring, "23674F21B2441527", true)); assert_true(key_check(pubring, "37E285E9E9851491", true)); rnp_key_store_clear(pubring); /* load ecdsa/ecdh key with forged self-signature. Both valid since there is valid binding. */ key_store_add(pubring, DATA_PATH "ecc-p256-pub-forged-key.pgp"); assert_true(key_check(pubring, "23674F21B2441527", true)); assert_true(key_check(pubring, "37E285E9E9851491", true)); rnp_key_store_clear(pubring); /* load ecdsa/ecdh key with forged key material. Subkey is not valid as well. */ key_store_add(pubring, DATA_PATH "ecc-p256-pub-forged-material.pgp"); key = rnp_tests_get_key_by_id(pubring, "23674F21B2441527"); assert_null(key); key = rnp_tests_get_key_by_id(pubring, "41DEA786D18E5184"); assert_non_null(key); assert_false(key->valid()); assert_false(key->expired()); assert_true(key_check(pubring, "37E285E9E9851491", false)); rnp_key_store_clear(pubring); /* load ecdsa/ecdh keypair with forged subkey binding signature */ key_store_add(pubring, DATA_PATH "ecc-p256-pub-forged-subkey.pgp"); assert_true(key_check(pubring, "37E285E9E9851491", false)); assert_true(key_check(pubring, "23674F21B2441527", true)); rnp_key_store_clear(pubring); /* load ecdsa/ecdh keypair without certification: valid since have binding */ key_store_add(pubring, DATA_PATH "ecc-p256-pub-no-certification.pgp"); assert_true(key_check(pubring, "23674F21B2441527", true)); assert_true(key_check(pubring, "37E285E9E9851491", true)); rnp_key_store_clear(pubring); /* load ecdsa/ecdh keypair without certification and invalid binding */ key_store_add(pubring, DATA_PATH "ecc-p256-pub-no-cert-malf-binding.pgp"); assert_true(key_check(pubring, "23674F21B2441527", false)); assert_true(key_check(pubring, "37E285E9E9851491", false)); rnp_key_store_clear(pubring); /* load ecdsa/ecdh keypair without subkey binding */ key_store_add(pubring, DATA_PATH "ecc-p256-pub-no-binding.pgp"); assert_true(key_check(pubring, "23674F21B2441527", true)); assert_true(key_check(pubring, "37E285E9E9851491", false)); rnp_key_store_clear(pubring); /* load valid rsa/rsa keypair */ key_store_add(pubring, DATA_PATH "rsa-rsa-pub.pgp"); /* it is valid only till year 2024 since SHA1 hash is used for signatures */ assert_true(key_check(pubring, "2FB9179118898E8B", true)); assert_true(key_check(pubring, "6E2F73008F8B8D6E", true)); rnp_key_store_clear(pubring); /* load eddsa key which uses SHA1 signature and is created after the cutoff date */ global_ctx.set_time(SHA1_KEY_FROM + 10); key_store_add(pubring, DATA_PATH "eddsa-2024-pub.pgp"); assert_false(key_check(pubring, "980E3741F632212C", true)); assert_false(key_check(pubring, "6DA00BF7F8B59B53", true)); global_ctx.set_time(0); rnp_key_store_clear(pubring); /* load rsa/rsa key with forged self-signature. Valid because of valid binding. */ key_store_add(pubring, DATA_PATH "rsa-rsa-pub-forged-key.pgp"); assert_true(key_check(pubring, "2FB9179118898E8B", true)); assert_true(key_check(pubring, "6E2F73008F8B8D6E", true)); rnp_key_store_clear(pubring); /* load rsa/rsa key with forged key material. Subkey is not valid as well. */ key_store_add(pubring, DATA_PATH "rsa-rsa-pub-forged-material.pgp"); key = rnp_tests_get_key_by_id(pubring, "2FB9179118898E8B"); assert_null(key); key = rnp_tests_get_key_by_id(pubring, "791B14952D8F906C"); assert_non_null(key); assert_false(key->valid()); assert_false(key->expired()); assert_true(key_check(pubring, "6E2F73008F8B8D6E", false)); rnp_key_store_clear(pubring); /* load rsa/rsa keypair with forged subkey binding signature */ key_store_add(pubring, DATA_PATH "rsa-rsa-pub-forged-subkey.pgp"); assert_true(key_check(pubring, "2FB9179118898E8B", true)); assert_true(key_check(pubring, "6E2F73008F8B8D6E", false)); rnp_key_store_clear(pubring); /* load rsa/rsa keypair with future creation date */ key_store_add(pubring, DATA_PATH "rsa-rsa-pub-future-key.pgp"); assert_true(key_check(pubring, "3D032D00EE1EC3F5", false)); assert_true(key_check(pubring, "021085B640CE8DCE", false)); rnp_key_store_clear(pubring); /* load eddsa/rsa keypair with certification with future creation date - valid because of * binding. */ key_store_add(pubring, DATA_PATH "ecc-25519-pub-future-cert.pgp"); assert_true(key_check(pubring, "D3B746FA852C2BE8", true)); assert_true(key_check(pubring, "EB8C21ACDC15CA14", true)); rnp_key_store_clear(pubring); /* load eddsa/rsa keypair with certification with future creation date - invalid because of * invalid binding. */ key_store_add(pubring, DATA_PATH "ecc-25519-pub-future-cert-malf-bind.pgp"); assert_true(key_check(pubring, "D3B746FA852C2BE8", false)); assert_true(key_check(pubring, "EB8C21ACDC15CA14", false)); rnp_key_store_clear(pubring); /* load ecdsa/rsa keypair with expired subkey */ key_store_add(pubring, DATA_PATH "ecc-p256-pub-expired-subkey.pgp"); assert_true(key_check(pubring, "23674F21B2441527", true)); assert_true(key_check(pubring, "37E285E9E9851491", false, true)); rnp_key_store_clear(pubring); /* load ecdsa/ecdh keypair with expired key */ key_store_add(pubring, DATA_PATH "ecc-p256-pub-expired-key.pgp"); assert_true(key_check(pubring, "23674F21B2441527", false, true)); assert_true(key_check(pubring, "37E285E9E9851491", false)); rnp_key_store_clear(pubring); delete pubring; } #define KEYSIG_PATH "data/test_key_validity/" TEST_F(rnp_tests, test_key_validity) { rnp_key_store_t *pubring; pgp_key_t * key = NULL; /* Case1: * Keys: Alice [pub] * Alice is signed by Basil, but without the Basil's key. * Result: Alice [valid] */ pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, KEYSIG_PATH "case1/pubring.gpg", global_ctx); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_non_null(key = rnp_tests_key_search(pubring, "Alice ")); assert_true(key->valid()); assert_false(key->expired()); delete pubring; /* Case2: * Keys: Alice [pub], Basil [pub] * Alice is signed by Basil, Basil is signed by Alice, but Alice's self-signature is * corrupted. * Result: Alice [invalid], Basil [valid] */ pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, KEYSIG_PATH "case2/pubring.gpg", global_ctx); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_non_null(key = rnp_tests_get_key_by_id(pubring, "0451409669FFDE3C")); assert_false(key->valid()); assert_false(key->expired()); assert_non_null(key = rnp_tests_key_search(pubring, "Basil ")); assert_true(key->valid()); assert_false(key->expired()); delete pubring; /* Case3: * Keys: Alice [pub], Basil [pub] * Alice is signed by Basil, but doesn't have self-signature * Result: Alice [invalid] */ pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, KEYSIG_PATH "case3/pubring.gpg", global_ctx); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_non_null(key = rnp_tests_get_key_by_id(pubring, "0451409669FFDE3C")); assert_false(key->valid()); assert_false(key->expired()); assert_non_null(key = rnp_tests_key_search(pubring, "Basil ")); assert_true(key->valid()); assert_false(key->expired()); delete pubring; /* Case4: * Keys Alice [pub, sub] * Alice subkey has invalid binding signature * Result: Alice [valid], Alice sub [invalid] */ pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, KEYSIG_PATH "case4/pubring.gpg", global_ctx); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_non_null(key = rnp_tests_key_search(pubring, "Alice ")); assert_true(key->valid()); assert_false(key->expired()); assert_int_equal(key->subkey_count(), 1); pgp_key_t *subkey = NULL; assert_non_null(subkey = pgp_key_get_subkey(key, pubring, 0)); assert_false(subkey->valid()); assert_false(subkey->expired()); delete pubring; /* Case5: * Keys Alice [pub, sub], Basil [pub] * Alice subkey has valid binding signature, but from the key Basil * Result: Alice [valid], Alice sub [invalid] * * Note: to re-generate keyring file, use generate.cpp from case5 folder. * To build it, feed -DBUILD_TESTING_GENERATORS=On to the cmake. */ pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, KEYSIG_PATH "case5/pubring.gpg", global_ctx); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_non_null(key = rnp_tests_key_search(pubring, "Alice ")); assert_true(key->valid()); assert_false(key->expired()); assert_int_equal(key->subkey_count(), 1); assert_non_null(subkey = pgp_key_get_subkey(key, pubring, 0)); assert_false(subkey->valid()); assert_false(subkey->expired()); delete pubring; /* Case6: * Keys Alice [pub, sub] * Key Alice has revocation signature by Alice, and subkey doesn't * Result: Alice [invalid], Alice sub [invalid] */ pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, KEYSIG_PATH "case6/pubring.gpg", global_ctx); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_non_null(key = rnp_tests_key_search(pubring, "Alice ")); assert_false(key->valid()); assert_false(key->expired()); assert_true(key->revoked()); assert_int_equal(key->subkey_count(), 1); assert_non_null(subkey = pgp_key_get_subkey(key, pubring, 0)); assert_false(subkey->valid()); assert_false(subkey->expired()); assert_false(subkey->revoked()); delete pubring; /* Case7: * Keys Alice [pub, sub] * Alice subkey has revocation signature by Alice * Result: Alice [valid], Alice sub [invalid] */ pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, KEYSIG_PATH "case7/pubring.gpg", global_ctx); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_non_null(key = rnp_tests_key_search(pubring, "Alice ")); assert_true(key->valid()); assert_false(key->expired()); assert_false(key->revoked()); assert_int_equal(key->subkey_count(), 1); assert_non_null(subkey = pgp_key_get_subkey(key, pubring, 0)); assert_false(subkey->valid()); assert_false(subkey->expired()); assert_true(subkey->revoked()); delete pubring; /* Case8: * Keys Alice [pub, sub] * Userid is stripped from the key, but it still has valid subkey binding * Result: Alice [valid], Alice sub[valid] */ pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, KEYSIG_PATH "case8/pubring.gpg", global_ctx); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_non_null(key = rnp_tests_get_key_by_id(pubring, "0451409669FFDE3C")); assert_true(key->valid()); assert_int_equal(key->subkey_count(), 1); assert_non_null(subkey = pgp_key_get_subkey(key, pubring, 0)); assert_true(subkey->valid()); delete pubring; /* Case9: * Keys Alice [pub, sub] * Alice key has two self-signatures, one which expires key and second without key * expiration. * Result: Alice [valid], Alice sub[valid] */ pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, KEYSIG_PATH "case9/pubring.gpg", global_ctx); assert_non_null(pubring); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_non_null(key = rnp_tests_get_key_by_id(pubring, "0451409669FFDE3C")); assert_true(key->valid()); assert_false(key->expired()); assert_int_equal(key->subkey_count(), 1); assert_non_null(subkey = pgp_key_get_subkey(key, pubring, 0)); assert_true(subkey->valid()); assert_false(subkey->expired()); delete pubring; /* Case10: * Keys Alice [pub, sub] * Alice key has expiring direct-key signature and non-expiring self-certification. * Result: Alice [invalid], Alice sub[invalid] */ pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, KEYSIG_PATH "case10/pubring.gpg", global_ctx); assert_non_null(pubring); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_non_null(key = rnp_tests_get_key_by_id(pubring, "0451409669FFDE3C")); assert_false(key->valid()); assert_true(key->expired()); assert_int_equal(key->subkey_count(), 1); assert_non_null(subkey = pgp_key_get_subkey(key, pubring, 0)); assert_false(subkey->valid()); assert_false(subkey->expired()); delete pubring; /* Case11: * Keys Alice [pub, sub] * Alice key has expiring direct-key signature, non-expiring self-certification and * expiring primary userid certification. Result: Alice [invalid], Alice sub[invalid] */ pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, KEYSIG_PATH "case11/pubring.gpg", global_ctx); assert_non_null(pubring); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_non_null(key = rnp_tests_get_key_by_id(pubring, "0451409669FFDE3C")); assert_false(key->valid()); assert_true(key->expired()); assert_int_equal(key->expiration(), 100); assert_int_equal(key->subkey_count(), 1); assert_non_null(subkey = pgp_key_get_subkey(key, pubring, 0)); assert_false(subkey->valid()); assert_false(subkey->expired()); delete pubring; /* Case12: * Keys Alice [pub, sub] * Alice key has non-expiring direct-key signature, non-expiring self-certification and * expiring primary userid certification. Result: Alice [invalid], Alice sub[invalid] */ pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, KEYSIG_PATH "case12/pubring.gpg", global_ctx); assert_non_null(pubring); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_non_null(key = rnp_tests_get_key_by_id(pubring, "0451409669FFDE3C")); assert_false(key->valid()); assert_true(key->expired()); assert_int_equal(key->expiration(), 2000); assert_int_equal(key->subkey_count(), 1); assert_non_null(subkey = pgp_key_get_subkey(key, pubring, 0)); assert_false(subkey->valid()); assert_false(subkey->expired()); delete pubring; /* Case13: * Keys Alice [pub, sub] * Alice key has expiring direct-key signature, non-expiring self-certification and * non-expiring primary userid certification. Result: Alice [invalid], Alice sub[invalid] */ pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, KEYSIG_PATH "case13/pubring.gpg", global_ctx); assert_non_null(pubring); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_non_null(key = rnp_tests_get_key_by_id(pubring, "0451409669FFDE3C")); assert_false(key->valid()); assert_true(key->expired()); assert_int_equal(key->expiration(), 6); assert_int_equal(key->subkey_count(), 1); assert_non_null(subkey = pgp_key_get_subkey(key, pubring, 0)); assert_false(subkey->valid()); assert_false(subkey->expired()); delete pubring; /* Case14: * Keys Alice [pub, sub] * Alice key has expiring direct-key signature, non-expiring self-certification and * non-expiring primary userid certification (with 0 key expiration subpacket). Result: * Alice [invalid], Alice sub[invalid] */ pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, KEYSIG_PATH "case14/pubring.gpg", global_ctx); assert_non_null(pubring); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_non_null(key = rnp_tests_get_key_by_id(pubring, "0451409669FFDE3C")); assert_false(key->valid()); assert_true(key->expired()); assert_int_equal(key->expiration(), 6); assert_int_equal(key->subkey_count(), 1); assert_non_null(subkey = pgp_key_get_subkey(key, pubring, 0)); assert_false(subkey->valid()); assert_false(subkey->expired()); delete pubring; /* Case15: * Keys [pub, sub] * Signing subkey has expired primary-key signature embedded into the subkey binding. * Result: primary [valid], sub[invalid] */ pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, KEYSIG_PATH "case15/pubring.gpg", global_ctx); assert_non_null(pubring); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_non_null(key = rnp_tests_get_key_by_id(pubring, "E863072D3E9042EE")); assert_true(key->valid()); assert_false(key->expired()); assert_int_equal(key->expiration(), 0); assert_int_equal(key->subkey_count(), 1); assert_non_null(subkey = pgp_key_get_subkey(key, pubring, 0)); assert_false(subkey->valid()); assert_false(subkey->expired()); delete pubring; } TEST_F(rnp_tests, test_key_expiry_direct_sig) { /* this test was mainly used to generate test data for cases 10-12 in test_key_validity */ rnp_key_store_t *secring = new rnp_key_store_t(PGP_KEY_STORE_GPG, KEYSIG_PATH "alice-sub-sec.pgp", global_ctx); assert_true(rnp_key_store_load_from_path(secring, NULL)); pgp_key_t *key = NULL; assert_non_null(key = rnp_tests_key_search(secring, "Alice ")); assert_true(key->valid()); assert_false(key->expired()); /* create direct-key signature */ pgp_signature_t sig; sig.version = PGP_V4; sig.halg = PGP_HASH_SHA256; sig.palg = key->alg(); sig.set_type(PGP_SIG_DIRECT); sig.set_creation(key->creation()); sig.set_key_expiration(1000); sig.set_keyfp(key->fp()); sig.set_keyid(key->keyid()); pgp_password_provider_t pprov(string_copy_password_callback, (void *) "password"); key->unlock(pprov); key->sign_direct(key->pkt(), sig, global_ctx); key->add_sig(sig, PGP_UID_NONE); key->revalidate(*secring); /* key becomsed invalid even since it is secret */ assert_int_equal(key->expiration(), 1000); assert_false(key->valid()); assert_true(key->expired()); rnp_key_store_t *pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, KEYSIG_PATH "alice-sub-pub.pgp", global_ctx); assert_true(rnp_key_store_load_from_path(pubring, NULL)); pgp_key_t *pubkey = NULL; assert_non_null(pubkey = rnp_tests_key_search(pubring, "Alice ")); assert_int_equal(pubkey->expiration(), 0); assert_true(pubkey->valid()); assert_false(pubkey->expired()); pgp_key_t *subpub = NULL; assert_non_null(subpub = rnp_tests_get_key_by_id(pubring, "dd23ceb7febeff17")); assert_int_equal(subpub->expiration(), 0); assert_true(subpub->valid()); assert_false(subpub->expired()); pubkey->add_sig(sig, PGP_UID_NONE); pubkey->revalidate(*pubring); assert_int_equal(pubkey->expiration(), 1000); assert_false(pubkey->valid()); assert_true(pubkey->expired()); assert_int_equal(subpub->expiration(), 0); assert_false(subpub->valid()); assert_false(subpub->expired()); /* add primary userid with smaller expiration date */ rnp_selfsig_cert_info_t selfsig1 = {}; const char * boris = "Boris "; selfsig1.userid = boris; selfsig1.key_expiration = 100; selfsig1.primary = true; key->add_uid_cert(selfsig1, PGP_HASH_SHA256, global_ctx); key->revalidate(*secring); /* key becomes invalid even it is secret */ assert_int_equal(key->expiration(), 100); assert_false(key->valid()); assert_true(key->expired()); delete secring; delete pubring; secring = new rnp_key_store_t(PGP_KEY_STORE_GPG, KEYSIG_PATH "alice-sub-sec.pgp", global_ctx); assert_true(rnp_key_store_load_from_path(secring, NULL)); assert_non_null(key = rnp_tests_key_search(secring, "Alice ")); /* create direct-key signature */ sig = {}; sig.version = PGP_V4; sig.halg = PGP_HASH_SHA256; sig.palg = key->alg(); sig.set_type(PGP_SIG_DIRECT); sig.set_creation(key->creation()); sig.set_key_expiration(6); sig.set_keyfp(key->fp()); sig.set_keyid(key->keyid()); key->unlock(pprov); key->sign_direct(key->pkt(), sig, global_ctx); key->add_sig(sig, PGP_UID_NONE); key->revalidate(*secring); assert_int_equal(key->expiration(), 6); /* add primary userid with 0 expiration */ selfsig1 = {}; selfsig1.userid = boris; selfsig1.key_expiration = 0; selfsig1.primary = true; key->add_uid_cert(selfsig1, PGP_HASH_SHA256, global_ctx); key->revalidate(*secring); assert_int_equal(key->expiration(), 6); pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, KEYSIG_PATH "alice-sub-pub.pgp", global_ctx); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_non_null(pubkey = rnp_tests_key_search(pubring, "Alice ")); assert_non_null(subpub = rnp_tests_get_key_by_id(pubring, "dd23ceb7febeff17")); assert_int_equal(subpub->expiration(), 0); assert_true(subpub->valid()); assert_false(subpub->expired()); pubkey->add_sig(sig, PGP_UID_NONE); pubkey->revalidate(*pubring); assert_int_equal(pubkey->expiration(), 6); assert_false(pubkey->valid()); assert_true(pubkey->expired()); pgp_transferable_userid_t truid = {}; truid.uid = key->get_uid(1).pkt; truid.signatures.push_back(key->get_sig(key->get_uid(1).get_sig(0)).sig); pubkey->add_uid(truid); pubkey->revalidate(*pubring); assert_int_equal(pubkey->expiration(), 6); assert_false(pubkey->valid()); assert_true(pubkey->expired()); /* code below may be used to print out generated key to save it somewhere */ /* pgp_dest_t out = {}; pgp_dest_t armored = {}; assert_rnp_success(init_stdout_dest(&out)); assert_rnp_success(init_armored_dst(&armored, &out, PGP_ARMORED_PUBLIC_KEY)); pubkey->write_xfer(armored, pubring); dst_close(&armored, false); dst_close(&out, false); */ delete secring; delete pubring; }