diff options
Diffstat (limited to 'src/tests/key-validate.cpp')
-rw-r--r-- | src/tests/key-validate.cpp | 765 |
1 files changed, 765 insertions, 0 deletions
diff --git a/src/tests/key-validate.cpp b/src/tests/key-validate.cpp new file mode 100644 index 0000000..928415b --- /dev/null +++ b/src/tests/key-validate.cpp @@ -0,0 +1,765 @@ +/* + * 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 <alice@rnp>")); + 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 <basil@rnp>")); + 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 <basil@rnp>")); + 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 <alice@rnp>")); + 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 <alice@rnp>")); + 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 <alice@rnp>")); + 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 <alice@rnp>")); + 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 <alice@rnp>")); + 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 <alice@rnp>")); + 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 <boris@rnp>"; + 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 <alice@rnp>")); + /* 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 <alice@rnp>")); + 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; +} |