/* * Copyright (c) 2018-2022, [Ribose Inc](https://www.ribose.com). * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "rnp_tests.h" #include "support.h" #include "rnp.h" #include "crypto/hash.hpp" #include "crypto/signatures.h" #include "pgp-key.h" #include #include "rnp.h" #include #include #include #include #include #include #include #include #include "time-utils.h" static bool stream_hash_file(rnp::Hash &hash, const char *path) { pgp_source_t src; if (init_file_src(&src, path)) { return false; } bool res = false; do { uint8_t readbuf[1024]; size_t read = 0; if (!src_read(&src, readbuf, sizeof(readbuf), &read)) { goto finish; } else if (read == 0) { break; } hash.add(readbuf, read); } while (1); res = true; finish: src_close(&src); return res; } TEST_F(rnp_tests, test_stream_memory) { const char *data = "Sample data to test memory streams"; size_t datalen; pgp_dest_t memdst; void * mown; void * mcpy; datalen = strlen(data) + 1; /* populate memory dst and own inner data */ assert_rnp_success(init_mem_dest(&memdst, NULL, 0)); assert_rnp_success(memdst.werr); dst_write(&memdst, data, datalen); assert_rnp_success(memdst.werr); assert_int_equal(memdst.writeb, datalen); assert_non_null(mcpy = mem_dest_get_memory(&memdst)); assert_false(memcmp(mcpy, data, datalen)); assert_non_null(mown = mem_dest_own_memory(&memdst)); assert_false(memcmp(mown, data, datalen)); dst_close(&memdst, true); /* make sure we own data after close */ assert_false(memcmp(mown, data, datalen)); free(mown); /* make sure init_mem_src fails with NULL parameter */ pgp_source_t memsrc; assert_rnp_failure(init_mem_src(&memsrc, NULL, 12, false)); assert_rnp_failure(init_mem_src(&memsrc, NULL, 12, true)); } TEST_F(rnp_tests, test_stream_memory_discard) { pgp_dest_t memdst = {}; char mem[32]; const char * hexes = "123456789ABCDEF"; const size_t hexes_len = 15; /* init mem dst and write some data */ assert_rnp_success(init_mem_dest(&memdst, mem, sizeof(mem))); dst_write(&memdst, "Hello", 5); assert_int_equal(memdst.writeb, 5); dst_write(&memdst, ", ", 2); assert_int_equal(memdst.writeb, 7); dst_write(&memdst, "world!\n", 7); assert_int_equal(memdst.writeb, 14); /* Now discard overflowing data and attempt to overflow it */ mem_dest_discard_overflow(&memdst, true); dst_write(&memdst, "Hello, world!\n", 14); assert_int_equal(memdst.writeb, 28); assert_int_equal(memdst.werr, RNP_SUCCESS); dst_write(&memdst, hexes, hexes_len); /* While extra data is discarded, writeb is still incremented by hexes_len */ assert_int_equal(memdst.writeb, 43); assert_int_equal(memdst.werr, RNP_SUCCESS); assert_int_equal(memcmp(mem, "Hello, world!\nHello, world!\n1234", 32), 0); dst_write(&memdst, hexes, hexes_len); assert_int_equal(memdst.writeb, 58); assert_int_equal(memdst.werr, RNP_SUCCESS); assert_int_equal(memcmp(mem, "Hello, world!\nHello, world!\n1234", 32), 0); /* Now make sure that error is generated */ mem_dest_discard_overflow(&memdst, false); dst_write(&memdst, hexes, hexes_len); assert_int_equal(memdst.writeb, 58); assert_int_not_equal(memdst.werr, RNP_SUCCESS); dst_close(&memdst, true); assert_int_equal(memcmp(mem, "Hello, world!\nHello, world!\n1234", 32), 0); /* Now do tests with dynamic memory allocation */ const size_t bytes = 12345; assert_rnp_success(init_mem_dest(&memdst, NULL, bytes)); for (size_t i = 0; i < bytes / hexes_len; i++) { dst_write(&memdst, hexes, hexes_len); assert_int_equal(memdst.writeb, (i + 1) * hexes_len); assert_int_equal(memdst.werr, RNP_SUCCESS); } mem_dest_discard_overflow(&memdst, true); dst_write(&memdst, hexes, hexes_len); assert_int_equal(memdst.writeb, bytes - bytes % hexes_len + hexes_len); assert_int_equal(memdst.werr, RNP_SUCCESS); mem_dest_discard_overflow(&memdst, false); dst_write(&memdst, hexes, hexes_len); assert_int_equal(memdst.writeb, bytes - bytes % hexes_len + hexes_len); assert_int_not_equal(memdst.werr, RNP_SUCCESS); dst_write(&memdst, hexes, hexes_len); assert_int_equal(memdst.writeb, bytes - bytes % hexes_len + hexes_len); assert_int_not_equal(memdst.werr, RNP_SUCCESS); dst_close(&memdst, true); } static void copy_tmp_path(char *buf, size_t buflen, pgp_dest_t *dst) { typedef struct pgp_dest_file_param_t { int fd; int errcode; bool overwrite; std::string path; } pgp_dest_file_param_t; pgp_dest_file_param_t *param = (pgp_dest_file_param_t *) dst->param; strncpy(buf, param->path.c_str(), buflen); } TEST_F(rnp_tests, test_stream_file) { const char * filename = "dummyfile.dat"; const char * dirname = "dummydir"; const char * file2name = "dummydir/dummyfile.dat"; const char * filedata = "dummy message to be stored in the file"; const int iterations = 10000; const int filedatalen = strlen(filedata); char tmpname[128] = {0}; uint8_t tmpbuf[1024] = {0}; pgp_dest_t dst = {}; pgp_source_t src = {}; /* try to read non-existing file */ assert_rnp_failure(init_file_src(&src, filename)); assert_rnp_failure(init_file_src(&src, dirname)); /* create dir */ assert_int_equal(RNP_MKDIR(dirname, S_IRWXU), 0); /* attempt to read or create file in place of directory */ assert_rnp_failure(init_file_src(&src, dirname)); assert_rnp_failure(init_file_dest(&dst, dirname, false)); /* with overwrite flag it must succeed, then delete it */ assert_rnp_success(init_file_dest(&dst, dirname, true)); assert_int_equal(file_size(dirname), 0); dst_close(&dst, true); /* create dir back */ assert_int_equal(RNP_MKDIR(dirname, S_IRWXU), 0); /* write some data to the file and the discard it */ assert_rnp_success(init_file_dest(&dst, filename, false)); dst_write(&dst, filedata, filedatalen); assert_int_not_equal(file_size(filename), -1); dst_close(&dst, true); assert_int_equal(file_size(filename), -1); /* write some data to the file and make sure it is written */ assert_rnp_success(init_file_dest(&dst, filename, false)); dst_write(&dst, filedata, filedatalen); assert_int_not_equal(file_size(filename), -1); dst_close(&dst, false); assert_int_equal(file_size(filename), filedatalen); /* attempt to create file over existing without overwrite flag */ assert_rnp_failure(init_file_dest(&dst, filename, false)); assert_int_equal(file_size(filename), filedatalen); /* overwrite file - it should be truncated, then write bunch of bytes */ assert_rnp_success(init_file_dest(&dst, filename, true)); assert_int_equal(file_size(filename), 0); for (int i = 0; i < iterations; i++) { dst_write(&dst, filedata, filedatalen); } /* and some smaller writes */ for (int i = 0; i < 5 * iterations; i++) { dst_write(&dst, "zzz", 3); } dst_close(&dst, false); assert_int_equal(file_size(filename), iterations * (filedatalen + 15)); /* read file back, checking the contents */ assert_rnp_success(init_file_src(&src, filename)); for (int i = 0; i < iterations; i++) { size_t read = 0; assert_true(src_read(&src, tmpbuf, filedatalen, &read)); assert_int_equal(read, filedatalen); assert_int_equal(memcmp(tmpbuf, filedata, filedatalen), 0); } for (int i = 0; i < 5 * iterations; i++) { size_t read = 0; assert_true(src_read(&src, tmpbuf, 3, &read)); assert_int_equal(read, 3); assert_int_equal(memcmp(tmpbuf, "zzz", 3), 0); } src_close(&src); /* overwrite and discard - file should be deleted */ assert_rnp_success(init_file_dest(&dst, filename, true)); assert_int_equal(file_size(filename), 0); for (int i = 0; i < iterations; i++) { dst_write(&dst, "hello", 6); } dst_close(&dst, true); assert_int_equal(file_size(filename), -1); /* create and populate file in subfolder */ assert_rnp_success(init_file_dest(&dst, file2name, true)); assert_int_equal(file_size(file2name), 0); for (int i = 0; i < iterations; i++) { dst_write(&dst, filedata, filedatalen); } dst_close(&dst, false); assert_int_equal(file_size(file2name), filedatalen * iterations); assert_int_equal(rnp_unlink(file2name), 0); /* create and populate file stream, using tmp name before closing */ assert_rnp_success(init_tmpfile_dest(&dst, filename, false)); copy_tmp_path(tmpname, sizeof(tmpname), &dst); assert_int_equal(file_size(tmpname), 0); assert_int_equal(file_size(filename), -1); for (int i = 0; i < iterations; i++) { dst_write(&dst, filedata, filedatalen); } dst_close(&dst, false); assert_int_equal(file_size(tmpname), -1); assert_int_equal(file_size(filename), filedatalen * iterations); /* create and then discard file stream, using tmp name before closing */ assert_rnp_success(init_tmpfile_dest(&dst, filename, true)); copy_tmp_path(tmpname, sizeof(tmpname), &dst); assert_int_equal(file_size(tmpname), 0); dst_write(&dst, filedata, filedatalen); /* make sure file was not overwritten */ assert_int_equal(file_size(filename), filedatalen * iterations); dst_close(&dst, true); assert_int_equal(file_size(tmpname), -1); assert_int_equal(file_size(filename), filedatalen * iterations); /* create and then close file stream, using tmp name before closing. No overwrite. */ assert_rnp_success(init_tmpfile_dest(&dst, filename, false)); copy_tmp_path(tmpname, sizeof(tmpname), &dst); assert_int_equal(file_size(tmpname), 0); dst_write(&dst, filedata, filedatalen); /* make sure file was not overwritten */ assert_int_equal(file_size(filename), filedatalen * iterations); assert_rnp_failure(dst_finish(&dst)); dst_close(&dst, false); assert_int_equal(file_size(tmpname), filedatalen); assert_int_equal(file_size(filename), filedatalen * iterations); assert_int_equal(rnp_unlink(tmpname), 0); /* create and then close file stream, using tmp name before closing. Overwrite existing. */ assert_rnp_success(init_tmpfile_dest(&dst, filename, true)); copy_tmp_path(tmpname, sizeof(tmpname), &dst); assert_int_equal(file_size(tmpname), 0); dst_write(&dst, filedata, filedatalen); /* make sure file was not overwritten yet */ assert_int_equal(file_size(filename), filedatalen * iterations); assert_rnp_success(dst_finish(&dst)); dst_close(&dst, false); assert_int_equal(file_size(tmpname), -1); assert_int_equal(file_size(filename), filedatalen); /* make sure we can overwrite directory */ assert_rnp_success(init_tmpfile_dest(&dst, dirname, true)); copy_tmp_path(tmpname, sizeof(tmpname), &dst); assert_int_equal(file_size(tmpname), 0); dst_write(&dst, filedata, filedatalen); /* make sure file was not overwritten yet */ assert_int_equal(file_size(dirname), -1); assert_rnp_success(dst_finish(&dst)); dst_close(&dst, false); assert_int_equal(file_size(tmpname), -1); assert_int_equal(file_size(dirname), filedatalen); /* cleanup */ assert_int_equal(rnp_unlink(dirname), 0); } TEST_F(rnp_tests, test_stream_signatures) { rnp_key_store_t *pubring; rnp_key_store_t *secring; pgp_signature_t sig; pgp_source_t sigsrc; pgp_key_t * key = NULL; /* load keys */ pubring = new rnp_key_store_t( PGP_KEY_STORE_GPG, "data/test_stream_signatures/pub.asc", global_ctx); assert_true(rnp_key_store_load_from_path(pubring, NULL)); /* load signature */ assert_rnp_success(init_file_src(&sigsrc, "data/test_stream_signatures/source.txt.sig")); assert_rnp_success(sig.parse(sigsrc)); src_close(&sigsrc); /* hash signed file */ pgp_hash_alg_t halg = sig.halg; auto hash_orig = rnp::Hash::create(halg); assert_true(stream_hash_file(*hash_orig, "data/test_stream_signatures/source.txt")); /* hash forged file */ auto hash_forged = rnp::Hash::create(halg); assert_true( stream_hash_file(*hash_forged, "data/test_stream_signatures/source_forged.txt")); /* find signing key */ assert_non_null(key = rnp_key_store_get_signer_key(pubring, &sig)); /* validate signature and fields */ auto hash = hash_orig->clone(); assert_int_equal(sig.creation(), 1522241943); assert_rnp_success(signature_validate(sig, key->material(), *hash, global_ctx)); /* check forged file */ hash = hash_forged->clone(); assert_rnp_failure(signature_validate(sig, key->material(), *hash, global_ctx)); /* now let's create signature and sign file */ /* load secret key */ secring = new rnp_key_store_t( PGP_KEY_STORE_GPG, "data/test_stream_signatures/sec.asc", global_ctx); assert_true(rnp_key_store_load_from_path(secring, NULL)); assert_non_null(key = rnp_key_store_get_signer_key(secring, &sig)); assert_true(key->is_secret()); /* fill signature */ uint32_t create = time(NULL); uint32_t expire = 123456; sig = {}; sig.version = PGP_V4; sig.halg = halg; sig.palg = key->alg(); sig.set_type(PGP_SIG_BINARY); sig.set_keyfp(key->fp()); sig.set_keyid(key->keyid()); sig.set_creation(create); sig.set_expiration(expire); /* make use of add_notation() to cover it */ try { std::vector value; value.resize(66000); sig.add_notation("dummy@example.com", value, false, true); assert_true(false); } catch (const rnp::rnp_exception &e) { assert_int_equal(e.code(), RNP_ERROR_BAD_PARAMETERS); } sig.add_notation("dummy@example.com", "make codecov happy!", false); sig.fill_hashed_data(); /* try to sign without decrypting of the secret key */ hash = hash_orig->clone(); assert_throw(signature_calculate(sig, key->material(), *hash, global_ctx)); /* now unlock the key and sign */ pgp_password_provider_t pswd_prov(rnp_password_provider_string, (void *) "password"); assert_true(key->unlock(pswd_prov)); hash = hash_orig->clone(); signature_calculate(sig, key->material(), *hash, global_ctx); /* now verify signature */ hash = hash_orig->clone(); /* validate signature and fields */ assert_int_equal(sig.creation(), create); assert_int_equal(sig.expiration(), expire); assert_true(sig.has_subpkt(PGP_SIG_SUBPKT_ISSUER_FPR)); assert_true(sig.keyfp() == key->fp()); assert_rnp_success(signature_validate(sig, key->material(), *hash, global_ctx)); /* cleanup */ delete pubring; delete secring; } TEST_F(rnp_tests, test_stream_signatures_revoked_key) { pgp_signature_t sig = {}; pgp_source_t sigsrc = {0}; /* load signature */ assert_rnp_success( init_file_src(&sigsrc, "data/test_stream_signatures/revoked-key-sig.gpg")); assert_rnp_success(sig.parse(sigsrc)); src_close(&sigsrc); /* check revocation */ assert_int_equal(sig.revocation_code(), PGP_REVOCATION_RETIRED); assert_string_equal(sig.revocation_reason().c_str(), "For testing!"); } TEST_F(rnp_tests, test_stream_key_load) { pgp_source_t keysrc = {0}; pgp_dest_t keydst = {0}; pgp_key_sequence_t keyseq; pgp_key_id_t keyid = {}; pgp_fingerprint_t keyfp; pgp_transferable_key_t * key = NULL; pgp_transferable_subkey_t *skey = NULL; /* public keyring, read-save-read-save armored-read */ assert_rnp_success(init_file_src(&keysrc, "data/keyrings/1/pubring.gpg")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_true(keyseq.keys.size() > 1); src_close(&keysrc); assert_rnp_success(init_file_dest(&keydst, "keyout.gpg", true)); assert_true(write_transferable_keys(keyseq, &keydst, false)); dst_close(&keydst, false); assert_rnp_success(init_file_src(&keysrc, "keyout.gpg")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); src_close(&keysrc); assert_rnp_success(init_file_dest(&keydst, "keyout.asc", true)); assert_true(write_transferable_keys(keyseq, &keydst, true)); dst_close(&keydst, false); assert_rnp_success(init_file_src(&keysrc, "keyout.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); src_close(&keysrc); /* secret keyring */ assert_rnp_success(init_file_src(&keysrc, "data/keyrings/1/secring.gpg")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_true(keyseq.keys.size() > 1); src_close(&keysrc); assert_rnp_success(init_file_dest(&keydst, "keyout-sec.gpg", true)); assert_true(write_transferable_keys(keyseq, &keydst, false)); dst_close(&keydst, false); assert_rnp_success(init_file_src(&keysrc, "keyout-sec.gpg")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); src_close(&keysrc); assert_rnp_success(init_file_dest(&keydst, "keyout-sec.asc", true)); assert_true(write_transferable_keys(keyseq, &keydst, true)); dst_close(&keydst, false); assert_rnp_success(init_file_src(&keysrc, "keyout-sec.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); src_close(&keysrc); /* armored v3 public key */ assert_rnp_success(init_file_src(&keysrc, "data/keyrings/4/rsav3-p.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(pgp_keyid(keyid, key->key)); assert_true(cmp_keyid(keyid, "7D0BC10E933404C9")); assert_false(cmp_keyid(keyid, "1D0BC10E933404C9")); src_close(&keysrc); /* armored v3 secret key */ assert_rnp_success(init_file_src(&keysrc, "data/keyrings/4/rsav3-s.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(pgp_keyid(keyid, key->key)); assert_true(cmp_keyid(keyid, "7D0BC10E933404C9")); src_close(&keysrc); /* rsa/rsa public key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/rsa-rsa-pub.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(pgp_fingerprint(keyfp, key->key)); assert_true(cmp_keyfp(keyfp, "6BC04A5A3DDB35766B9A40D82FB9179118898E8B")); assert_rnp_success(pgp_keyid(keyid, key->key)); assert_true(cmp_keyid(keyid, "2FB9179118898E8B")); assert_int_equal(key->subkeys.size(), 1); assert_non_null(key->subkeys[0].subkey.hashed_data); src_close(&keysrc); /* rsa/rsa secret key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/rsa-rsa-sec.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(pgp_fingerprint(keyfp, key->key)); assert_true(cmp_keyfp(keyfp, "6BC04A5A3DDB35766B9A40D82FB9179118898E8B")); assert_rnp_success(pgp_keyid(keyid, key->key)); assert_true(cmp_keyid(keyid, "2FB9179118898E8B")); assert_int_equal(key->subkeys.size(), 1); assert_non_null(key->subkeys[0].subkey.hashed_data); src_close(&keysrc); /* dsa/el-gamal public key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/dsa-eg-pub.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(pgp_fingerprint(keyfp, key->key)); assert_true(cmp_keyfp(keyfp, "091C44CE9CFBC3FF7EC7A64DC8A10A7D78273E10")); assert_int_equal(key->subkeys.size(), 1); assert_non_null(skey = &key->subkeys.front()); assert_rnp_success(pgp_keyid(keyid, skey->subkey)); assert_true(cmp_keyid(keyid, "02A5715C3537717E")); src_close(&keysrc); /* dsa/el-gamal secret key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/dsa-eg-sec.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_int_equal(key->subkeys.size(), 1); assert_non_null(key->subkeys[0].subkey.hashed_data); src_close(&keysrc); /* curve 25519 ecc public key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-25519-pub.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(pgp_fingerprint(keyfp, key->key)); assert_true(cmp_keyfp(keyfp, "21FC68274AAE3B5DE39A4277CC786278981B0728")); src_close(&keysrc); /* curve 25519 ecc secret key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-25519-sec.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_true(key->subkeys.empty()); src_close(&keysrc); /* eddsa/x25519 ecc public key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-x25519-pub.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(pgp_fingerprint(keyfp, key->key)); assert_true(cmp_keyfp(keyfp, "4C9738A6F2BE4E1A796C9B7B941822A0FC1B30A5")); assert_int_equal(key->subkeys.size(), 1); assert_non_null(skey = &key->subkeys.front()); assert_rnp_success(pgp_keyid(keyid, skey->subkey)); assert_true(cmp_keyid(keyid, "C711187E594376AF")); src_close(&keysrc); /* eddsa/x25519 ecc secret key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-x25519-sec.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_int_equal(key->subkeys.size(), 1); assert_non_null(key->subkeys[0].subkey.hashed_data); src_close(&keysrc); /* p-256 ecc public key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-p256-pub.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(pgp_fingerprint(keyfp, key->key)); assert_true(cmp_keyfp(keyfp, "B54FDEBBB673423A5D0AA54423674F21B2441527")); assert_non_null(skey = &key->subkeys.front()); assert_rnp_success(pgp_keyid(keyid, skey->subkey)); assert_true(cmp_keyid(keyid, "37E285E9E9851491")); src_close(&keysrc); /* p-256 ecc secret key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-p256-sec.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_int_equal(key->subkeys.size(), 1); assert_non_null(key->subkeys[0].subkey.hashed_data); src_close(&keysrc); /* p-384 ecc public key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-p384-pub.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(pgp_fingerprint(keyfp, key->key)); assert_true(cmp_keyfp(keyfp, "AB25CBA042DD924C3ACC3ED3242A3AA5EA85F44A")); assert_non_null(skey = &key->subkeys.front()); assert_rnp_success(pgp_keyid(keyid, skey->subkey)); assert_true(cmp_keyid(keyid, "E210E3D554A4FAD9")); src_close(&keysrc); /* p-384 ecc secret key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-p384-sec.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_int_equal(key->subkeys.size(), 1); assert_non_null(key->subkeys[0].subkey.hashed_data); src_close(&keysrc); /* p-521 ecc public key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-p521-pub.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(pgp_fingerprint(keyfp, key->key)); assert_true(cmp_keyfp(keyfp, "4FB39FF6FA4857A4BD7EF5B42092CA8324263B6A")); assert_non_null(skey = &key->subkeys.front()); assert_rnp_success(pgp_keyid(keyid, skey->subkey)); assert_true(cmp_keyid(keyid, "9853DF2F6D297442")); src_close(&keysrc); /* p-521 ecc secret key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-p521-sec.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_int_equal(key->subkeys.size(), 1); assert_non_null(key->subkeys[0].subkey.hashed_data); src_close(&keysrc); /* Brainpool P256 ecc public key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-bp256-pub.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(pgp_fingerprint(keyfp, key->key)); assert_true(cmp_keyfp(keyfp, "0633C5F72A198F51E650E4ABD0C8A3DAF9E0634A")); assert_non_null(skey = &key->subkeys.front()); assert_rnp_success(pgp_keyid(keyid, skey->subkey)); assert_true(cmp_keyid(keyid, "2EDABB94D3055F76")); src_close(&keysrc); /* Brainpool P256 ecc secret key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-bp256-sec.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_int_equal(key->subkeys.size(), 1); assert_non_null(key->subkeys[0].subkey.hashed_data); src_close(&keysrc); /* Brainpool P384 ecc public key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-bp384-pub.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(pgp_fingerprint(keyfp, key->key)); assert_true(cmp_keyfp(keyfp, "5B8A254C823CED98DECD10ED6CF2DCE85599ADA2")); assert_non_null(skey = &key->subkeys.front()); assert_rnp_success(pgp_keyid(keyid, skey->subkey)); assert_true(cmp_keyid(keyid, "CFF1BB6F16D28191")); src_close(&keysrc); /* Brainpool P384 ecc secret key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-bp384-sec.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_int_equal(key->subkeys.size(), 1); assert_non_null(key->subkeys[0].subkey.hashed_data); src_close(&keysrc); /* Brainpool P512 ecc public key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-bp512-pub.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(pgp_fingerprint(keyfp, key->key)); assert_true(cmp_keyfp(keyfp, "4C59AB9272AA6A1F60B85BD0AA5C58D14F7B8F48")); assert_non_null(skey = &key->subkeys.front()); assert_rnp_success(pgp_keyid(keyid, skey->subkey)); assert_true(cmp_keyid(keyid, "20CDAA1482BA79CE")); src_close(&keysrc); /* Brainpool P512 ecc secret key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-bp512-sec.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_int_equal(key->subkeys.size(), 1); assert_non_null(key->subkeys[0].subkey.hashed_data); src_close(&keysrc); /* secp256k1 ecc public key, not supported now */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-p256k1-pub.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(pgp_fingerprint(keyfp, key->key)); assert_true(cmp_keyfp(keyfp, "81F772B57D4EBFE7000A66233EA5BB6F9692C1A0")); assert_non_null(skey = &key->subkeys.front()); assert_rnp_success(pgp_keyid(keyid, skey->subkey)); assert_true(cmp_keyid(keyid, "7635401F90D3E533")); src_close(&keysrc); /* secp256k1 ecc secret key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-p256k1-sec.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_int_equal(keyseq.keys.size(), 1); assert_non_null(key = &keyseq.keys.front()); assert_int_equal(key->subkeys.size(), 1); assert_non_null(key->subkeys[0].subkey.hashed_data); src_close(&keysrc); } static void buggy_key_load_single(const void *keydata, size_t keylen) { pgp_source_t memsrc = {0}; pgp_key_sequence_t keyseq; size_t partlen; uint8_t * dataptr; /* try truncated load */ for (partlen = 1; partlen < keylen; partlen += 15) { assert_rnp_success(init_mem_src(&memsrc, keydata, partlen, false)); if (!process_pgp_keys(memsrc, keyseq, false)) { /* it may succeed if we accidentally hit some packet boundary */ assert_false(keyseq.keys.empty()); } else { assert_true(keyseq.keys.empty()); } src_close(&memsrc); } /* try modified load */ dataptr = (uint8_t *) keydata; for (partlen = 1; partlen < keylen; partlen++) { dataptr[partlen] ^= 0xff; assert_rnp_success(init_mem_src(&memsrc, keydata, keylen, false)); if (!process_pgp_keys(memsrc, keyseq, false)) { /* it may succeed if we accidentally hit some packet boundary */ assert_false(keyseq.keys.empty()); } else { assert_true(keyseq.keys.empty()); } src_close(&memsrc); dataptr[partlen] ^= 0xff; } } /* check for memory leaks during buggy key loads */ TEST_F(rnp_tests, test_stream_key_load_errors) { pgp_source_t fsrc = {0}; pgp_source_t armorsrc = {0}; pgp_source_t memsrc = {0}; const char *key_files[] = {"data/keyrings/4/rsav3-p.asc", "data/keyrings/4/rsav3-s.asc", "data/keyrings/1/pubring.gpg", "data/keyrings/1/secring.gpg", "data/test_stream_key_load/dsa-eg-pub.asc", "data/test_stream_key_load/dsa-eg-sec.asc", "data/test_stream_key_load/ecc-25519-pub.asc", "data/test_stream_key_load/ecc-25519-sec.asc", "data/test_stream_key_load/ecc-x25519-pub.asc", "data/test_stream_key_load/ecc-x25519-sec.asc", "data/test_stream_key_load/ecc-p256-pub.asc", "data/test_stream_key_load/ecc-p256-sec.asc", "data/test_stream_key_load/ecc-p384-pub.asc", "data/test_stream_key_load/ecc-p384-sec.asc", "data/test_stream_key_load/ecc-p521-pub.asc", "data/test_stream_key_load/ecc-p521-sec.asc", "data/test_stream_key_load/ecc-bp256-pub.asc", "data/test_stream_key_load/ecc-bp256-sec.asc", "data/test_stream_key_load/ecc-bp384-pub.asc", "data/test_stream_key_load/ecc-bp384-sec.asc", "data/test_stream_key_load/ecc-bp512-pub.asc", "data/test_stream_key_load/ecc-bp512-sec.asc", "data/test_stream_key_load/ecc-p256k1-pub.asc", "data/test_stream_key_load/ecc-p256k1-sec.asc"}; for (size_t i = 0; i < sizeof(key_files) / sizeof(char *); i++) { assert_rnp_success(init_file_src(&fsrc, key_files[i])); if (is_armored_source(&fsrc)) { assert_rnp_success(init_armored_src(&armorsrc, &fsrc)); assert_rnp_success(read_mem_src(&memsrc, &armorsrc)); src_close(&armorsrc); } else { assert_rnp_success(read_mem_src(&memsrc, &fsrc)); } src_close(&fsrc); buggy_key_load_single(mem_src_get_memory(&memsrc), memsrc.size); src_close(&memsrc); } } TEST_F(rnp_tests, test_stream_key_decrypt) { pgp_source_t keysrc = {0}; pgp_key_sequence_t keyseq; pgp_transferable_key_t * key = NULL; pgp_transferable_subkey_t *subkey = NULL; /* load and decrypt secret keyring */ assert_rnp_success(init_file_src(&keysrc, "data/keyrings/1/secring.gpg")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); for (auto &key : keyseq.keys) { assert_rnp_failure(decrypt_secret_key(&key.key, "passw0rd")); assert_rnp_success(decrypt_secret_key(&key.key, "password")); for (auto &subkey : key.subkeys) { assert_rnp_failure(decrypt_secret_key(&subkey.subkey, "passw0rd")); assert_rnp_success(decrypt_secret_key(&subkey.subkey, "password")); } } src_close(&keysrc); /* armored v3 secret key */ assert_rnp_success(init_file_src(&keysrc, "data/keyrings/4/rsav3-s.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_non_null(key = &keyseq.keys.front()); assert_rnp_failure(decrypt_secret_key(&key->key, "passw0rd")); #if defined(ENABLE_IDEA) assert_rnp_success(decrypt_secret_key(&key->key, "password")); #else assert_rnp_failure(decrypt_secret_key(&key->key, "password")); #endif src_close(&keysrc); /* rsa/rsa secret key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/rsa-rsa-sec.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(decrypt_secret_key(&key->key, "password")); assert_non_null(subkey = &key->subkeys.front()); assert_rnp_success(decrypt_secret_key(&subkey->subkey, "password")); src_close(&keysrc); /* dsa/el-gamal secret key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/dsa-eg-sec.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(decrypt_secret_key(&key->key, "password")); assert_non_null(subkey = &key->subkeys.front()); assert_rnp_success(decrypt_secret_key(&subkey->subkey, "password")); src_close(&keysrc); /* curve 25519 eddsa ecc secret key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-25519-sec.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(decrypt_secret_key(&key->key, "password")); src_close(&keysrc); /* x25519 ecc secret key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-x25519-sec.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(decrypt_secret_key(&key->key, "password")); assert_non_null(subkey = &key->subkeys.front()); assert_rnp_success(decrypt_secret_key(&subkey->subkey, "password")); src_close(&keysrc); /* p-256 ecc secret key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-p256-sec.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(decrypt_secret_key(&key->key, "password")); assert_non_null(subkey = &key->subkeys.front()); assert_rnp_success(decrypt_secret_key(&subkey->subkey, "password")); src_close(&keysrc); /* p-384 ecc secret key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-p384-sec.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(decrypt_secret_key(&key->key, "password")); assert_non_null(subkey = &key->subkeys.front()); assert_rnp_success(decrypt_secret_key(&subkey->subkey, "password")); src_close(&keysrc); /* p-521 ecc secret key */ assert_rnp_success(init_file_src(&keysrc, "data/test_stream_key_load/ecc-p521-sec.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); assert_non_null(key = &keyseq.keys.front()); assert_rnp_success(decrypt_secret_key(&key->key, "password")); assert_non_null(subkey = &key->subkeys.front()); assert_rnp_success(decrypt_secret_key(&subkey->subkey, "password")); src_close(&keysrc); } TEST_F(rnp_tests, test_stream_key_encrypt) { pgp_source_t keysrc = {0}; pgp_dest_t keydst = {0}; uint8_t keybuf[16384]; size_t keylen; pgp_key_sequence_t keyseq; pgp_key_sequence_t keyseq2; /* load and decrypt secret keyring, then re-encrypt and reload keys */ assert_rnp_success(init_file_src(&keysrc, "data/keyrings/1/secring.gpg")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); src_close(&keysrc); for (auto &key : keyseq.keys) { assert_rnp_success(decrypt_secret_key(&key.key, "password")); for (auto &subkey : key.subkeys) { assert_rnp_success(decrypt_secret_key(&subkey.subkey, "password")); } /* change password and encryption algorithm */ key.key.sec_protection.symm_alg = PGP_SA_CAMELLIA_192; assert_rnp_success(encrypt_secret_key(&key.key, "passw0rd", global_ctx.rng)); for (auto &subkey : key.subkeys) { subkey.subkey.sec_protection.symm_alg = PGP_SA_CAMELLIA_256; assert_rnp_success(encrypt_secret_key(&subkey.subkey, "passw0rd", global_ctx.rng)); } /* write changed key */ assert_rnp_success(init_mem_dest(&keydst, keybuf, sizeof(keybuf))); assert_true(write_transferable_key(key, keydst)); keylen = keydst.writeb; dst_close(&keydst, false); /* load and decrypt changed key */ assert_rnp_success(init_mem_src(&keysrc, keybuf, keylen, false)); assert_rnp_success(process_pgp_keys(keysrc, keyseq2, false)); src_close(&keysrc); assert_false(keyseq2.keys.empty()); auto &key2 = keyseq2.keys.front(); assert_int_equal(key2.key.sec_protection.symm_alg, PGP_SA_CAMELLIA_192); assert_rnp_success(decrypt_secret_key(&key2.key, "passw0rd")); for (auto &subkey : key2.subkeys) { assert_int_equal(subkey.subkey.sec_protection.symm_alg, PGP_SA_CAMELLIA_256); assert_rnp_success(decrypt_secret_key(&subkey.subkey, "passw0rd")); } /* write key without the password */ key2.key.sec_protection.s2k.usage = PGP_S2KU_NONE; assert_rnp_success(encrypt_secret_key(&key2.key, NULL, global_ctx.rng)); for (auto &subkey : key2.subkeys) { subkey.subkey.sec_protection.s2k.usage = PGP_S2KU_NONE; assert_rnp_success(encrypt_secret_key(&subkey.subkey, NULL, global_ctx.rng)); } /* write changed key */ assert_rnp_success(init_mem_dest(&keydst, keybuf, sizeof(keybuf))); assert_true(write_transferable_key(key2, keydst)); keylen = keydst.writeb; dst_close(&keydst, false); /* load non-encrypted key */ assert_rnp_success(init_mem_src(&keysrc, keybuf, keylen, false)); assert_rnp_success(process_pgp_keys(keysrc, keyseq2, false)); src_close(&keysrc); assert_false(keyseq2.keys.empty()); auto &key3 = keyseq2.keys.front(); assert_int_equal(key3.key.sec_protection.s2k.usage, PGP_S2KU_NONE); assert_rnp_success(decrypt_secret_key(&key3.key, NULL)); for (auto &subkey : key3.subkeys) { assert_int_equal(subkey.subkey.sec_protection.s2k.usage, PGP_S2KU_NONE); assert_rnp_success(decrypt_secret_key(&subkey.subkey, NULL)); } } } TEST_F(rnp_tests, test_stream_key_signatures) { pgp_source_t keysrc = {0}; pgp_key_sequence_t keyseq; pgp_key_t * pkey = NULL; pgp_signature_info_t sinfo = {}; /* v3 public key */ auto pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, "data/keyrings/4/rsav3-p.asc", global_ctx); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_rnp_success(init_file_src(&keysrc, "data/keyrings/4/rsav3-p.asc")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); src_close(&keysrc); assert_int_equal(keyseq.keys.size(), 1); auto &key = keyseq.keys.front(); auto &uid = key.userids.front(); auto &sig = uid.signatures.front(); assert_non_null(pkey = rnp_key_store_get_signer_key(pubring, &sig)); /* check certification signature */ auto hash = signature_hash_certification(sig, key.key, uid.uid); /* this signature uses MD5 hash after the allowed date */ assert_int_equal(signature_validate(sig, pkey->material(), *hash, global_ctx), RNP_ERROR_SIGNATURE_INVALID); /* 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_rnp_success(signature_validate(sig, pkey->material(), *hash, global_ctx)); /* modify userid and check signature */ uid.uid.uid[2] = '?'; hash = signature_hash_certification(sig, key.key, uid.uid); assert_rnp_failure(signature_validate(sig, pkey->material(), *hash, global_ctx)); /* remove MD5 rule */ assert_true(global_ctx.profile.del_rule(allow_md5)); delete pubring; /* keyring */ 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)); assert_rnp_success(init_file_src(&keysrc, "data/keyrings/1/pubring.gpg")); assert_rnp_success(process_pgp_keys(keysrc, keyseq, false)); src_close(&keysrc); /* check key signatures */ for (auto &keyref : keyseq.keys) { for (auto &uid : keyref.userids) { /* userid certifications */ for (auto &sig : uid.signatures) { assert_non_null(pkey = rnp_key_store_get_signer_key(pubring, &sig)); /* high level interface */ sinfo.sig = &sig; pkey->validate_cert(sinfo, keyref.key, uid.uid, global_ctx); assert_true(sinfo.valid); /* low level check */ auto hash = signature_hash_certification(sig, keyref.key, uid.uid); assert_rnp_success( signature_validate(sig, pkey->material(), *hash, global_ctx)); /* modify userid and check signature */ uid.uid.uid[2] = '?'; pkey->validate_cert(sinfo, keyref.key, uid.uid, global_ctx); assert_false(sinfo.valid); hash = signature_hash_certification(sig, keyref.key, uid.uid); assert_rnp_failure( signature_validate(sig, pkey->material(), *hash, global_ctx)); } } /* subkey binding signatures */ for (auto &subkey : keyref.subkeys) { auto &sig = subkey.signatures.front(); assert_non_null(pkey = rnp_key_store_get_signer_key(pubring, &sig)); /* high level interface */ sinfo.sig = &sig; pgp_key_id_t subid; assert_rnp_success(pgp_keyid(subid, subkey.subkey)); char ssubid[PGP_KEY_ID_SIZE * 2 + 1]; assert_true(rnp::hex_encode(subid.data(), subid.size(), ssubid, sizeof(ssubid))); pgp_key_t *psub = rnp_tests_get_key_by_id(pubring, ssubid); assert_non_null(psub); pkey->validate_binding(sinfo, *psub, global_ctx); assert_true(sinfo.valid); /* low level check */ hash = signature_hash_binding(sig, keyref.key, subkey.subkey); pkey->validate_sig(sinfo, *hash, global_ctx); assert_true(sinfo.valid); } } delete pubring; } static bool validate_key_sigs(const char *path, rnp::SecurityContext &global_ctx) { rnp_key_store_t *pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, path, global_ctx); bool valid = rnp_key_store_load_from_path(pubring, NULL); for (auto &key : pubring->keys) { key.validate(*pubring); valid = valid && key.valid(); } delete pubring; return valid; } TEST_F(rnp_tests, test_stream_key_signature_validate) { /* v3 public key */ rnp_key_store_t *pubring = new rnp_key_store_t(PGP_KEY_STORE_GPG, "data/keyrings/4/rsav3-p.asc", global_ctx); assert_true(rnp_key_store_load_from_path(pubring, NULL)); assert_int_equal(rnp_key_store_get_key_count(pubring), 1); pgp_key_t &pkey = pubring->keys.front(); pkey.validate(*pubring); /* MD5 signature is marked as invalid by default */ assert_false(pkey.valid()); rnp::SecurityRule allow_md5( rnp::FeatureType::Hash, PGP_HASH_MD5, rnp::SecurityLevel::Default); allow_md5.override = true; /* Allow MD5 */ global_ctx.profile.add_rule(allow_md5); /* we need to manually reset signature validity */ pkey.get_sig(0).validity.reset(); pkey.revalidate(*pubring); assert_true(pkey.valid()); /* Remove MD5 and revalidate */ assert_true(global_ctx.profile.del_rule(allow_md5)); pkey.get_sig(0).validity.reset(); pkey.revalidate(*pubring); assert_false(pkey.valid()); delete pubring; /* keyring */ 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)); assert_true(rnp_key_store_get_key_count(pubring) > 0); int i = 0; for (auto &key : pubring->keys) { key.validate(*pubring); // subkey #2 is expired if (i == 2) { assert_false(key.valid()); } else { assert_true(key.valid()); } i++; } delete pubring; /* misc key files */ auto validate = [this](const std::string &file) { auto path = "data/test_stream_key_load/" + file; return validate_key_sigs(path.c_str(), this->global_ctx); }; assert_true(validate("dsa-eg-pub.asc")); assert_true(validate("dsa-eg-sec.asc")); assert_true(validate("ecc-25519-pub.asc")); assert_true(validate("ecc-25519-sec.asc")); assert_true(validate("ecc-x25519-pub.asc")); assert_true(validate("ecc-x25519-sec.asc")); assert_true(validate("ecc-p256-pub.asc")); assert_true(validate("ecc-p256-sec.asc")); assert_true(validate("ecc-p384-pub.asc")); assert_true(validate("ecc-p384-sec.asc")); assert_true(validate("ecc-p521-pub.asc")); assert_true(validate("ecc-p521-sec.asc")); assert_true(validate("ecc-bp256-pub.asc") == brainpool_enabled()); assert_true(validate("ecc-bp256-sec.asc") == brainpool_enabled()); assert_true(validate("ecc-bp384-pub.asc") == brainpool_enabled()); assert_true(validate("ecc-bp384-sec.asc") == brainpool_enabled()); assert_true(validate("ecc-bp512-pub.asc") == brainpool_enabled()); assert_true(validate("ecc-bp512-sec.asc") == brainpool_enabled()); assert_true(validate("ecc-p256k1-pub.asc")); assert_true(validate("ecc-p256k1-sec.asc")); } TEST_F(rnp_tests, test_stream_verify_no_key) { cli_rnp_t rnp; rnp_cfg cfg; /* setup rnp structure and params */ cfg.set_str(CFG_KR_PUB_PATH, ""); cfg.set_str(CFG_KR_SEC_PATH, ""); cfg.set_str(CFG_KR_PUB_FORMAT, RNP_KEYSTORE_GPG); cfg.set_str(CFG_KR_SEC_FORMAT, RNP_KEYSTORE_GPG); assert_true(rnp.init(cfg)); rnp_cfg &rnpcfg = rnp.cfg(); /* setup cfg for verification */ rnpcfg.set_str(CFG_INFILE, "data/test_stream_verification/verify_encrypted_no_key.pgp"); rnpcfg.set_str(CFG_OUTFILE, "output.dat"); rnpcfg.set_bool(CFG_OVERWRITE, true); /* setup operation context */ assert_rnp_success( rnp_ffi_set_pass_provider(rnp.ffi, ffi_string_password_provider, (void *) "pass1")); rnpcfg.set_bool(CFG_NO_OUTPUT, false); if (sm2_enabled()) { /* operation should success if output is not discarded, i.e. operation = decrypt */ assert_true(cli_rnp_process_file(&rnp)); assert_int_equal(file_size("output.dat"), 4); } else { /* operation should fail */ assert_false(cli_rnp_process_file(&rnp)); assert_int_equal(file_size("output.dat"), -1); } /* try second password */ assert_rnp_success( rnp_ffi_set_pass_provider(rnp.ffi, ffi_string_password_provider, (void *) "pass2")); assert_true(cli_rnp_process_file(&rnp)); assert_int_equal(file_size("output.dat"), 4); /* decryption/verification fails without password */ assert_rnp_success( rnp_ffi_set_pass_provider(rnp.ffi, ffi_failing_password_provider, NULL)); assert_false(cli_rnp_process_file(&rnp)); assert_int_equal(file_size("output.dat"), -1); /* decryption/verification fails with wrong password */ assert_rnp_success( rnp_ffi_set_pass_provider(rnp.ffi, ffi_string_password_provider, (void *) "pass_wrong")); assert_false(cli_rnp_process_file(&rnp)); assert_int_equal(file_size("output.dat"), -1); /* verification fails if output is discarded, i.e. operation = verify */ rnpcfg.set_bool(CFG_NO_OUTPUT, true); assert_false(cli_rnp_process_file(&rnp)); assert_int_equal(file_size("output.dat"), -1); /* cleanup */ rnp.end(); } static bool check_dump_file_dst(const char *file, bool mpi, bool grip) { pgp_source_t src; pgp_dest_t dst; rnp_dump_ctx_t ctx = {0}; ctx.dump_mpi = mpi; ctx.dump_grips = grip; if (init_file_src(&src, file)) { return false; } if (init_mem_dest(&dst, NULL, 0)) { return false; } if (stream_dump_packets(&ctx, &src, &dst)) { return false; } src_close(&src); dst_close(&dst, false); return true; } static bool check_dump_file_json(const char *file, bool mpi, bool grip) { pgp_source_t src; rnp_dump_ctx_t ctx = {0}; json_object * jso = NULL; ctx.dump_mpi = mpi; ctx.dump_grips = grip; if (init_file_src(&src, file)) { return false; } if (stream_dump_packets_json(&ctx, &src, &jso)) { return false; } if (!json_object_is_type(jso, json_type_array)) { return false; } src_close(&src); json_object_put(jso); return true; } static bool check_dump_file(const char *file, bool mpi, bool grip) { return check_dump_file_dst(file, mpi, grip) && check_dump_file_json(file, mpi, grip); } TEST_F(rnp_tests, test_y2k38) { cli_rnp_t rnp; rnp_cfg cfg; /* setup rnp structure and params */ cfg.set_str(CFG_KR_PUB_PATH, "data/keyrings/6"); cfg.set_str(CFG_KR_SEC_PATH, "data/keyrings/6"); cfg.set_str(CFG_KR_PUB_FORMAT, RNP_KEYSTORE_GPG); cfg.set_str(CFG_KR_SEC_FORMAT, RNP_KEYSTORE_GPG); cfg.set_str(CFG_IO_RESS, "stderr.dat"); assert_true(rnp.init(cfg)); rnp_cfg &rnpcfg = rnp.cfg(); /* verify */ rnpcfg.set_str(CFG_INFILE, "data/test_messages/future.pgp"); rnpcfg.set_bool(CFG_OVERWRITE, true); assert_false(cli_rnp_process_file(&rnp)); /* clean up and flush the file */ rnp.end(); /* check the file for presence of correct dates */ auto output = file_to_str("stderr.dat"); time_t crtime = 0xC0000000; std::string correctMade = "signature made "; if (rnp_y2k38_warning(crtime)) { correctMade += ">="; } correctMade += rnp_ctime(crtime); assert_true( std::search(output.begin(), output.end(), correctMade.begin(), correctMade.end()) != output.end()); time_t validtime = rnp_timeadd(crtime, 0xD0000000); std::string correctValid = "Valid until "; if (rnp_y2k38_warning(validtime)) { correctValid += ">="; } correctValid += rnp_ctime(validtime); assert_true( std::search(output.begin(), output.end(), correctValid.begin(), correctValid.end()) != output.end()); unlink("stderr.dat"); } TEST_F(rnp_tests, test_stream_dumper_y2k38) { pgp_source_t src; pgp_dest_t dst; rnp_dump_ctx_t ctx = {0}; assert_rnp_success(init_file_src(&src, "data/keyrings/6/pubring.gpg")); assert_rnp_success(init_mem_dest(&dst, NULL, 0)); assert_rnp_success(stream_dump_packets(&ctx, &src, &dst)); src_close(&src); auto written = (const uint8_t *) mem_dest_get_memory(&dst); auto last = written + dst.writeb; time_t timestamp = 2958774690; // regenerate time for the current timezone std::string correct = "creation time: 2958774690 ("; if (rnp_y2k38_warning(timestamp)) { correct += ">="; } correct += rnp_ctime(timestamp).substr(0, 24); correct += ')'; assert_true(std::search(written, last, correct.begin(), correct.end()) != last); dst_close(&dst, false); } TEST_F(rnp_tests, test_stream_dumper) { pgp_source_t src; pgp_dest_t dst; rnp_dump_ctx_t ctx = {0}; assert_true(check_dump_file("data/keyrings/1/pubring.gpg", false, false)); assert_true(check_dump_file("data/keyrings/1/secring.gpg", false, false)); assert_true(check_dump_file("data/keyrings/4/rsav3-p.asc", false, false)); assert_true(check_dump_file("data/keyrings/4/rsav3-p.asc", true, true)); assert_true(check_dump_file("data/keyrings/4/rsav3-s.asc", true, false)); assert_true(check_dump_file("data/test_repgp/encrypted_text.gpg", true, false)); assert_true(check_dump_file("data/test_repgp/signed.gpg", true, false)); assert_true(check_dump_file("data/test_repgp/encrypted_key.gpg", true, false)); assert_true(check_dump_file("data/test_stream_key_load/dsa-eg-pub.asc", true, true)); assert_true(check_dump_file("data/test_stream_key_load/dsa-eg-sec.asc", true, true)); assert_true(check_dump_file("data/test_stream_key_load/ecc-25519-pub.asc", true, true)); assert_true(check_dump_file("data/test_stream_key_load/ecc-25519-sec.asc", true, true)); assert_true(check_dump_file("data/test_stream_key_load/ecc-x25519-pub.asc", true, true)); assert_true(check_dump_file("data/test_stream_key_load/ecc-x25519-sec.asc", true, true)); assert_true(check_dump_file("data/test_stream_key_load/ecc-p256-pub.asc", true, true)); assert_true(check_dump_file("data/test_stream_key_load/ecc-p256-sec.asc", true, true)); assert_true(check_dump_file("data/test_stream_key_load/ecc-p384-pub.asc", true, true)); assert_true(check_dump_file("data/test_stream_key_load/ecc-p384-sec.asc", true, true)); assert_true(check_dump_file("data/test_stream_key_load/ecc-p521-pub.asc", true, true)); assert_true(check_dump_file("data/test_stream_key_load/ecc-p521-sec.asc", true, true)); assert_true(check_dump_file("data/test_stream_key_load/ecc-bp256-pub.asc", true, true)); assert_true(check_dump_file("data/test_stream_key_load/ecc-bp256-sec.asc", true, true)); assert_true(check_dump_file("data/test_stream_key_load/ecc-bp384-pub.asc", true, true)); assert_true(check_dump_file("data/test_stream_key_load/ecc-bp384-sec.asc", true, true)); assert_true(check_dump_file("data/test_stream_key_load/ecc-bp512-pub.asc", true, true)); assert_true(check_dump_file("data/test_stream_key_load/ecc-bp512-sec.asc", true, true)); assert_true(check_dump_file("data/test_stream_key_load/ecc-p256k1-pub.asc", true, true)); assert_true(check_dump_file("data/test_stream_key_load/ecc-p256k1-sec.asc", true, true)); assert_true(check_dump_file("data/test_stream_signatures/source.txt.asc", true, true)); assert_true(check_dump_file("data/test_stream_signatures/source.txt.asc.asc", true, true)); assert_true(check_dump_file( "data/test_stream_verification/verify_encrypted_no_key.pgp", true, true)); assert_rnp_success(init_file_src(&src, "data/test_stream_signatures/source.txt")); assert_rnp_success(init_mem_dest(&dst, NULL, 0)); assert_rnp_failure(stream_dump_packets(&ctx, &src, &dst)); src_close(&src); dst_close(&dst, false); assert_rnp_success(init_file_src(&src, "data/test_messages/message.txt.enc-no-mdc")); assert_rnp_success(init_mem_dest(&dst, NULL, 0)); assert_rnp_success(stream_dump_packets(&ctx, &src, &dst)); src_close(&src); dst_close(&dst, false); assert_rnp_success(init_file_src(&src, "data/test_messages/message.txt.enc-mdc")); assert_rnp_success(init_mem_dest(&dst, NULL, 0)); assert_rnp_success(stream_dump_packets(&ctx, &src, &dst)); src_close(&src); dst_close(&dst, false); assert_rnp_success(init_file_src(&src, "data/test_messages/message-32k-crlf.txt.gpg")); assert_rnp_success(init_mem_dest(&dst, NULL, 0)); assert_rnp_success(stream_dump_packets(&ctx, &src, &dst)); src_close(&src); dst_close(&dst, false); } TEST_F(rnp_tests, test_stream_z) { pgp_source_t src; pgp_dest_t dst; rnp_dump_ctx_t ctx = {0}; /* packet dumper will decompress source stream, making less code lines here */ ctx.dump_mpi = true; ctx.dump_packets = true; assert_rnp_success(init_file_src(&src, "data/test_stream_z/4gb.bzip2")); assert_rnp_success(init_null_dest(&dst)); assert_rnp_success(stream_dump_packets(&ctx, &src, &dst)); src_close(&src); dst_close(&dst, true); assert_rnp_success(init_file_src(&src, "data/test_stream_z/4gb.bzip2.cut")); assert_rnp_success(init_null_dest(&dst)); assert_rnp_success(stream_dump_packets(&ctx, &src, &dst)); src_close(&src); dst_close(&dst, true); assert_rnp_success(init_file_src(&src, "data/test_stream_z/128mb.zlib")); assert_rnp_success(init_null_dest(&dst)); assert_rnp_success(stream_dump_packets(&ctx, &src, &dst)); src_close(&src); dst_close(&dst, true); assert_rnp_success(init_file_src(&src, "data/test_stream_z/128mb.zlib.cut")); assert_rnp_success(init_null_dest(&dst)); assert_rnp_success(stream_dump_packets(&ctx, &src, &dst)); src_close(&src); dst_close(&dst, true); assert_rnp_success(init_file_src(&src, "data/test_stream_z/128mb.zip")); assert_rnp_success(init_null_dest(&dst)); assert_rnp_success(stream_dump_packets(&ctx, &src, &dst)); src_close(&src); dst_close(&dst, true); assert_rnp_success(init_file_src(&src, "data/test_stream_z/128mb.zip.cut")); assert_rnp_success(init_null_dest(&dst)); assert_rnp_success(stream_dump_packets(&ctx, &src, &dst)); src_close(&src); dst_close(&dst, true); } /* This test checks for GitHub issue #814. */ TEST_F(rnp_tests, test_stream_814_dearmor_double_free) { pgp_source_t src; pgp_dest_t dst; const char * buf = "-----BEGIN PGP BAD HEADER-----"; assert_rnp_success(init_mem_src(&src, buf, strlen(buf), false)); assert_rnp_success(init_null_dest(&dst)); assert_rnp_failure(rnp_dearmor_source(&src, &dst)); src_close(&src); dst_close(&dst, true); } TEST_F(rnp_tests, test_stream_825_dearmor_blank_line) { rnp_key_store_t *keystore = NULL; pgp_source_t src = {}; keystore = new rnp_key_store_t(PGP_KEY_STORE_GPG, "", global_ctx); assert_rnp_success( init_file_src(&src, "data/test_stream_armor/extra_line_before_trailer.asc")); assert_true(rnp_key_store_load_from_src(keystore, &src, NULL)); assert_int_equal(rnp_key_store_get_key_count(keystore), 2); src_close(&src); delete keystore; } static bool try_dearmor(const char *str, int len) { pgp_source_t src = {}; pgp_dest_t dst = {}; bool res = false; if (len < 0) { return false; } if (init_mem_src(&src, str, len, false) != RNP_SUCCESS) { goto done; } if (init_null_dest(&dst) != RNP_SUCCESS) { goto done; } res = rnp_dearmor_source(&src, &dst) == RNP_SUCCESS; done: src_close(&src); dst_close(&dst, true); return res; } TEST_F(rnp_tests, test_stream_dearmor_edge_cases) { const char *HDR = "-----BEGIN PGP PUBLIC KEY BLOCK-----"; const char *B1 = "mDMEWsN6MBYJKwYBBAHaRw8BAQdAAS+nkv9BdVi0JX7g6d+O201bdKhdowbielOo"; const char *B2 = "ugCpCfi0CWVjYy0yNTUxOYiUBBMWCAA8AhsDBQsJCAcCAyICAQYVCgkICwIEFgID"; const char *B3 = "AQIeAwIXgBYhBCH8aCdKrjtd45pCd8x4YniYGwcoBQJcVa/NAAoJEMx4YniYGwco"; const char *B4 = "lFAA/jMt3RUUb5xt63JW6HFcrYq0RrDAcYMsXAY73iZpPsEcAQDmKbH21LkwoClU"; const char *B5 = "9RrUJSYZnMla/pQdgOxd7/PjRCpbCg=="; const char *CRC = "=miZp"; const char *FTR = "-----END PGP PUBLIC KEY BLOCK-----"; const char *FTR2 = "-----END PGP WEIRD KEY BLOCK-----"; char b64[1024]; char msg[1024]; int b64len = 0; int len = 0; /* fill the body with normal \n line endings */ b64len = snprintf(b64, sizeof(b64), "%s\n%s\n%s\n%s\n%s", B1, B2, B3, B4, B5); assert_true((b64len > 0) && (b64len < (int) sizeof(b64))); /* try normal message */ len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s\n%s\n", HDR, b64, CRC, FTR); assert_true(try_dearmor(msg, len)); /* no empty line after the headers, now accepted, see #1289 */ len = snprintf(msg, sizeof(msg), "%s\n%s\n%s\n%s\n", HDR, b64, CRC, FTR); assert_true(try_dearmor(msg, len)); /* \r\n line ending */ len = snprintf(msg, sizeof(msg), "%s\r\n\r\n%s\r\n%s\r\n%s\r\n", HDR, b64, CRC, FTR); assert_true(try_dearmor(msg, len)); /* mixed line ending */ len = snprintf(msg, sizeof(msg), "%s\r\n\n%s\r\n%s\n%s\r\n", HDR, b64, CRC, FTR); assert_true(try_dearmor(msg, len)); /* extra line before the footer */ len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s\n\n%s\n", HDR, b64, CRC, FTR); assert_true(try_dearmor(msg, len)); /* extra spaces after the header: allowed by RFC */ len = snprintf(msg, sizeof(msg), "%s \t \n\n%s\n%s\n%s\n", HDR, b64, CRC, FTR); assert_true(try_dearmor(msg, len)); /* extra spaces after the footer: allowed by RFC as well */ len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s\n%s \n", HDR, b64, CRC, FTR); assert_true(try_dearmor(msg, len)); len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s\n%s\t\n", HDR, b64, CRC, FTR); assert_true(try_dearmor(msg, len)); len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s\n%s\t\t \t\t \n", HDR, b64, CRC, FTR); assert_true(try_dearmor(msg, len)); /* invalid footer */ len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s\n%s\n", HDR, b64, CRC, FTR2); assert_false(try_dearmor(msg, len)); /* extra spaces or tabs before the footer - allow it, see issue #2199 */ len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s\n %s\n", HDR, b64, CRC, FTR); assert_true(try_dearmor(msg, len)); len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s\n\t\t %s\n", HDR, b64, CRC, FTR); assert_true(try_dearmor(msg, len)); /* no empty line between crc and footer - FAIL */ len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s%s\n", HDR, b64, CRC, FTR); assert_false(try_dearmor(msg, len)); /* extra chars before the footer - FAIL */ len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s\n11111%s\n", HDR, b64, CRC, FTR); assert_false(try_dearmor(msg, len)); /* cut out or extended b64 padding */ len = snprintf(msg, sizeof(msg), "%s\n\n%.*s\n%s\n%s\n", HDR, b64len - 1, b64, CRC, FTR); assert_false(try_dearmor(msg, len)); len = snprintf(msg, sizeof(msg), "%s\n\n%.*s\n%s\n%s\n", HDR, b64len - 2, b64, CRC, FTR); assert_false(try_dearmor(msg, len)); len = snprintf(msg, sizeof(msg), "%s\n\n%s==\n%s\n%s\n", HDR, b64, CRC, FTR); assert_false(try_dearmor(msg, len)); /* invalid chars in b64 data */ char old = b64[30]; b64[30] = '?'; len = snprintf(msg, sizeof(msg), "%s\n\n%s\n%s\n%s\n", HDR, b64, CRC, FTR); assert_false(try_dearmor(msg, len)); b64[30] = old; /* modified/malformed crc (should be accepted now, see #1401) */ len = snprintf(msg, sizeof(msg), "%s\n\n%s\n=miZq\n%s\n", HDR, b64, FTR); assert_true(try_dearmor(msg, len)); len = snprintf(msg, sizeof(msg), "%s\n\n%s\nmiZp\n%s\n", HDR, b64, FTR); assert_false(try_dearmor(msg, len)); len = snprintf(msg, sizeof(msg), "%s\n\n%s\n==miZp\n%s\n", HDR, b64, FTR); assert_false(try_dearmor(msg, len)); len = snprintf(msg, sizeof(msg), "%s\n\n%s\n=miZpp\n%s\n", HDR, b64, FTR); assert_false(try_dearmor(msg, len)); /* missing crc */ len = snprintf(msg, sizeof(msg), "%s\n\n%s\n\n%s\n", HDR, b64, FTR); assert_true(try_dearmor(msg, len)); } static void add_openpgp_layers( const char *msg, pgp_dest_t &pgpdst, int compr, int encr, rnp::SecurityContext &global_ctx) { pgp_source_t src = {}; pgp_dest_t dst = {}; assert_rnp_success(init_mem_src(&src, msg, strlen(msg), false)); assert_rnp_success(init_mem_dest(&dst, NULL, 0)); assert_rnp_success(rnp_wrap_src(src, dst, "message.txt", time(NULL))); src_close(&src); assert_rnp_success(init_mem_src(&src, mem_dest_own_memory(&dst), dst.writeb, true)); dst_close(&dst, false); /* add compression layers */ for (int i = 0; i < compr; i++) { pgp_compression_type_t alg = (pgp_compression_type_t)((i % 3) + 1); assert_rnp_success(init_mem_dest(&dst, NULL, 0)); assert_rnp_success(rnp_compress_src(src, dst, alg, 9)); src_close(&src); assert_rnp_success(init_mem_src(&src, mem_dest_own_memory(&dst), dst.writeb, true)); dst_close(&dst, false); } /* add encryption layers */ for (int i = 0; i < encr; i++) { assert_rnp_success(init_mem_dest(&dst, NULL, 0)); assert_rnp_success(rnp_raw_encrypt_src(src, dst, "password", global_ctx)); src_close(&src); assert_rnp_success(init_mem_src(&src, mem_dest_own_memory(&dst), dst.writeb, true)); dst_close(&dst, false); } assert_rnp_success(init_mem_dest(&pgpdst, NULL, 0)); assert_rnp_success(dst_write_src(&src, &pgpdst)); src_close(&src); } TEST_F(rnp_tests, test_stream_deep_packet_nesting) { const char *message = "message"; pgp_dest_t dst = {}; /* add 30 compression layers and 2 encryption - must fail */ add_openpgp_layers(message, dst, 30, 2, global_ctx); #ifdef DUMP_TEST_CASE /* remove ifdef if you want to write it to stdout */ pgp_source_t src = {}; assert_rnp_success(init_mem_src(&src, mem_dest_get_memory(&dst), dst.writeb, false)); pgp_dest_t outdst = {}; assert_rnp_success(init_stdout_dest(&outdst)); assert_rnp_success(rnp_armor_source(&src, &outdst, PGP_ARMORED_MESSAGE)); dst_close(&outdst, false); src_close(&src); #endif /* decrypt it via FFI for less code */ rnp_ffi_t ffi = NULL; rnp_ffi_create(&ffi, "GPG", "GPG"); assert_rnp_success( rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); rnp_input_t input = NULL; assert_rnp_success(rnp_input_from_memory( &input, (const uint8_t *) mem_dest_get_memory(&dst), dst.writeb, false)); rnp_output_t output = NULL; assert_rnp_success(rnp_output_to_memory(&output, 0)); assert_rnp_failure(rnp_decrypt(ffi, input, output)); rnp_input_destroy(input); rnp_output_destroy(output); dst_close(&dst, false); /* add 27 compression & 4 encryption layers - must succeed */ add_openpgp_layers("message", dst, 27, 4, global_ctx); #ifdef DUMP_TEST_CASE /* remove ifdef if you want to write it to stdout */ assert_rnp_success(init_mem_src(&src, mem_dest_get_memory(&dst), dst.writeb, false)); assert_rnp_success(init_stdout_dest(&outdst)); assert_rnp_success(rnp_armor_source(&src, &outdst, PGP_ARMORED_MESSAGE)); dst_close(&outdst, false); src_close(&src); #endif /* decrypt it via FFI for less code */ assert_rnp_success(rnp_input_from_memory( &input, (const uint8_t *) mem_dest_get_memory(&dst), dst.writeb, false)); assert_rnp_success(rnp_output_to_memory(&output, 0)); assert_rnp_success(rnp_decrypt(ffi, input, output)); rnp_input_destroy(input); /* check output */ uint8_t *buf = NULL; size_t len = 0; assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); assert_int_equal(strlen(message), len); assert_int_equal(memcmp(buf, message, len), 0); rnp_output_destroy(output); dst_close(&dst, false); rnp_ffi_destroy(ffi); } static bool src_reader_generator(pgp_source_t *, void *buf, size_t len, size_t *read) { *read = len; for (; len; buf = ((uint8_t *) buf) + 1, len--) { *(uint8_t *) buf = len & 0x7F; } return true; } TEST_F(rnp_tests, test_stream_cache) { pgp_source_t src = {0}; uint8_t sample[sizeof(src.cache->buf)]; size_t samplesize = sizeof(sample); assert_true(src_reader_generator(NULL, sample, samplesize, &samplesize)); assert_int_equal(sizeof(sample), samplesize); init_src_common(&src, 0); int8_t *buf = (int8_t *) src.cache->buf; src.read = src_reader_generator; size_t len = sizeof(src.cache->buf); // empty cache, pos=0 memset(src.cache->buf, 0xFF, len); src.cache->pos = 0; src.cache->len = 0; assert_true(src_peek_eq(&src, NULL, len)); assert_false(memcmp(buf, sample, samplesize)); // empty cache, pos is somewhere in the middle memset(src.cache->buf, 0xFF, len); src.cache->pos = 100; src.cache->len = 100; assert_true(src_peek_eq(&src, NULL, len)); assert_false(memcmp(buf, sample, samplesize)); // empty cache, pos=max memset(src.cache->buf, 0xFF, len); src.cache->pos = len; src.cache->len = len; assert_true(src_peek_eq(&src, NULL, len)); assert_false(memcmp(buf, sample, samplesize)); // cache has some data in the middle src.cache->pos = 128; // sample boundary src.cache->len = 300; memset(src.cache->buf, 0xFF, src.cache->pos); memset(src.cache->buf + src.cache->len, 0xFF, len - src.cache->len); assert_true(src_peek_eq(&src, NULL, len)); assert_false(memcmp(buf, sample, samplesize)); // cache has some data starting from pos until the end src.cache->pos = 128; // sample boundary src.cache->len = len; memset(src.cache->buf, 0xFF, src.cache->pos); assert_true(src_peek_eq(&src, NULL, len)); assert_false(memcmp(buf, sample, samplesize)); // cache is almost full src.cache->pos = 0; src.cache->len = len - 1; src.cache->buf[len - 1] = 0xFF; assert_true(src_peek_eq(&src, NULL, len)); assert_false(memcmp(buf, sample, samplesize)); // cache is full src.cache->pos = 0; src.cache->len = len; memset(src.cache->buf, 0xFF, src.cache->pos); memset(src.cache->buf + src.cache->len, 0xFF, len - src.cache->len); assert_true(src_peek_eq(&src, NULL, len)); assert_false(memcmp(buf, sample, samplesize)); src_close(&src); }