Revision control
Copy as Markdown
Other Tools
/*
* 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;
}