diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:32:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:32:49 +0000 |
commit | 8053187731ae8e3eb368d8360989cf5fd6eed9f7 (patch) | |
tree | 32bada84ff5d7460cdf3934fcbdbe770d6afe4cd /src/tests/ffi-enc.cpp | |
parent | Initial commit. (diff) | |
download | rnp-8053187731ae8e3eb368d8360989cf5fd6eed9f7.tar.xz rnp-8053187731ae8e3eb368d8360989cf5fd6eed9f7.zip |
Adding upstream version 0.17.0.upstream/0.17.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tests/ffi-enc.cpp')
-rw-r--r-- | src/tests/ffi-enc.cpp | 1362 |
1 files changed, 1362 insertions, 0 deletions
diff --git a/src/tests/ffi-enc.cpp b/src/tests/ffi-enc.cpp new file mode 100644 index 0000000..55b0d10 --- /dev/null +++ b/src/tests/ffi-enc.cpp @@ -0,0 +1,1362 @@ +/* + * Copyright (c) 2020 [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 <fstream> +#include <vector> +#include <string> + +#include <rnp/rnp.h> +#include "rnp_tests.h" +#include "support.h" +#include "librepgp/stream-common.h" +#include "librepgp/stream-packet.h" +#include "librepgp/stream-sig.h" +#include <json.h> +#include <vector> +#include <string> +#include "file-utils.h" +#include <librepgp/stream-ctx.h> +#include "pgp-key.h" +#include "ffi-priv-types.h" + +static bool +getpasscb_once(rnp_ffi_t ffi, + void * app_ctx, + rnp_key_handle_t key, + const char * pgp_context, + char * buf, + size_t buf_len) +{ + const char **pass = (const char **) app_ctx; + if (!*pass) { + return false; + } + size_t pass_len = strlen(*pass); + if (pass_len >= buf_len) { + return false; + } + memcpy(buf, *pass, pass_len); + *pass = NULL; + return true; +} + +static bool +getpasscb_inc(rnp_ffi_t ffi, + void * app_ctx, + rnp_key_handle_t key, + const char * pgp_context, + char * buf, + size_t buf_len) +{ + int * num = (int *) app_ctx; + std::string pass = "pass" + std::to_string(*num); + (*num)++; + strncpy(buf, pass.c_str(), buf_len - 1); + return true; +} + +#define TBL_MAX_USERIDS 4 +typedef struct key_tbl_t { + const uint8_t *key_data; + size_t key_data_size; + bool secret; + const char * keyid; + const char * grip; + const char * userids[TBL_MAX_USERIDS + 1]; +} key_tbl_t; + +static void +tbl_getkeycb(rnp_ffi_t ffi, + void * app_ctx, + const char *identifier_type, + const char *identifier, + bool secret) +{ + key_tbl_t *found = NULL; + for (key_tbl_t *tbl = (key_tbl_t *) app_ctx; tbl && tbl->key_data && !found; tbl++) { + if (tbl->secret != secret) { + continue; + } + if (!strcmp(identifier_type, "keyid") && !strcmp(identifier, tbl->keyid)) { + found = tbl; + break; + } else if (!strcmp(identifier_type, "grip") && !strcmp(identifier, tbl->grip)) { + found = tbl; + break; + } else if (!strcmp(identifier_type, "userid")) { + for (size_t i = 0; i < TBL_MAX_USERIDS; i++) { + if (!strcmp(identifier, tbl->userids[i])) { + found = tbl; + break; + } + } + } + } + if (found) { + char *format = NULL; + assert_rnp_success( + rnp_detect_key_format(found->key_data, found->key_data_size, &format)); + assert_non_null(format); + uint32_t flags = secret ? RNP_LOAD_SAVE_SECRET_KEYS : RNP_LOAD_SAVE_PUBLIC_KEYS; + rnp_input_t input = NULL; + assert_rnp_success( + rnp_input_from_memory(&input, found->key_data, found->key_data_size, true)); + assert_non_null(input); + assert_rnp_success(rnp_load_keys(ffi, format, input, flags)); + free(format); + assert_rnp_success(rnp_input_destroy(input)); + input = NULL; + } +} + +TEST_F(rnp_tests, test_ffi_encrypt_pass) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_op_encrypt_t op = NULL; + const char * plaintext = "data1"; + + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + + // load our keyrings + assert_true( + load_keys_gpg(ffi, "data/keyrings/1/pubring.gpg", "data/keyrings/1/secring.gpg")); + + // write out some data + str_to_file("plaintext", plaintext); + // create input+output w/ bad paths (should fail) + input = NULL; + assert_rnp_failure(rnp_input_from_path(&input, "noexist")); + assert_null(input); + assert_rnp_failure(rnp_output_to_path(&output, "")); + assert_null(output); + + // create input+output + assert_rnp_success(rnp_input_from_path(&input, "plaintext")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "encrypted")); + assert_non_null(output); + // create encrypt operation + assert_rnp_failure(rnp_op_encrypt_create(NULL, ffi, input, output)); + assert_rnp_failure(rnp_op_encrypt_create(&op, NULL, input, output)); + assert_rnp_failure(rnp_op_encrypt_create(&op, ffi, NULL, output)); + assert_rnp_failure(rnp_op_encrypt_create(&op, ffi, input, NULL)); + assert_rnp_success(rnp_op_encrypt_create(&op, ffi, input, output)); + // add password (using all defaults) + assert_rnp_failure(rnp_op_encrypt_add_password(NULL, "pass1", NULL, 0, NULL)); + assert_rnp_failure(rnp_op_encrypt_add_password(op, "", NULL, 0, NULL)); + assert_rnp_failure(rnp_op_encrypt_add_password(op, "pass1", "WRONG", 0, NULL)); + assert_rnp_failure(rnp_op_encrypt_add_password(op, "pass1", NULL, 0, "WRONG")); + assert_rnp_success(rnp_op_encrypt_add_password(op, "pass1", NULL, 0, NULL)); + // add password + if (!sm2_enabled() && !twofish_enabled()) { + assert_rnp_failure(rnp_op_encrypt_add_password(op, "pass2", "SM3", 12345, "TWOFISH")); + assert_rnp_failure( + rnp_op_encrypt_add_password(op, "pass2", "SHA256", 12345, "TWOFISH")); + assert_rnp_success( + rnp_op_encrypt_add_password(op, "pass2", "SHA256", 12345, "BLOWFISH")); + } else if (!sm2_enabled() && twofish_enabled()) { + assert_rnp_failure(rnp_op_encrypt_add_password(op, "pass2", "SM3", 12345, "TWOFISH")); + assert_rnp_success( + rnp_op_encrypt_add_password(op, "pass2", "SHA256", 12345, "TWOFISH")); + } else if (sm2_enabled() && !twofish_enabled()) { + assert_rnp_failure(rnp_op_encrypt_add_password(op, "pass2", "SM3", 12345, "TWOFISH")); + assert_rnp_success(rnp_op_encrypt_add_password(op, "pass2", "SM3", 12345, "BLOWFISH")); + } else { + assert_rnp_success(rnp_op_encrypt_add_password(op, "pass2", "SM3", 12345, "TWOFISH")); + } + // set the data encryption cipher + assert_rnp_failure(rnp_op_encrypt_set_cipher(NULL, "CAST5")); + assert_rnp_failure(rnp_op_encrypt_set_cipher(op, NULL)); + assert_rnp_failure(rnp_op_encrypt_set_cipher(op, "WRONG")); + if (cast5_enabled()) { + assert_rnp_success(rnp_op_encrypt_set_cipher(op, "CAST5")); + } else { + assert_rnp_failure(rnp_op_encrypt_set_cipher(op, "CAST5")); + assert_rnp_success(rnp_op_encrypt_set_cipher(op, "AES256")); + } + // execute the operation + assert_rnp_success(rnp_op_encrypt_execute(op)); + + // make sure the output file was created + assert_true(rnp_file_exists("encrypted")); + + // cleanup + assert_rnp_success(rnp_input_destroy(input)); + input = NULL; + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_rnp_success(rnp_op_encrypt_destroy(op)); + op = NULL; + + /* decrypt */ + + // decrypt (no pass provider, should fail) + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_non_null(output); + assert_rnp_success(rnp_ffi_set_pass_provider(ffi, NULL, NULL)); + assert_rnp_failure(rnp_decrypt(ffi, input, output)); + // cleanup + rnp_input_destroy(input); + input = NULL; + rnp_output_destroy(output); + output = NULL; + + // decrypt (wrong pass, should fail) + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_non_null(output); + const char *pass = "wrong1"; + assert_rnp_success(rnp_ffi_set_pass_provider(ffi, getpasscb_once, &pass)); + assert_rnp_failure(rnp_decrypt(ffi, input, output)); + // cleanup + rnp_input_destroy(input); + input = NULL; + rnp_output_destroy(output); + output = NULL; + + // decrypt (pass1) + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_non_null(output); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "pass1")); + assert_rnp_success(rnp_decrypt(ffi, input, output)); + // cleanup + rnp_input_destroy(input); + input = NULL; + rnp_output_destroy(output); + output = NULL; + // compare the decrypted file + assert_string_equal(file_to_str("decrypted").c_str(), plaintext); + unlink("decrypted"); + + // decrypt (pass2) + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_non_null(output); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "pass2")); + assert_rnp_success(rnp_decrypt(ffi, input, output)); + // cleanup + rnp_input_destroy(input); + input = NULL; + rnp_output_destroy(output); + output = NULL; + // compare the decrypted file + assert_string_equal(file_to_str("decrypted").c_str(), plaintext); + // final cleanup + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_encrypt_pass_provider) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_op_encrypt_t op = NULL; + const char *plaintext = "Data encrypted with password provided via password provider."; + + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + // write out some data + str_to_file("plaintext", plaintext); + // create input + output + assert_rnp_success(rnp_input_from_path(&input, "plaintext")); + assert_rnp_success(rnp_output_to_path(&output, "encrypted")); + // create encrypt operation + assert_rnp_success(rnp_op_encrypt_create(&op, ffi, input, output)); + // add password with NULL password provider set - should fail + assert_rnp_success(rnp_ffi_set_pass_provider(ffi, NULL, NULL)); + assert_rnp_failure(rnp_op_encrypt_add_password(op, NULL, NULL, 0, NULL)); + // add password with password provider set. + int pswdnum = 1; + assert_rnp_success(rnp_ffi_set_pass_provider(ffi, getpasscb_inc, &pswdnum)); + assert_rnp_success(rnp_op_encrypt_add_password(op, NULL, NULL, 0, NULL)); + // add another password with different encryption parameters + if (!sm2_enabled() && !twofish_enabled()) { + assert_rnp_failure(rnp_op_encrypt_add_password(op, NULL, "SM3", 12345, "TWOFISH")); + assert_rnp_failure(rnp_op_encrypt_add_password(op, NULL, "SHA256", 12345, "TWOFISH")); + assert_rnp_success(rnp_op_encrypt_add_password(op, NULL, NULL, 12345, NULL)); + } else if (!sm2_enabled() && twofish_enabled()) { + assert_rnp_failure(rnp_op_encrypt_add_password(op, NULL, "SM3", 12345, "TWOFISH")); + assert_rnp_success(rnp_op_encrypt_add_password(op, NULL, "SHA256", 12345, "TWOFISH")); + } else if (sm2_enabled() && !twofish_enabled()) { + assert_rnp_failure(rnp_op_encrypt_add_password(op, NULL, "SM3", 12345, "TWOFISH")); + assert_rnp_success(rnp_op_encrypt_add_password(op, NULL, "SM3", 12345, NULL)); + } else { + assert_rnp_success(rnp_op_encrypt_add_password(op, NULL, "SM3", 12345, "TWOFISH")); + } + // set the data encryption cipher + assert_rnp_success(rnp_op_encrypt_set_cipher(op, "CAMELLIA256")); + // execute the operation + assert_rnp_success(rnp_op_encrypt_execute(op)); + // make sure the output file was created + assert_true(rnp_file_exists("encrypted")); + + // cleanup + assert_rnp_success(rnp_input_destroy(input)); + input = NULL; + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_rnp_success(rnp_op_encrypt_destroy(op)); + op = NULL; + + /* decrypt with pass1 */ + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "pass1")); + assert_rnp_success(rnp_decrypt(ffi, input, output)); + rnp_input_destroy(input); + input = NULL; + rnp_output_destroy(output); + output = NULL; + assert_string_equal(file_to_str("decrypted").c_str(), plaintext); + unlink("decrypted"); + + /* decrypt with pass2 via provider */ + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + pswdnum = 2; + assert_rnp_success(rnp_ffi_set_pass_provider(ffi, getpasscb_inc, &pswdnum)); + assert_rnp_success(rnp_decrypt(ffi, input, output)); + rnp_input_destroy(input); + rnp_output_destroy(output); + assert_string_equal(file_to_str("decrypted").c_str(), plaintext); + unlink("decrypted"); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_encrypt_set_cipher) +{ + /* setup FFI */ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + /* create input + output */ + rnp_input_t input = NULL; + const char *plaintext = "Data encrypted with password using different CEK/KEK."; + assert_rnp_success( + rnp_input_from_memory(&input, (const uint8_t *) plaintext, strlen(plaintext), false)); + rnp_output_t output = NULL; + assert_rnp_success(rnp_output_to_path(&output, "encrypted")); + /* create encrypt operation */ + rnp_op_encrypt_t op = NULL; + assert_rnp_success(rnp_op_encrypt_create(&op, ffi, input, output)); + /* use different sym algos */ + assert_rnp_success(rnp_op_encrypt_add_password(op, "password1", NULL, 0, "AES192")); + assert_rnp_success(rnp_op_encrypt_add_password(op, "password2", NULL, 0, "AES128")); + assert_rnp_success(rnp_op_encrypt_set_cipher(op, "AES256")); + /* execute the operation */ + assert_rnp_success(rnp_op_encrypt_execute(op)); + assert_true(rnp_file_exists("encrypted")); + /* cleanup */ + assert_rnp_success(rnp_input_destroy(input)); + assert_rnp_success(rnp_output_destroy(output)); + assert_rnp_success(rnp_op_encrypt_destroy(op)); + /* decrypt with password1 */ + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password1")); + rnp_op_verify_t verify; + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + rnp_input_destroy(input); + rnp_output_destroy(output); + assert_string_equal(file_to_str("decrypted").c_str(), plaintext); + /* Check protection info */ + char *mode = NULL; + char *cipher = NULL; + bool valid = false; + assert_rnp_success(rnp_op_verify_get_protection_info(verify, &mode, &cipher, &valid)); + assert_string_equal(mode, "cfb-mdc"); + assert_string_equal(cipher, "AES256"); + assert_true(valid); + rnp_buffer_destroy(mode); + rnp_buffer_destroy(cipher); + /* Check SESKs */ + size_t count = 0; + assert_rnp_success(rnp_op_verify_get_symenc_count(verify, &count)); + assert_int_equal(count, 2); + /* First SESK: AES192 */ + rnp_symenc_handle_t symenc = NULL; + assert_rnp_success(rnp_op_verify_get_symenc_at(verify, 0, &symenc)); + char *aalg = NULL; + assert_rnp_success(rnp_symenc_get_aead_alg(symenc, &aalg)); + assert_string_equal(aalg, "None"); + assert_rnp_success(rnp_symenc_get_cipher(symenc, &cipher)); + assert_string_equal(cipher, "AES192"); + rnp_buffer_destroy(aalg); + rnp_buffer_destroy(cipher); + /* Second SESK: AES128 */ + assert_rnp_success(rnp_op_verify_get_symenc_at(verify, 1, &symenc)); + assert_rnp_success(rnp_symenc_get_aead_alg(symenc, &aalg)); + assert_string_equal(aalg, "None"); + assert_rnp_success(rnp_symenc_get_cipher(symenc, &cipher)); + assert_string_equal(cipher, "AES128"); + rnp_buffer_destroy(aalg); + rnp_buffer_destroy(cipher); + unlink("decrypted"); + unlink("encrypted"); + rnp_op_verify_destroy(verify); + + /* Now use AEAD */ + assert_rnp_success( + rnp_input_from_memory(&input, (const uint8_t *) plaintext, strlen(plaintext), false)); + assert_rnp_success(rnp_output_to_path(&output, "encrypted-aead")); + /* create encrypt operation */ + assert_rnp_success(rnp_op_encrypt_create(&op, ffi, input, output)); + /* use different sym algos */ + assert_rnp_success(rnp_op_encrypt_add_password(op, "password1", NULL, 0, "AES256")); + assert_rnp_success(rnp_op_encrypt_add_password(op, "password2", NULL, 0, "AES192")); + assert_rnp_success(rnp_op_encrypt_set_cipher(op, "AES128")); + assert_rnp_success(rnp_op_encrypt_set_aead(op, "OCB")); + /* execute the operation */ + assert_rnp_success(rnp_op_encrypt_execute(op)); + assert_true(rnp_file_exists("encrypted-aead")); + /* cleanup */ + assert_rnp_success(rnp_input_destroy(input)); + assert_rnp_success(rnp_output_destroy(output)); + assert_rnp_success(rnp_op_encrypt_destroy(op)); + /* decrypt with password2 */ + assert_rnp_success(rnp_input_from_path(&input, "encrypted-aead")); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password2")); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + rnp_input_destroy(input); + rnp_output_destroy(output); + assert_string_equal(file_to_str("decrypted").c_str(), plaintext); + /* Check protection info */ + assert_rnp_success(rnp_op_verify_get_protection_info(verify, &mode, &cipher, &valid)); + assert_string_equal(mode, "aead-ocb"); + assert_string_equal(cipher, "AES128"); + assert_true(valid); + rnp_buffer_destroy(mode); + rnp_buffer_destroy(cipher); + /* Check SESKs */ + assert_rnp_success(rnp_op_verify_get_symenc_count(verify, &count)); + assert_int_equal(count, 2); + /* First SESK: AES192 */ + assert_rnp_success(rnp_op_verify_get_symenc_at(verify, 0, &symenc)); + assert_rnp_success(rnp_symenc_get_aead_alg(symenc, &aalg)); + assert_string_equal(aalg, "OCB"); + assert_rnp_success(rnp_symenc_get_cipher(symenc, &cipher)); + assert_string_equal(cipher, "AES256"); + rnp_buffer_destroy(aalg); + rnp_buffer_destroy(cipher); + /* Second SESK: AES128 */ + assert_rnp_success(rnp_op_verify_get_symenc_at(verify, 1, &symenc)); + assert_rnp_success(rnp_symenc_get_aead_alg(symenc, &aalg)); + assert_string_equal(aalg, "OCB"); + assert_rnp_success(rnp_symenc_get_cipher(symenc, &cipher)); + assert_string_equal(cipher, "AES192"); + rnp_buffer_destroy(aalg); + rnp_buffer_destroy(cipher); + unlink("decrypted"); + unlink("encrypted-aead"); + rnp_op_verify_destroy(verify); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_encrypt_pk) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_op_encrypt_t op = NULL; + const char * plaintext = "data1"; + + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + + // load our keyrings + assert_true( + load_keys_gpg(ffi, "data/keyrings/1/pubring.gpg", "data/keyrings/1/secring.gpg")); + + // write out some data + str_to_file("plaintext", plaintext); + // create input+output + assert_rnp_success(rnp_input_from_path(&input, "plaintext")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "encrypted")); + assert_non_null(output); + // create encrypt operation + assert_rnp_success(rnp_op_encrypt_create(&op, ffi, input, output)); + // add recipients + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "userid", "key0-uid2", &key)); + assert_rnp_failure(rnp_op_encrypt_add_recipient(NULL, key)); + assert_rnp_failure(rnp_op_encrypt_add_recipient(op, NULL)); + assert_rnp_success(rnp_op_encrypt_add_recipient(op, key)); + rnp_key_handle_destroy(key); + key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "userid", "key1-uid1", &key)); + assert_rnp_success(rnp_op_encrypt_add_recipient(op, key)); + rnp_key_handle_destroy(key); + key = NULL; + // set the data encryption cipher + if (cast5_enabled()) { + assert_rnp_success(rnp_op_encrypt_set_cipher(op, "CAST5")); + } else { + assert_rnp_failure(rnp_op_encrypt_set_cipher(op, "CAST5")); + assert_rnp_success(rnp_op_encrypt_set_cipher(op, "AES256")); + } + // execute the operation + assert_rnp_success(rnp_op_encrypt_execute(op)); + + // make sure the output file was created + assert_true(rnp_file_exists("encrypted")); + + // cleanup + assert_rnp_success(rnp_input_destroy(input)); + input = NULL; + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_rnp_success(rnp_op_encrypt_destroy(op)); + op = NULL; + + /* decrypt */ + + // decrypt (no pass provider, should fail) + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_non_null(output); + assert_rnp_success(rnp_ffi_set_pass_provider(ffi, NULL, NULL)); + assert_rnp_failure(rnp_decrypt(ffi, input, output)); + // cleanup + rnp_input_destroy(input); + input = NULL; + rnp_output_destroy(output); + output = NULL; + + // decrypt (wrong pass, should fail) + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_non_null(output); + const char *pass = "wrong1"; + assert_rnp_success(rnp_ffi_set_pass_provider(ffi, getpasscb_once, &pass)); + assert_rnp_failure(rnp_decrypt(ffi, input, output)); + // cleanup + rnp_input_destroy(input); + input = NULL; + rnp_output_destroy(output); + output = NULL; + + // decrypt + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_non_null(output); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + assert_rnp_success(rnp_decrypt(ffi, input, output)); + // cleanup + rnp_input_destroy(input); + input = NULL; + rnp_output_destroy(output); + output = NULL; + // read in the decrypted file + assert_string_equal(file_to_str("decrypted").c_str(), plaintext); + // final cleanup + rnp_ffi_destroy(ffi); +} + +bool +first_key_password_provider(rnp_ffi_t ffi, + void * app_ctx, + rnp_key_handle_t key, + const char * pgp_context, + char * buf, + size_t buf_len) +{ + if (!key) { + throw std::invalid_argument("key"); + } + char *keyid = NULL; + rnp_key_get_keyid(key, &keyid); + if (strcmp(keyid, "8A05B89FAD5ADED1")) { + throw std::invalid_argument("keyid"); + } + rnp_buffer_destroy(keyid); + return false; +} + +TEST_F(rnp_tests, test_ffi_decrypt_pk_unlocked) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_op_encrypt_t op = NULL; + const char * plaintext = "data1"; + + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + + // load our keyrings + assert_true( + load_keys_gpg(ffi, "data/keyrings/1/pubring.gpg", "data/keyrings/1/secring.gpg")); + + // write out some data + str_to_file("plaintext", plaintext); + // create input+output + assert_rnp_success(rnp_input_from_path(&input, "plaintext")); + assert_rnp_success(rnp_output_to_path(&output, "encrypted")); + // create encrypt operation + assert_rnp_success(rnp_op_encrypt_create(&op, ffi, input, output)); + // add recipients + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "userid", "key0-uid2", &key)); + assert_rnp_success(rnp_op_encrypt_add_recipient(op, key)); + rnp_key_handle_destroy(key); + assert_rnp_success(rnp_locate_key(ffi, "userid", "key1-uid1", &key)); + assert_rnp_success(rnp_op_encrypt_add_recipient(op, key)); + rnp_key_handle_destroy(key); + // execute the operation + assert_rnp_success(rnp_op_encrypt_execute(op)); + + // make sure the output file was created + assert_true(rnp_file_exists("encrypted")); + + // cleanup + assert_rnp_success(rnp_input_destroy(input)); + assert_rnp_success(rnp_output_destroy(output)); + assert_rnp_success(rnp_op_encrypt_destroy(op)); + + /* decrypt (unlocked first key, no pass provider) */ + assert_rnp_success(rnp_ffi_set_pass_provider(ffi, NULL, NULL)); + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + rnp_key_handle_t defkey = NULL; + assert_rnp_success(rnp_locate_key(ffi, "userid", "key0-uid2", &key)); + assert_rnp_success(rnp_key_get_default_key(key, "encrypt", 0, &defkey)); + assert_non_null(defkey); + assert_rnp_success(rnp_key_unlock(defkey, "password")); + assert_rnp_success(rnp_decrypt(ffi, input, output)); + assert_rnp_success(rnp_key_lock(defkey)); + rnp_key_handle_destroy(key); + rnp_key_handle_destroy(defkey); + // cleanup + rnp_input_destroy(input); + rnp_output_destroy(output); + assert_string_equal(file_to_str("decrypted").c_str(), plaintext); + assert_int_equal(unlink("decrypted"), 0); + + /* decrypt (unlocked second key, no pass provider) */ + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_rnp_success(rnp_locate_key(ffi, "userid", "key1-uid1", &key)); + assert_rnp_success(rnp_key_get_default_key(key, "encrypt", 0, &defkey)); + assert_non_null(defkey); + assert_rnp_success(rnp_key_unlock(defkey, "password")); + assert_rnp_success(rnp_decrypt(ffi, input, output)); + assert_rnp_success(rnp_key_lock(defkey)); + rnp_key_handle_destroy(key); + rnp_key_handle_destroy(defkey); + // cleanup + rnp_input_destroy(input); + rnp_output_destroy(output); + assert_string_equal(file_to_str("decrypted").c_str(), plaintext); + assert_int_equal(unlink("decrypted"), 0); + + /* decrypt (unlocked first key, pass provider should not be called) */ + assert_rnp_success(rnp_ffi_set_pass_provider(ffi, ffi_asserting_password_provider, NULL)); + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_rnp_success(rnp_locate_key(ffi, "userid", "key0-uid2", &key)); + assert_rnp_success(rnp_key_get_default_key(key, "encrypt", 0, &defkey)); + assert_non_null(defkey); + assert_rnp_success(rnp_key_unlock(defkey, "password")); + assert_rnp_success(rnp_decrypt(ffi, input, output)); + assert_rnp_success(rnp_key_lock(defkey)); + rnp_key_handle_destroy(key); + rnp_key_handle_destroy(defkey); + // cleanup + rnp_input_destroy(input); + rnp_output_destroy(output); + assert_string_equal(file_to_str("decrypted").c_str(), plaintext); + assert_int_equal(unlink("decrypted"), 0); + + /* decrypt (unlocked second key, pass provider should not be called) */ + assert_rnp_success(rnp_ffi_set_pass_provider(ffi, first_key_password_provider, NULL)); + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_rnp_success(rnp_locate_key(ffi, "userid", "key1-uid1", &key)); + assert_rnp_success(rnp_key_get_default_key(key, "encrypt", 0, &defkey)); + assert_non_null(defkey); + assert_rnp_success(rnp_key_unlock(defkey, "password")); + assert_rnp_success(rnp_decrypt(ffi, input, output)); + assert_rnp_success(rnp_key_lock(defkey)); + rnp_key_handle_destroy(key); + rnp_key_handle_destroy(defkey); + // cleanup + rnp_input_destroy(input); + rnp_output_destroy(output); + assert_string_equal(file_to_str("decrypted").c_str(), plaintext); + assert_int_equal(unlink("decrypted"), 0); + + // final cleanup + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_encrypt_pk_key_provider) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_op_encrypt_t op = NULL; + const char * plaintext = "data1"; + uint8_t * primary_sec_key_data = NULL; + size_t primary_sec_size = 0; + uint8_t * sub_sec_key_data = NULL; + size_t sub_sec_size = 0; + + /* first, let's generate some encrypted data */ + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_non_null(ffi); + // load our keyrings + assert_true( + load_keys_gpg(ffi, "data/keyrings/1/pubring.gpg", "data/keyrings/1/secring.gpg")); + // write out some data + str_to_file("plaintext", plaintext); + // create input+output + assert_rnp_success(rnp_input_from_path(&input, "plaintext")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "encrypted")); + assert_non_null(output); + // create encrypt operation + assert_rnp_success(rnp_op_encrypt_create(&op, ffi, input, output)); + // add recipient 1 + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "userid", "key0-uid2", &key)); + assert_non_null(key); + assert_rnp_success(rnp_op_encrypt_add_recipient(op, key)); + // cleanup + assert_rnp_success(rnp_key_handle_destroy(key)); + key = NULL; + // add recipient 2 + assert_rnp_success(rnp_locate_key(ffi, "userid", "key1-uid1", &key)); + assert_non_null(key); + assert_rnp_success(rnp_op_encrypt_add_recipient(op, key)); + // save the primary key data for later + assert_rnp_success(rnp_get_secret_key_data(key, &primary_sec_key_data, &primary_sec_size)); + assert_non_null(primary_sec_key_data); + assert_rnp_success(rnp_key_handle_destroy(key)); + key = NULL; + // save the appropriate encrypting subkey for the key provider to use during decryption + // later + assert_rnp_success(rnp_locate_key(ffi, "keyid", "8A05B89FAD5ADED1", &key)); + assert_non_null(key); + assert_rnp_success(rnp_get_secret_key_data(key, &sub_sec_key_data, &sub_sec_size)); + assert_non_null(sub_sec_key_data); + // cleanup + assert_rnp_success(rnp_key_handle_destroy(key)); + key = NULL; + // set the data encryption cipher + if (cast5_enabled()) { + assert_rnp_success(rnp_op_encrypt_set_cipher(op, "CAST5")); + } else { + assert_rnp_failure(rnp_op_encrypt_set_cipher(op, "CAST5")); + assert_rnp_success(rnp_op_encrypt_set_cipher(op, "AES256")); + } + // execute the operation + assert_rnp_success(rnp_op_encrypt_execute(op)); + // make sure the output file was created + assert_true(rnp_file_exists("encrypted")); + // cleanup + assert_rnp_success(rnp_input_destroy(input)); + input = NULL; + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_rnp_success(rnp_op_encrypt_destroy(op)); + op = NULL; + assert_rnp_success(rnp_ffi_destroy(ffi)); + ffi = NULL; + + /* decrypt */ + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + // load the primary + input = NULL; + assert_rnp_success( + rnp_input_from_memory(&input, primary_sec_key_data, primary_sec_size, true)); + assert_non_null(input); + assert_rnp_success(rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_SECRET_KEYS)); + rnp_input_destroy(input); + input = NULL; + + // decrypt (no key to decrypt, should fail) + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_non_null(output); + assert_int_equal(RNP_ERROR_NO_SUITABLE_KEY, rnp_decrypt(ffi, input, output)); + // cleanup + rnp_input_destroy(input); + input = NULL; + rnp_output_destroy(output); + output = NULL; + + // key_data key_data_size secret keyid grip userids + const key_tbl_t keydb[] = { + {sub_sec_key_data, sub_sec_size, true, "8A05B89FAD5ADED1", NULL, {NULL}}, {0}}; + + // decrypt + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_non_null(output); + assert_rnp_success(rnp_ffi_set_key_provider(ffi, tbl_getkeycb, (void *) keydb)); + assert_rnp_success(rnp_decrypt(ffi, input, output)); + // cleanup + rnp_input_destroy(input); + input = NULL; + rnp_output_destroy(output); + output = NULL; + // compare the decrypted file + assert_string_equal(file_to_str("decrypted").c_str(), plaintext); + // final cleanup + rnp_ffi_destroy(ffi); + free(sub_sec_key_data); + free(primary_sec_key_data); +} + +TEST_F(rnp_tests, test_ffi_encrypt_and_sign) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_op_encrypt_t op = NULL; + rnp_op_sign_signature_t signsig = NULL; + const char * plaintext = "data1"; + rnp_key_handle_t key = NULL; + const uint32_t issued = 1516211899; // Unix epoch, nowish + const uint32_t expires = 1000000000; // expires later + const uint32_t issued2 = 1516211900; // Unix epoch, nowish + const uint32_t expires2 = 2000000000; // expires later + + // setup FFI + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + + // load our keyrings + assert_true( + load_keys_gpg(ffi, "data/keyrings/1/pubring.gpg", "data/keyrings/1/secring.gpg")); + + // write out some data + str_to_file("plaintext", plaintext); + // create input+output + assert_rnp_success(rnp_input_from_path(&input, "plaintext")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "encrypted")); + assert_non_null(output); + // create encrypt operation + assert_rnp_success(rnp_op_encrypt_create(&op, ffi, input, output)); + // add recipients + assert_rnp_success(rnp_locate_key(ffi, "userid", "key0-uid2", &key)); + assert_rnp_success(rnp_op_encrypt_add_recipient(op, key)); + rnp_key_handle_destroy(key); + key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "userid", "key1-uid1", &key)); + assert_rnp_success(rnp_op_encrypt_add_recipient(op, key)); + rnp_key_handle_destroy(key); + key = NULL; + // set the data encryption cipher + if (cast5_enabled()) { + assert_rnp_success(rnp_op_encrypt_set_cipher(op, "CAST5")); + } else { + assert_rnp_failure(rnp_op_encrypt_set_cipher(op, "CAST5")); + assert_rnp_success(rnp_op_encrypt_set_cipher(op, "AES256")); + } + // enable armoring + assert_rnp_failure(rnp_op_encrypt_set_armor(NULL, true)); + assert_rnp_success(rnp_op_encrypt_set_armor(op, true)); + // add signature + assert_rnp_failure(rnp_op_encrypt_set_hash(NULL, "SHA256")); + assert_rnp_failure(rnp_op_encrypt_set_hash(op, NULL)); + assert_rnp_failure(rnp_op_encrypt_set_hash(op, "WRONG")); + assert_rnp_success(rnp_op_encrypt_set_hash(op, "SHA1")); + assert_rnp_failure(rnp_op_encrypt_set_creation_time(NULL, 0)); + assert_rnp_success(rnp_op_encrypt_set_creation_time(op, 0)); + assert_rnp_failure(rnp_op_encrypt_set_expiration_time(NULL, 0)); + assert_rnp_success(rnp_op_encrypt_set_expiration_time(op, 0)); + assert_rnp_success(rnp_locate_key(ffi, "userid", "key1-uid1", &key)); + assert_rnp_failure(rnp_op_encrypt_add_signature(NULL, key, NULL)); + assert_rnp_failure(rnp_op_encrypt_add_signature(op, NULL, NULL)); + assert_rnp_success(rnp_op_encrypt_add_signature(op, key, NULL)); + rnp_key_handle_destroy(key); + key = NULL; + // attempt to add signature from the public key + assert_true(import_pub_keys(ffi, "data/test_stream_key_load/ecc-p256-pub.asc")); + assert_rnp_success(rnp_locate_key(ffi, "userid", "ecc-p256", &key)); + assert_rnp_failure(rnp_op_encrypt_add_signature(op, key, &signsig)); + rnp_key_handle_destroy(key); + key = NULL; + // attempt to add signature by the offline secret key + assert_true( + import_pub_keys(ffi, "data/test_key_edge_cases/alice-s2k-101-no-sign-sub.pgp")); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "0451409669ffde3c", &key)); + assert_rnp_failure(rnp_op_encrypt_add_signature(op, key, &signsig)); + rnp_key_handle_destroy(key); + key = NULL; + // add second signature with different hash/issued/expiration + assert_rnp_success(rnp_locate_key(ffi, "userid", "key1-uid2", &key)); + assert_rnp_success(rnp_op_encrypt_add_signature(op, key, &signsig)); + assert_rnp_success(rnp_op_sign_signature_set_creation_time(signsig, issued2)); + assert_rnp_success(rnp_op_sign_signature_set_expiration_time(signsig, expires2)); + assert_rnp_failure(rnp_op_sign_signature_set_hash(signsig, NULL)); + assert_rnp_failure(rnp_op_sign_signature_set_hash(NULL, "SHA512")); + assert_rnp_failure(rnp_op_sign_signature_set_hash(signsig, "UNKNOWN")); + assert_rnp_success(rnp_op_sign_signature_set_hash(signsig, "SHA512")); + rnp_key_handle_destroy(key); + key = NULL; + // set default sig parameters after the signature is added - those should be picked up + assert_rnp_success(rnp_op_encrypt_set_hash(op, "SHA256")); + assert_rnp_success(rnp_op_encrypt_set_creation_time(op, issued)); + assert_rnp_success(rnp_op_encrypt_set_expiration_time(op, expires)); + // execute the operation + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + assert_rnp_success(rnp_op_encrypt_execute(op)); + + // make sure the output file was created + assert_true(rnp_file_exists("encrypted")); + + // check whether keys are locked + rnp_identifier_iterator_t it = NULL; + assert_rnp_success(rnp_identifier_iterator_create(ffi, &it, "fingerprint")); + const char *fprint = NULL; + while (!rnp_identifier_iterator_next(it, &fprint)) { + if (!fprint) { + break; + } + SCOPED_TRACE(fprint); + rnp_key_handle_t skey = NULL; + assert_rnp_success(rnp_locate_key(ffi, "fingerprint", fprint, &skey)); + bool secret = true; + assert_rnp_success(rnp_key_have_secret(skey, &secret)); + if (secret) { + bool locked = false; + assert_rnp_success(rnp_key_is_locked(skey, &locked)); + assert_true(locked); + } + rnp_key_handle_destroy(skey); + } + rnp_identifier_iterator_destroy(it); + + // cleanup + assert_rnp_success(rnp_input_destroy(input)); + input = NULL; + assert_rnp_success(rnp_output_destroy(output)); + output = NULL; + assert_rnp_success(rnp_op_encrypt_destroy(op)); + op = NULL; + + /* decrypt */ + + // decrypt (no pass provider, should fail) + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_non_null(output); + assert_rnp_success(rnp_ffi_set_pass_provider(ffi, NULL, NULL)); + assert_rnp_failure(rnp_decrypt(ffi, input, output)); + // cleanup + rnp_input_destroy(input); + input = NULL; + rnp_output_destroy(output); + output = NULL; + + // decrypt (wrong pass, should fail) + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_non_null(output); + const char *pass = "wrong1"; + assert_rnp_success(rnp_ffi_set_pass_provider(ffi, getpasscb_once, &pass)); + assert_rnp_failure(rnp_decrypt(ffi, input, output)); + // cleanup + rnp_input_destroy(input); + input = NULL; + rnp_output_destroy(output); + output = NULL; + + // decrypt + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_non_null(output); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + assert_rnp_success(rnp_decrypt(ffi, input, output)); + // cleanup + rnp_input_destroy(input); + input = NULL; + rnp_output_destroy(output); + output = NULL; + // compare the decrypted file + assert_string_equal(file_to_str("decrypted").c_str(), plaintext); + // verify and check signatures + rnp_op_verify_t verify; + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_non_null(input); + assert_rnp_success(rnp_output_to_path(&output, "verified")); + assert_non_null(output); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + // check signatures + rnp_op_verify_signature_t sig; + size_t sig_count; + uint32_t sig_create; + uint32_t sig_expires; + char * hname = NULL; + + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sig_count)); + assert_int_equal(sig_count, 2); + // signature 1 + assert_rnp_success(rnp_op_verify_get_signature_at(verify, 0, &sig)); + assert_rnp_success(rnp_op_verify_signature_get_status(sig)); + assert_rnp_success(rnp_op_verify_signature_get_times(sig, &sig_create, &sig_expires)); + assert_int_equal(sig_create, issued); + assert_int_equal(sig_expires, expires); + assert_rnp_success(rnp_op_verify_signature_get_hash(sig, &hname)); + assert_string_equal(hname, "SHA256"); + rnp_buffer_destroy(hname); + hname = NULL; + // signature 2 + assert_rnp_success(rnp_op_verify_get_signature_at(verify, 1, &sig)); + assert_rnp_success(rnp_op_verify_signature_get_status(sig)); + assert_rnp_success(rnp_op_verify_signature_get_times(sig, &sig_create, &sig_expires)); + assert_int_equal(sig_create, issued2); + assert_int_equal(sig_expires, expires2); + assert_rnp_success(rnp_op_verify_signature_get_hash(sig, &hname)); + assert_string_equal(hname, "SHA512"); + rnp_buffer_destroy(hname); + hname = NULL; + // make sure keys are locked + assert_rnp_success(rnp_identifier_iterator_create(ffi, &it, "fingerprint")); + while (!rnp_identifier_iterator_next(it, &fprint)) { + if (!fprint) { + break; + } + SCOPED_TRACE(fprint); + rnp_key_handle_t skey = NULL; + assert_rnp_success(rnp_locate_key(ffi, "fingerprint", fprint, &skey)); + bool secret = true; + assert_rnp_success(rnp_key_have_secret(skey, &secret)); + if (secret) { + bool locked = false; + assert_rnp_success(rnp_key_is_locked(skey, &locked)); + assert_true(locked); + } + rnp_key_handle_destroy(skey); + } + rnp_identifier_iterator_destroy(it); + // cleanup + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + input = NULL; + rnp_output_destroy(output); + output = NULL; + // compare the decrypted file + assert_string_equal(file_to_str("verified").c_str(), plaintext); + // final cleanup + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_encrypt_pk_subkey_selection) +{ + rnp_ffi_t ffi = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_op_encrypt_t op = NULL; + const char * plaintext = "data1"; + + /* check whether a latest subkey is selected for encryption */ + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + + /* case 1: three encryption subkeys, second expired, third has later creation time */ + assert_true(load_keys_gpg(ffi, "data/test_stream_key_load/key0-sub02.pgp")); + + assert_rnp_success( + rnp_input_from_memory(&input, (uint8_t *) plaintext, strlen(plaintext), false)); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + /* create encrypt operation, add recipient and execute */ + assert_rnp_success(rnp_op_encrypt_create(&op, ffi, input, output)); + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "userid", "key0-uid0", &key)); + assert_rnp_success(rnp_op_encrypt_add_recipient(op, key)); + rnp_key_handle_destroy(key); + assert_rnp_success(rnp_op_encrypt_execute(op)); + /* get output */ + uint8_t *buf = NULL; + size_t len = 0; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, true)); + assert_true(buf && len); + rnp_input_destroy(input); + rnp_output_destroy(output); + rnp_op_encrypt_destroy(op); + /* decrypt */ + assert_true(load_keys_gpg(ffi, "", "data/keyrings/1/secring.gpg")); + assert_rnp_success(rnp_input_from_memory(&input, buf, len, true)); + rnp_buffer_destroy(buf); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + + rnp_op_verify_t verify = NULL; + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + + /* check whether we used correct subkey */ + size_t count = 0; + assert_rnp_success(rnp_op_verify_get_recipient_count(verify, &count)); + assert_int_equal(count, 1); + rnp_recipient_handle_t recipient = NULL; + assert_rnp_success(rnp_op_verify_get_recipient_at(verify, 0, &recipient)); + assert_non_null(recipient); + char *keyid = NULL; + assert_rnp_success(rnp_recipient_get_keyid(recipient, &keyid)); + assert_non_null(keyid); + assert_string_equal(keyid, "8A05B89FAD5ADED1"); + rnp_buffer_destroy(keyid); + + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* case 2: only subkeys 1-2, make sure that latest but expired subkey is not selected */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET)); + assert_true(load_keys_gpg(ffi, "data/test_stream_key_load/key0-sub01.pgp")); + assert_rnp_success( + rnp_input_from_memory(&input, (uint8_t *) plaintext, strlen(plaintext), false)); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + /* create encrypt operation, add recipient and execute */ + assert_rnp_success(rnp_op_encrypt_create(&op, ffi, input, output)); + key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "7bc6709b15c23a4a", &key)); + assert_rnp_success(rnp_op_encrypt_add_recipient(op, key)); + rnp_key_handle_destroy(key); + assert_rnp_success(rnp_op_encrypt_execute(op)); + /* get output */ + buf = NULL; + len = 0; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, true)); + assert_true(buf && len); + rnp_input_destroy(input); + rnp_output_destroy(output); + rnp_op_encrypt_destroy(op); + /* decrypt */ + assert_true(load_keys_gpg(ffi, "", "data/keyrings/1/secring.gpg")); + assert_rnp_success(rnp_input_from_memory(&input, buf, len, true)); + rnp_buffer_destroy(buf); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + + verify = NULL; + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + + /* check whether we used correct subkey */ + count = 0; + assert_rnp_success(rnp_op_verify_get_recipient_count(verify, &count)); + assert_int_equal(count, 1); + recipient = NULL; + assert_rnp_success(rnp_op_verify_get_recipient_at(verify, 0, &recipient)); + assert_non_null(recipient); + keyid = NULL; + assert_rnp_success(rnp_recipient_get_keyid(recipient, &keyid)); + assert_non_null(keyid); + assert_string_equal(keyid, "1ED63EE56FADC34D"); + rnp_buffer_destroy(keyid); + + rnp_op_verify_destroy(verify); + rnp_input_destroy(input); + rnp_output_destroy(output); + + /* case 3: only expired subkey, make sure encryption operation fails */ + assert_rnp_success(rnp_unload_keys(ffi, RNP_KEY_UNLOAD_PUBLIC | RNP_KEY_UNLOAD_SECRET)); + assert_true(load_keys_gpg(ffi, "data/test_stream_key_load/key0-sub1.pgp")); + + assert_rnp_success( + rnp_input_from_memory(&input, (uint8_t *) plaintext, strlen(plaintext), false)); + assert_rnp_success(rnp_output_to_memory(&output, 0)); + /* create encrypt operation, add recipient and execute */ + assert_rnp_success(rnp_op_encrypt_create(&op, ffi, input, output)); + key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "7bc6709b15c23a4a", &key)); + assert_int_equal(rnp_op_encrypt_add_recipient(op, key), RNP_ERROR_NO_SUITABLE_KEY); + rnp_key_handle_destroy(key); + assert_rnp_failure(rnp_op_encrypt_execute(op)); + rnp_op_encrypt_destroy(op); + rnp_input_destroy(input); + rnp_output_destroy(output); + + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_decrypt_small_rsa) +{ + rnp_ffi_t ffi = NULL; + const char *plaintext = "data1"; + + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_true(import_all_keys(ffi, "data/test_key_validity/rsa_key_small_sig-sec.asc")); + rnp_input_t input = NULL; + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/data.enc.small-rsa")); + rnp_output_t output = NULL; + assert_rnp_success(rnp_output_to_memory(&output, 0)); + assert_rnp_success(rnp_decrypt(ffi, input, output)); + size_t len = 0; + uint8_t *buf = NULL; + assert_rnp_success(rnp_output_memory_get_buf(output, &buf, &len, false)); + assert_int_equal(len, 5); + assert_int_equal(memcmp(plaintext, buf, 5), 0); + assert_rnp_success(rnp_input_destroy(input)); + assert_rnp_success(rnp_output_destroy(output)); + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_decrypt_small_eg) +{ + /* make sure unlock and decrypt fails with invalid key */ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_true( + import_all_keys(ffi, "data/test_key_edge_cases/key-eg-small-subgroup-sec.pgp")); + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "keyid", "3b8dda452b9f69b4", &key)); + assert_non_null(key); + /* key is not encrypted */ + assert_rnp_success(rnp_key_unlock(key, NULL)); + rnp_key_handle_destroy(key); + rnp_input_t input = NULL; + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-eg-bad")); + rnp_output_t output = NULL; + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_failure(rnp_decrypt(ffi, input, output)); + assert_rnp_success(rnp_input_destroy(input)); + assert_rnp_success(rnp_output_destroy(output)); + rnp_ffi_destroy(ffi); + /* make sure unlock and decrypt fails with invalid encrypted key */ + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + assert_true( + import_all_keys(ffi, "data/test_key_edge_cases/key-eg-small-subgroup-sec-enc.pgp")); + assert_rnp_success(rnp_locate_key(ffi, "keyid", "3b072c3bb2d1a8b2", &key)); + assert_non_null(key); + assert_rnp_success(rnp_key_unlock(key, "password")); + assert_rnp_success(rnp_key_lock(key)); + rnp_key_handle_destroy(key); + assert_rnp_success( + rnp_input_from_path(&input, "data/test_messages/message.txt.enc-eg-bad2")); + assert_rnp_success(rnp_output_to_null(&output)); + assert_rnp_failure(rnp_decrypt(ffi, input, output)); + assert_rnp_success(rnp_input_destroy(input)); + assert_rnp_success(rnp_output_destroy(output)); + rnp_ffi_destroy(ffi); +} + +TEST_F(rnp_tests, test_ffi_encrypt_no_wrap) +{ + rnp_ffi_t ffi = NULL; + assert_rnp_success(rnp_ffi_create(&ffi, "GPG", "GPG")); + assert_true( + load_keys_gpg(ffi, "data/keyrings/1/pubring.gpg", "data/keyrings/1/secring.gpg")); + + rnp_input_t input = NULL; + assert_rnp_success(rnp_input_from_path(&input, "data/test_messages/message.txt.signed")); + rnp_output_t output = NULL; + assert_rnp_success(rnp_output_to_path(&output, "encrypted")); + rnp_op_encrypt_t op = NULL; + assert_rnp_success(rnp_op_encrypt_create(&op, ffi, input, output)); + rnp_key_handle_t key = NULL; + assert_rnp_success(rnp_locate_key(ffi, "userid", "key0-uid2", &key)); + assert_rnp_success(rnp_op_encrypt_add_recipient(op, key)); + rnp_key_handle_destroy(key); + /* set nowrap flag */ + assert_rnp_failure(rnp_op_encrypt_set_flags(NULL, RNP_ENCRYPT_NOWRAP)); + assert_rnp_failure(rnp_op_encrypt_set_flags(op, 17)); + assert_rnp_success(rnp_op_encrypt_set_flags(op, RNP_ENCRYPT_NOWRAP)); + assert_rnp_success(rnp_op_encrypt_execute(op)); + + assert_rnp_success(rnp_input_destroy(input)); + assert_rnp_success(rnp_output_destroy(output)); + assert_rnp_success(rnp_op_encrypt_destroy(op)); + + /* decrypt via rnp_decrypt() */ + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_rnp_success(rnp_output_to_path(&output, "decrypted")); + assert_rnp_success( + rnp_ffi_set_pass_provider(ffi, ffi_string_password_provider, (void *) "password")); + assert_rnp_success(rnp_decrypt(ffi, input, output)); + rnp_input_destroy(input); + rnp_output_destroy(output); + assert_string_equal(file_to_str("decrypted").c_str(), + file_to_str("data/test_messages/message.txt").c_str()); + unlink("decrypted"); + + /* decrypt and verify signatures */ + rnp_op_verify_t verify; + assert_rnp_success(rnp_input_from_path(&input, "encrypted")); + assert_rnp_success(rnp_output_to_path(&output, "verified")); + assert_rnp_success(rnp_op_verify_create(&verify, ffi, input, output)); + assert_rnp_success(rnp_op_verify_execute(verify)); + /* check signature */ + rnp_op_verify_signature_t sig; + size_t sig_count; + assert_rnp_success(rnp_op_verify_get_signature_count(verify, &sig_count)); + assert_int_equal(sig_count, 1); + assert_rnp_success(rnp_op_verify_get_signature_at(verify, 0, &sig)); + assert_rnp_success(rnp_op_verify_signature_get_status(sig)); + assert_rnp_success(rnp_input_destroy(input)); + assert_rnp_success(rnp_output_destroy(output)); + rnp_op_verify_destroy(verify); + assert_string_equal(file_to_str("verified").c_str(), + file_to_str("data/test_messages/message.txt").c_str()); + unlink("verified"); + + // final cleanup + rnp_ffi_destroy(ffi); +} |