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 "../librepgp/stream-ctx.h"
#include "pgp-key.h"
#include "ffi-priv-types.h"
#include "rnp_tests.h"
#include "support.h"
#include <fstream>
TEST_F(rnp_tests, test_key_unlock_pgp)
{
cli_rnp_t rnp = {};
const char * data = "my test data";
pgp_password_provider_t provider = {0};
static const char * keyids[] = {"7bc6709b15c23a4a", // primary
"1ed63ee56fadc34d",
"1d7e8a5393c997a8",
"8a05b89fad5aded1",
"2fcadf05ffa501bb", // primary
"54505a936a4a970e",
"326ef111425d14a5"};
assert_true(setup_cli_rnp_common(&rnp, RNP_KEYSTORE_GPG, "data/keyrings/1/", NULL));
assert_true(rnp.load_keyrings(true));
for (size_t i = 0; i < ARRAY_SIZE(keyids); i++) {
rnp_key_handle_t handle = NULL;
assert_rnp_success(rnp_locate_key(rnp.ffi, "keyid", keyids[i], &handle));
assert_non_null(handle);
bool locked = false;
assert_rnp_success(rnp_key_is_locked(handle, &locked));
// all keys in this keyring are encrypted and thus should be locked initially
assert_true(locked);
rnp_key_handle_destroy(handle);
}
std::ofstream out("dummyfile.dat");
out << data;
out.close();
// try signing with a failing password provider (should fail)
assert_rnp_success(
rnp_ffi_set_pass_provider(rnp.ffi, ffi_failing_password_provider, NULL));
rnp_cfg &cfg = rnp.cfg();
cfg.load_defaults();
cfg.set_bool(CFG_SIGN_NEEDED, true);
cfg.set_str(CFG_HASH, "SHA256");
cfg.set_int(CFG_ZLEVEL, 0);
cfg.set_str(CFG_INFILE, "dummyfile.dat");
cfg.set_str(CFG_OUTFILE, "dummyfile.dat.pgp");
cfg.add_str(CFG_SIGNERS, keyids[0]);
assert_false(cli_rnp_protect_file(&rnp));
// grab the signing key to unlock
rnp_key_handle_t key = NULL;
assert_rnp_success(rnp_locate_key(rnp.ffi, "keyid", keyids[0], &key));
assert_non_null(key);
char *alg = NULL;
// confirm that this key is indeed RSA first
assert_rnp_success(rnp_key_get_alg(key, &alg));
assert_int_equal(strcmp(alg, "RSA"), 0);
rnp_buffer_destroy(alg);
// confirm the secret MPIs are NULL
assert_true(mpi_empty(key->sec->material().rsa.d));
assert_true(mpi_empty(key->sec->material().rsa.p));
assert_true(mpi_empty(key->sec->material().rsa.q));
assert_true(mpi_empty(key->sec->material().rsa.u));
// try to unlock with a failing password provider
provider.callback = failing_password_callback;
provider.userdata = NULL;
assert_false(key->sec->unlock(provider));
bool locked = false;
assert_rnp_success(rnp_key_is_locked(key, &locked));
assert_true(locked);
// try to unlock with an incorrect password
provider.callback = string_copy_password_callback;
provider.userdata = (void *) "badpass";
assert_false(key->sec->unlock(provider));
assert_rnp_success(rnp_key_is_locked(key, &locked));
assert_true(locked);
// unlock the signing key
provider.callback = string_copy_password_callback;
provider.userdata = (void *) "password";
assert_true(key->sec->unlock(provider));
assert_rnp_success(rnp_key_is_locked(key, &locked));
assert_false(locked);
// confirm the secret MPIs are now filled in
assert_false(mpi_empty(key->sec->material().rsa.d));
assert_false(mpi_empty(key->sec->material().rsa.p));
assert_false(mpi_empty(key->sec->material().rsa.q));
assert_false(mpi_empty(key->sec->material().rsa.u));
// now the signing key is unlocked, confirm that no password is required for signing
assert_rnp_success(
rnp_ffi_set_pass_provider(rnp.ffi, ffi_asserting_password_provider, NULL));
cfg.clear();
cfg.load_defaults();
cfg.set_bool(CFG_SIGN_NEEDED, true);
cfg.set_str(CFG_HASH, "SHA256");
cfg.set_int(CFG_ZLEVEL, 0);
cfg.set_str(CFG_INFILE, "dummyfile.dat");
cfg.set_str(CFG_OUTFILE, "dummyfile.dat.pgp");
cfg.add_str(CFG_SIGNERS, keyids[0]);
assert_true(cli_rnp_protect_file(&rnp));
cfg.clear();
// verify
cfg.load_defaults();
cfg.set_bool(CFG_OVERWRITE, true);
cfg.set_str(CFG_INFILE, "dummyfile.dat.pgp");
cfg.set_str(CFG_OUTFILE, "dummyfile.verify");
assert_true(cli_rnp_process_file(&rnp));
// verify (negative)
std::fstream verf("dummyfile.dat.pgp",
std::ios_base::binary | std::ios_base::out | std::ios_base::in);
verf.seekg(-3, std::ios::end);
char bt = verf.peek() ^ 0xff;
verf.seekp(-3, std::ios::end);
verf.write(&bt, 1);
verf.close();
assert_false(cli_rnp_process_file(&rnp));
// lock the signing key
assert_rnp_success(rnp_key_lock(key));
assert_rnp_success(rnp_key_is_locked(key, &locked));
assert_true(locked);
// sign, with no password (should now fail)
assert_rnp_success(
rnp_ffi_set_pass_provider(rnp.ffi, ffi_failing_password_provider, NULL));
cfg.clear();
cfg.load_defaults();
cfg.set_bool(CFG_SIGN_NEEDED, true);
cfg.set_bool(CFG_OVERWRITE, true);
cfg.set_str(CFG_HASH, "SHA1");
cfg.set_int(CFG_ZLEVEL, 0);
cfg.set_str(CFG_INFILE, "dummyfile.dat");
cfg.set_str(CFG_OUTFILE, "dummyfile.dat.pgp");
cfg.add_str(CFG_SIGNERS, keyids[0]);
assert_false(cli_rnp_protect_file(&rnp));
cfg.clear();
// encrypt
cfg.load_defaults();
cfg.set_bool(CFG_ENCRYPT_PK, true);
cfg.set_int(CFG_ZLEVEL, 0);
cfg.set_str(CFG_INFILE, "dummyfile.dat");
cfg.set_str(CFG_OUTFILE, "dummyfile.dat.pgp");
cfg.set_bool(CFG_OVERWRITE, true);
cfg.set_str(CFG_CIPHER, "AES256");
cfg.add_str(CFG_RECIPIENTS, keyids[1]);
assert_true(cli_rnp_protect_file(&rnp));
cfg.clear();
// try decrypting with a failing password provider (should fail)
cfg.load_defaults();
cfg.set_bool(CFG_OVERWRITE, true);
cfg.set_str(CFG_INFILE, "dummyfile.dat.pgp");
cfg.set_str(CFG_OUTFILE, "dummyfile.decrypt");
assert_false(cli_rnp_process_file(&rnp));
// grab the encrypting key to unlock
rnp_key_handle_t subkey = NULL;
assert_rnp_success(rnp_locate_key(rnp.ffi, "keyid", keyids[1], &subkey));
assert_non_null(subkey);
// unlock the encrypting key
assert_rnp_success(rnp_key_unlock(subkey, "password"));
assert_rnp_success(rnp_key_is_locked(subkey, &locked));
assert_false(locked);
// decrypt, with no password
assert_true(cli_rnp_process_file(&rnp));
std::string decrypt = file_to_str("dummyfile.decrypt");
assert_true(decrypt == data);
// lock the encrypting key
assert_rnp_success(rnp_key_lock(subkey));
assert_rnp_success(rnp_key_is_locked(subkey, &locked));
assert_true(locked);
// decrypt, with no password (should now fail)
assert_false(cli_rnp_process_file(&rnp));
// cleanup
assert_rnp_success(rnp_key_handle_destroy(key));
assert_rnp_success(rnp_key_handle_destroy(subkey));
rnp.end();
assert_int_equal(rnp_unlink("dummyfile.dat"), 0);
}