summaryrefslogtreecommitdiffstats
path: root/src/examples
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/examples/CMakeLists.txt140
-rw-r--r--src/examples/README.md5
-rw-r--r--src/examples/decrypt.c138
-rw-r--r--src/examples/dump.c163
-rw-r--r--src/examples/encrypt.c133
-rw-r--r--src/examples/generate.c330
-rw-r--r--src/examples/sign.c168
-rw-r--r--src/examples/verify.c165
8 files changed, 1242 insertions, 0 deletions
diff --git a/src/examples/CMakeLists.txt b/src/examples/CMakeLists.txt
new file mode 100644
index 0000000..8cf1e72
--- /dev/null
+++ b/src/examples/CMakeLists.txt
@@ -0,0 +1,140 @@
+# Copyright (c) 2018-2020 Ribose Inc.
+# 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 HOLDERS 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.
+
+if(MSVC)
+ # remove extra ${Configuration} subfolder
+ set(ArchiveOutputDir ${CMAKE_BINARY_DIR}\\src\\examples)
+ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${ArchiveOutputDir})
+ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL ${ArchiveOutputDir})
+ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${ArchiveOutputDir})
+ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO ${ArchiveOutputDir})
+
+ set(RuntimeOutputDir ${CMAKE_BINARY_DIR}\\src\\examples)
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${RuntimeOutputDir})
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${RuntimeOutputDir})
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${RuntimeOutputDir})
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${RuntimeOutputDir})
+
+ find_path(GETOPT_INCLUDE_DIR
+ NAMES getopt.h
+ )
+ find_library(GETOPT_LIBRARY
+ NAMES getopt
+ )
+endif()
+
+add_executable(generate generate.c)
+
+target_include_directories(generate
+ PRIVATE
+ "${PROJECT_SOURCE_DIR}/src"
+ "${PROJECT_SOURCE_DIR}/src/lib"
+)
+
+target_link_libraries(generate
+ PRIVATE
+ librnp
+)
+
+add_executable(encrypt encrypt.c)
+
+target_include_directories(encrypt
+ PRIVATE
+ "${PROJECT_SOURCE_DIR}/src"
+ "${PROJECT_SOURCE_DIR}/src/lib"
+)
+
+target_link_libraries(encrypt
+ PRIVATE
+ librnp
+)
+
+add_executable(decrypt decrypt.c)
+
+target_include_directories(decrypt
+ PRIVATE
+ "${PROJECT_SOURCE_DIR}/src"
+ "${PROJECT_SOURCE_DIR}/src/lib"
+)
+
+target_link_libraries(decrypt
+ PRIVATE
+ librnp
+)
+
+add_executable(sign sign.c)
+
+target_include_directories(sign
+ PRIVATE
+ "${PROJECT_SOURCE_DIR}/src"
+ "${PROJECT_SOURCE_DIR}/src/lib"
+)
+
+target_link_libraries(sign
+ PRIVATE
+ librnp
+)
+
+add_executable(verify verify.c)
+
+target_include_directories(verify
+ PRIVATE
+ "${PROJECT_SOURCE_DIR}/src"
+ "${PROJECT_SOURCE_DIR}/src/lib"
+)
+
+target_link_libraries(verify
+ PRIVATE
+ librnp
+)
+
+add_executable(dump dump.c)
+
+target_include_directories(dump
+ PRIVATE
+ "${PROJECT_SOURCE_DIR}/src"
+ "${PROJECT_SOURCE_DIR}/src/lib"
+)
+
+target_link_libraries(dump
+ PRIVATE
+ librnp
+)
+
+if(MSVC)
+ target_include_directories(dump
+ PRIVATE
+ "${GETOPT_INCLUDE_DIR}"
+ )
+ target_link_libraries(dump
+ PRIVATE
+ "${GETOPT_LIBRARY}"
+ )
+endif()
+
+if (ENABLE_SANITIZERS)
+ foreach(tgt generate encrypt decrypt sign verify dump)
+ set_target_properties(${tgt} PROPERTIES LINKER_LANGUAGE CXX)
+ endforeach()
+endif()
diff --git a/src/examples/README.md b/src/examples/README.md
new file mode 100644
index 0000000..84bdc2e
--- /dev/null
+++ b/src/examples/README.md
@@ -0,0 +1,5 @@
+# RNP C API usage samples
+
+This folder includes examples of RNP library usage for developers.
+
+See [Using the RNP C API](/docs/c-usage.adoc) for more details.
diff --git a/src/examples/decrypt.c b/src/examples/decrypt.c
new file mode 100644
index 0000000..5454a4b
--- /dev/null
+++ b/src/examples/decrypt.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2018, [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/rnp.h>
+#include <string.h>
+
+#define RNP_SUCCESS 0
+
+/* sample pass provider implementation, which always return 'password' for key decryption and
+ * 'encpassword' when password is needed for file decryption. You may ask for password via
+ * stdin, or choose password based on key properties, whatever else */
+static bool
+example_pass_provider(rnp_ffi_t ffi,
+ void * app_ctx,
+ rnp_key_handle_t key,
+ const char * pgp_context,
+ char buf[],
+ size_t buf_len)
+{
+ if (!strcmp(pgp_context, "decrypt (symmetric)")) {
+ strncpy(buf, "encpassword", buf_len);
+ return true;
+ }
+ if (!strcmp(pgp_context, "decrypt")) {
+ strncpy(buf, "password", buf_len);
+ return true;
+ }
+
+ return false;
+}
+
+static int
+ffi_decrypt(bool usekeys)
+{
+ rnp_ffi_t ffi = NULL;
+ rnp_input_t keyfile = NULL;
+ rnp_input_t input = NULL;
+ rnp_output_t output = NULL;
+ uint8_t * buf = NULL;
+ size_t buf_len = 0;
+ int result = 1;
+
+ /* initialize FFI object */
+ if (rnp_ffi_create(&ffi, "GPG", "GPG") != RNP_SUCCESS) {
+ return result;
+ }
+
+ /* check whether we want to use key or password for decryption */
+ if (usekeys) {
+ /* load secret keyring, as it is required for public-key decryption. However, you may
+ * need to load public keyring as well to validate key's signatures. */
+ if (rnp_input_from_path(&keyfile, "secring.pgp") != RNP_SUCCESS) {
+ fprintf(stdout, "failed to open secring.pgp. Did you run ./generate sample?\n");
+ goto finish;
+ }
+
+ /* we may use RNP_LOAD_SAVE_SECRET_KEYS | RNP_LOAD_SAVE_PUBLIC_KEYS as well*/
+ if (rnp_load_keys(ffi, "GPG", keyfile, RNP_LOAD_SAVE_SECRET_KEYS) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to read secring.pgp\n");
+ goto finish;
+ }
+ rnp_input_destroy(keyfile);
+ keyfile = NULL;
+ }
+
+ /* set the password provider */
+ rnp_ffi_set_pass_provider(ffi, example_pass_provider, NULL);
+
+ /* create file input and memory output objects for the encrypted message and decrypted
+ * message */
+ if (rnp_input_from_path(&input, "encrypted.asc") != RNP_SUCCESS) {
+ fprintf(stdout, "failed to create input object\n");
+ goto finish;
+ }
+
+ if (rnp_output_to_memory(&output, 0) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to create output object\n");
+ goto finish;
+ }
+
+ if (rnp_decrypt(ffi, input, output) != RNP_SUCCESS) {
+ fprintf(stdout, "public-key decryption failed\n");
+ goto finish;
+ }
+
+ /* get the decrypted message from the output structure */
+ if (rnp_output_memory_get_buf(output, &buf, &buf_len, false) != RNP_SUCCESS) {
+ goto finish;
+ }
+ fprintf(stdout,
+ "Decrypted message (%s):\n%.*s\n",
+ usekeys ? "with key" : "with password",
+ (int) buf_len,
+ buf);
+
+ result = 0;
+finish:
+ rnp_input_destroy(keyfile);
+ rnp_input_destroy(input);
+ rnp_output_destroy(output);
+ rnp_ffi_destroy(ffi);
+ return result;
+}
+
+int
+main(int argc, char **argv)
+{
+ int res;
+ res = ffi_decrypt(true);
+ if (res) {
+ return res;
+ }
+ res = ffi_decrypt(false);
+ return res;
+} \ No newline at end of file
diff --git a/src/examples/dump.c b/src/examples/dump.c
new file mode 100644
index 0000000..dc24644
--- /dev/null
+++ b/src/examples/dump.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2019, [Ribose Inc](https://www.ribose.com).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#ifdef _MSC_VER
+#include "uniwin.h"
+#else
+#include <unistd.h> /* getopt() */
+#include <libgen.h> /* basename() */
+#endif
+#include <rnp/rnp.h>
+
+#define PFX "dump: "
+
+static void
+print_usage(char *program_name)
+{
+ const char *program_basename;
+#ifdef _MSC_VER
+ char fname[_MAX_FNAME];
+ program_basename = fname;
+ _splitpath_s(program_name, NULL, 0, NULL, 0, fname, _MAX_FNAME, NULL, 0);
+#else
+ program_basename = basename(program_name);
+#endif
+
+ fprintf(stderr,
+ "dump: " PFX "Program dumps PGP packets. \n\nUsage:\n"
+ "\t%s [-d|-h] [input.pgp]\n"
+ "\t -d : indicates whether to print packet content. Data is represented as hex\n"
+ "\t -m : dump mpi values\n"
+ "\t -g : dump key fingerprints and grips\n"
+ "\t -j : JSON output\n"
+ "\t -h : prints help and exists\n",
+ program_basename);
+}
+
+static bool
+stdin_reader(void *app_ctx, void *buf, size_t len, size_t *readres)
+{
+ ssize_t res = read(STDIN_FILENO, buf, len);
+ if (res < 0) {
+ return false;
+ }
+ *readres = res;
+ return true;
+}
+
+static bool
+stdout_writer(void *app_ctx, const void *buf, size_t len)
+{
+ ssize_t wlen = write(STDOUT_FILENO, buf, len);
+ return (wlen >= 0) && (size_t) wlen == len;
+}
+
+int
+main(int argc, char *const argv[])
+{
+ char * input_file = NULL;
+ uint32_t flags = 0;
+ uint32_t jflags = 0;
+ bool json = false;
+
+ /* Parse command line options:
+ -i input_file [mandatory]: specifies name of the file with PGP packets
+ -d : indicates whether to dump whole packet content
+ -m : dump mpi contents
+ -g : dump key grips and fingerprints
+ -j : JSON output
+ -h : prints help and exists
+ */
+ int opt = 0;
+ while ((opt = getopt(argc, argv, "dmgjh")) != -1) {
+ switch (opt) {
+ case 'd':
+ flags |= RNP_DUMP_RAW;
+ jflags |= RNP_JSON_DUMP_RAW;
+ break;
+ case 'm':
+ flags |= RNP_DUMP_MPI;
+ jflags |= RNP_JSON_DUMP_MPI;
+ break;
+ case 'g':
+ flags |= RNP_DUMP_GRIP;
+ jflags |= RNP_JSON_DUMP_GRIP;
+ break;
+ case 'j':
+ json = true;
+ break;
+ default:
+ print_usage(argv[0]);
+ return 1;
+ }
+ }
+
+ /* Check whether we have input file */
+ if (optind < argc) {
+ input_file = argv[optind];
+ }
+
+ rnp_input_t input = NULL;
+ rnp_result_t ret = 0;
+ if (input_file) {
+ ret = rnp_input_from_path(&input, input_file);
+ } else {
+ ret = rnp_input_from_callback(&input, stdin_reader, NULL, NULL);
+ }
+ if (ret) {
+ fprintf(stderr, "failed to open source: error 0x%x\n", (int) ret);
+ return 1;
+ }
+
+ if (!json) {
+ rnp_output_t output = NULL;
+ ret = rnp_output_to_callback(&output, stdout_writer, NULL, NULL);
+ if (ret) {
+ fprintf(stderr, "failed to open stdout: error 0x%x\n", (int) ret);
+ rnp_input_destroy(input);
+ return 1;
+ }
+ ret = rnp_dump_packets_to_output(input, output, flags);
+ rnp_output_destroy(output);
+ } else {
+ char *json = NULL;
+ ret = rnp_dump_packets_to_json(input, jflags, &json);
+ if (!ret) {
+ fprintf(stdout, "%s\n", json);
+ }
+ rnp_buffer_destroy(json);
+ }
+ rnp_input_destroy(input);
+
+ /* Inform in case of error occurred during parsing */
+ if (ret) {
+ fprintf(stderr, "Operation failed [error code: 0x%X]\n", (int) ret);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/examples/encrypt.c b/src/examples/encrypt.c
new file mode 100644
index 0000000..0b07fd4
--- /dev/null
+++ b/src/examples/encrypt.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2018, [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 <string.h>
+#include <time.h>
+#include <rnp/rnp.h>
+
+#define RNP_SUCCESS 0
+
+static int
+ffi_encrypt()
+{
+ rnp_ffi_t ffi = NULL;
+ rnp_op_encrypt_t encrypt = NULL;
+ rnp_key_handle_t key = NULL;
+ rnp_input_t keyfile = NULL;
+ rnp_input_t input = NULL;
+ rnp_output_t output = NULL;
+ const char * message = "RNP encryption sample message";
+ int result = 1;
+
+ /* initialize FFI object */
+ if (rnp_ffi_create(&ffi, "GPG", "GPG") != RNP_SUCCESS) {
+ return result;
+ }
+
+ /* load public keyring - we do not need secret for encryption */
+ if (rnp_input_from_path(&keyfile, "pubring.pgp") != RNP_SUCCESS) {
+ fprintf(stdout, "failed to open pubring.pgp. Did you run ./generate sample?\n");
+ goto finish;
+ }
+
+ /* we may use RNP_LOAD_SAVE_SECRET_KEYS | RNP_LOAD_SAVE_PUBLIC_KEYS as well */
+ if (rnp_load_keys(ffi, "GPG", keyfile, RNP_LOAD_SAVE_PUBLIC_KEYS) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to read pubring.pgp\n");
+ goto finish;
+ }
+ rnp_input_destroy(keyfile);
+ keyfile = NULL;
+
+ /* create memory input and file output objects for the message and encrypted message */
+ if (rnp_input_from_memory(&input, (uint8_t *) message, strlen(message), false) !=
+ RNP_SUCCESS) {
+ fprintf(stdout, "failed to create input object\n");
+ goto finish;
+ }
+
+ if (rnp_output_to_path(&output, "encrypted.asc") != RNP_SUCCESS) {
+ fprintf(stdout, "failed to create output object\n");
+ goto finish;
+ }
+
+ /* create encryption operation */
+ if (rnp_op_encrypt_create(&encrypt, ffi, input, output) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to create encrypt operation\n");
+ goto finish;
+ }
+
+ /* setup encryption parameters */
+ rnp_op_encrypt_set_armor(encrypt, true);
+ rnp_op_encrypt_set_file_name(encrypt, "message.txt");
+ rnp_op_encrypt_set_file_mtime(encrypt, (uint32_t) time(NULL));
+ rnp_op_encrypt_set_compression(encrypt, "ZIP", 6);
+ rnp_op_encrypt_set_cipher(encrypt, RNP_ALGNAME_AES_256);
+ rnp_op_encrypt_set_aead(encrypt, "None");
+
+ /* locate recipient's key and add it to the operation context. While we search by userid
+ * (which is easier), you can search by keyid, fingerprint or grip. */
+ if (rnp_locate_key(ffi, "userid", "rsa@key", &key) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to locate recipient key rsa@key.\n");
+ goto finish;
+ }
+
+ if (rnp_op_encrypt_add_recipient(encrypt, key) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to add recipient\n");
+ goto finish;
+ }
+ rnp_key_handle_destroy(key);
+ key = NULL;
+
+ /* add encryption password as well */
+ if (rnp_op_encrypt_add_password(
+ encrypt, "encpassword", RNP_ALGNAME_SHA256, 0, RNP_ALGNAME_AES_256) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to add encryption password\n");
+ goto finish;
+ }
+
+ /* execute encryption operation */
+ if (rnp_op_encrypt_execute(encrypt) != RNP_SUCCESS) {
+ fprintf(stdout, "encryption failed\n");
+ goto finish;
+ }
+
+ fprintf(stdout, "Encryption succeeded. Encrypted message written to file encrypted.asc\n");
+ result = 0;
+finish:
+ rnp_op_encrypt_destroy(encrypt);
+ rnp_input_destroy(keyfile);
+ rnp_input_destroy(input);
+ rnp_output_destroy(output);
+ rnp_key_handle_destroy(key);
+ rnp_ffi_destroy(ffi);
+ return result;
+}
+
+int
+main(int argc, char **argv)
+{
+ return ffi_encrypt();
+}
diff --git a/src/examples/generate.c b/src/examples/generate.c
new file mode 100644
index 0000000..979e63e
--- /dev/null
+++ b/src/examples/generate.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2018, [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 <string.h>
+#include <rnp/rnp.h>
+
+#define RNP_SUCCESS 0
+
+/* RSA key JSON description. 31536000 = 1 year expiration, 15768000 = half year */
+const char *RSA_KEY_DESC = "{\
+ 'primary': {\
+ 'type': 'RSA',\
+ 'length': 2048,\
+ 'userid': 'rsa@key',\
+ 'expiration': 31536000,\
+ 'usage': ['sign'],\
+ 'protection': {\
+ 'cipher': 'AES256',\
+ 'hash': 'SHA256'\
+ }\
+ },\
+ 'sub': {\
+ 'type': 'RSA',\
+ 'length': 2048,\
+ 'expiration': 15768000,\
+ 'usage': ['encrypt'],\
+ 'protection': {\
+ 'cipher': 'AES256',\
+ 'hash': 'SHA256'\
+ }\
+ }\
+}";
+
+const char *CURVE_25519_KEY_DESC = "{\
+ 'primary': {\
+ 'type': 'EDDSA',\
+ 'userid': '25519@key',\
+ 'expiration': 0,\
+ 'usage': ['sign'],\
+ 'protection': {\
+ 'cipher': 'AES256',\
+ 'hash': 'SHA256'\
+ }\
+ },\
+ 'sub': {\
+ 'type': 'ECDH',\
+ 'curve': 'Curve25519',\
+ 'expiration': 15768000,\
+ 'usage': ['encrypt'],\
+ 'protection': {\
+ 'cipher': 'AES256',\
+ 'hash': 'SHA256'\
+ }\
+ }\
+}";
+
+/* basic pass provider implementation, which always return 'password' for key protection.
+You may ask for password via stdin, or choose password based on key properties, whatever else
+*/
+static bool
+example_pass_provider(rnp_ffi_t ffi,
+ void * app_ctx,
+ rnp_key_handle_t key,
+ const char * pgp_context,
+ char buf[],
+ size_t buf_len)
+{
+ if (strcmp(pgp_context, "protect")) {
+ return false;
+ }
+
+ strncpy(buf, "password", buf_len);
+ return true;
+}
+
+/* this simple helper function just prints armored key, searched by userid, to stdout */
+static bool
+ffi_print_key(rnp_ffi_t ffi, const char *uid, bool secret)
+{
+ rnp_output_t keydata = NULL;
+ rnp_key_handle_t key = NULL;
+ uint32_t flags = RNP_KEY_EXPORT_ARMORED | RNP_KEY_EXPORT_SUBKEYS;
+ uint8_t * buf = NULL;
+ size_t buf_len = 0;
+ bool result = false;
+
+ /* you may search for the key via userid, keyid, fingerprint, grip */
+ if (rnp_locate_key(ffi, "userid", uid, &key) != RNP_SUCCESS) {
+ return false;
+ }
+
+ if (!key) {
+ return false;
+ }
+
+ /* create in-memory output structure to later use buffer */
+ if (rnp_output_to_memory(&keydata, 0) != RNP_SUCCESS) {
+ goto finish;
+ }
+
+ flags = flags | (secret ? RNP_KEY_EXPORT_SECRET : RNP_KEY_EXPORT_PUBLIC);
+ if (rnp_key_export(key, keydata, flags) != RNP_SUCCESS) {
+ goto finish;
+ }
+
+ /* get key's contents from the output structure */
+ if (rnp_output_memory_get_buf(keydata, &buf, &buf_len, false) != RNP_SUCCESS) {
+ goto finish;
+ }
+ fprintf(stdout, "%.*s", (int) buf_len, buf);
+
+ result = true;
+finish:
+ rnp_key_handle_destroy(key);
+ rnp_output_destroy(keydata);
+ return result;
+}
+
+static bool
+ffi_export_key(rnp_ffi_t ffi, const char *uid, bool secret)
+{
+ rnp_output_t keyfile = NULL;
+ rnp_key_handle_t key = NULL;
+ uint32_t flags = RNP_KEY_EXPORT_ARMORED | RNP_KEY_EXPORT_SUBKEYS;
+ char filename[32] = {0};
+ char * keyid = NULL;
+ bool result = false;
+
+ /* you may search for the key via userid, keyid, fingerprint, grip */
+ if (rnp_locate_key(ffi, "userid", uid, &key) != RNP_SUCCESS) {
+ return false;
+ }
+
+ if (!key) {
+ return false;
+ }
+
+ /* get key's id and build filename */
+ if (rnp_key_get_keyid(key, &keyid) != RNP_SUCCESS) {
+ goto finish;
+ }
+ snprintf(filename, sizeof(filename), "key-%s-%s.asc", keyid, secret ? "sec" : "pub");
+ rnp_buffer_destroy(keyid);
+
+ /* create file output structure */
+ if (rnp_output_to_path(&keyfile, filename) != RNP_SUCCESS) {
+ goto finish;
+ }
+
+ flags = flags | (secret ? RNP_KEY_EXPORT_SECRET : RNP_KEY_EXPORT_PUBLIC);
+ if (rnp_key_export(key, keyfile, flags) != RNP_SUCCESS) {
+ goto finish;
+ }
+
+ result = true;
+finish:
+ rnp_key_handle_destroy(key);
+ rnp_output_destroy(keyfile);
+ return result;
+}
+
+/* this example function generates RSA/RSA and Eddsa/X25519 keypairs */
+static int
+ffi_generate_keys()
+{
+ rnp_ffi_t ffi = NULL;
+ rnp_output_t keyfile = NULL;
+ char * key_grips = NULL;
+ int result = 1;
+
+ /* initialize FFI object */
+ if (rnp_ffi_create(&ffi, "GPG", "GPG") != RNP_SUCCESS) {
+ return result;
+ }
+
+ /* set password provider */
+ if (rnp_ffi_set_pass_provider(ffi, example_pass_provider, NULL)) {
+ goto finish;
+ }
+
+ /* generate EDDSA/X25519 keypair */
+ if (rnp_generate_key_json(ffi, CURVE_25519_KEY_DESC, &key_grips) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to generate eddsa key\n");
+ goto finish;
+ }
+
+ fprintf(stdout, "Generated 25519 key/subkey:\n%s\n", key_grips);
+ /* destroying key_grips buffer is our obligation */
+ rnp_buffer_destroy(key_grips);
+ key_grips = NULL;
+
+ /* generate RSA keypair */
+ if (rnp_generate_key_json(ffi, RSA_KEY_DESC, &key_grips) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to generate rsa key\n");
+ goto finish;
+ }
+
+ fprintf(stdout, "Generated RSA key/subkey:\n%s\n", key_grips);
+ rnp_buffer_destroy(key_grips);
+ key_grips = NULL;
+
+ /* create file output object and save public keyring with generated keys, overwriting
+ * previous file if any. You may use rnp_output_to_memory() here as well. */
+ if (rnp_output_to_path(&keyfile, "pubring.pgp") != RNP_SUCCESS) {
+ fprintf(stdout, "failed to initialize pubring.pgp writing\n");
+ goto finish;
+ }
+
+ if (rnp_save_keys(ffi, "GPG", keyfile, RNP_LOAD_SAVE_PUBLIC_KEYS) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to save pubring\n");
+ goto finish;
+ }
+
+ rnp_output_destroy(keyfile);
+ keyfile = NULL;
+
+ /* create file output object and save secret keyring with generated keys */
+ if (rnp_output_to_path(&keyfile, "secring.pgp") != RNP_SUCCESS) {
+ fprintf(stdout, "failed to initialize secring.pgp writing\n");
+ goto finish;
+ }
+
+ if (rnp_save_keys(ffi, "GPG", keyfile, RNP_LOAD_SAVE_SECRET_KEYS) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to save secring\n");
+ goto finish;
+ }
+
+ rnp_output_destroy(keyfile);
+ keyfile = NULL;
+
+ result = 0;
+finish:
+ rnp_buffer_destroy(key_grips);
+ rnp_output_destroy(keyfile);
+ rnp_ffi_destroy(ffi);
+ return result;
+}
+
+static int
+ffi_output_keys()
+{
+ rnp_ffi_t ffi = NULL;
+ rnp_input_t keyfile = NULL;
+ int result = 2;
+
+ /* initialize FFI object */
+ if (rnp_ffi_create(&ffi, "GPG", "GPG") != RNP_SUCCESS) {
+ return result;
+ }
+
+ /* load keyrings */
+ if (rnp_input_from_path(&keyfile, "pubring.pgp") != RNP_SUCCESS) {
+ fprintf(stdout, "failed to open pubring.pgp\n");
+ goto finish;
+ }
+
+ /* actually, we may use 0 instead of RNP_LOAD_SAVE_PUBLIC_KEYS, to not check key types */
+ if (rnp_load_keys(ffi, "GPG", keyfile, RNP_LOAD_SAVE_PUBLIC_KEYS) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to read pubring.pgp\n");
+ goto finish;
+ }
+ rnp_input_destroy(keyfile);
+ keyfile = NULL;
+
+ if (rnp_input_from_path(&keyfile, "secring.pgp") != RNP_SUCCESS) {
+ fprintf(stdout, "failed to open secring.pgp\n");
+ goto finish;
+ }
+
+ if (rnp_load_keys(ffi, "GPG", keyfile, RNP_LOAD_SAVE_SECRET_KEYS) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to read secring.pgp\n");
+ goto finish;
+ }
+ rnp_input_destroy(keyfile);
+ keyfile = NULL;
+
+ /* print armored keys to the stdout */
+ if (!ffi_print_key(ffi, "rsa@key", false) || !ffi_print_key(ffi, "rsa@key", true) ||
+ !ffi_print_key(ffi, "25519@key", false) || !ffi_print_key(ffi, "25519@key", true)) {
+ fprintf(stdout, "failed to print armored key(s)\n");
+ goto finish;
+ }
+
+ /* write armored keys to the files, named key-<keyid>-pub.asc/named key-<keyid>-sec.asc */
+ if (!ffi_export_key(ffi, "rsa@key", false) || !ffi_export_key(ffi, "rsa@key", true) ||
+ !ffi_export_key(ffi, "25519@key", false) || !ffi_export_key(ffi, "25519@key", true)) {
+ fprintf(stdout, "failed to write armored key(s) to file\n");
+ goto finish;
+ }
+
+ result = 0;
+finish:
+ rnp_input_destroy(keyfile);
+ rnp_ffi_destroy(ffi);
+ return result;
+}
+
+int
+main(int argc, char **argv)
+{
+ int res = ffi_generate_keys();
+ if (res) {
+ return res;
+ }
+ res = ffi_output_keys();
+ return res;
+}
diff --git a/src/examples/sign.c b/src/examples/sign.c
new file mode 100644
index 0000000..678dd91
--- /dev/null
+++ b/src/examples/sign.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2018, [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/rnp.h>
+#include <string.h>
+#include <time.h>
+
+#define RNP_SUCCESS 0
+
+/* sample pass provider implementation, which always return 'password' */
+static bool
+example_pass_provider(rnp_ffi_t ffi,
+ void * app_ctx,
+ rnp_key_handle_t key,
+ const char * pgp_context,
+ char buf[],
+ size_t buf_len)
+{
+ strncpy(buf, "password", buf_len);
+ return true;
+}
+
+static int
+ffi_sign()
+{
+ rnp_ffi_t ffi = NULL;
+ rnp_input_t keyfile = NULL;
+ rnp_input_t input = NULL;
+ rnp_output_t output = NULL;
+ rnp_op_sign_t sign = NULL;
+ rnp_key_handle_t key = NULL;
+ const char * message = "RNP signing sample message";
+ int result = 1;
+
+ /* initialize FFI object */
+ if (rnp_ffi_create(&ffi, "GPG", "GPG") != RNP_SUCCESS) {
+ return result;
+ }
+
+ /* load secret keyring, as it is required for signing. However, you may need to load public
+ * keyring as well to validate key's signatures. */
+ if (rnp_input_from_path(&keyfile, "secring.pgp") != RNP_SUCCESS) {
+ fprintf(stdout, "failed to open secring.pgp. Did you run ./generate sample?\n");
+ goto finish;
+ }
+
+ /* we may use RNP_LOAD_SAVE_SECRET_KEYS | RNP_LOAD_SAVE_PUBLIC_KEYS as well */
+ if (rnp_load_keys(ffi, "GPG", keyfile, RNP_LOAD_SAVE_SECRET_KEYS) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to read secring.pgp\n");
+ goto finish;
+ }
+ rnp_input_destroy(keyfile);
+ keyfile = NULL;
+
+ /* set the password provider - we'll need password to unlock secret keys */
+ rnp_ffi_set_pass_provider(ffi, example_pass_provider, NULL);
+
+ /* create file input and memory output objects for the encrypted message and decrypted
+ * message */
+ if (rnp_input_from_memory(&input, (uint8_t *) message, strlen(message), false) !=
+ RNP_SUCCESS) {
+ fprintf(stdout, "failed to create input object\n");
+ goto finish;
+ }
+
+ if (rnp_output_to_path(&output, "signed.asc") != RNP_SUCCESS) {
+ fprintf(stdout, "failed to create output object\n");
+ goto finish;
+ }
+
+ /* initialize and configure sign operation, use
+ * rnp_op_sign_create_cleartext/rnp_op_sign_create_detached for cleartext or detached
+ * signature. */
+ if (rnp_op_sign_create(&sign, ffi, input, output) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to create sign operation\n");
+ goto finish;
+ }
+
+ /* armor, file name, compression */
+ rnp_op_sign_set_armor(sign, true);
+ rnp_op_sign_set_file_name(sign, "message.txt");
+ rnp_op_sign_set_file_mtime(sign, (uint32_t) time(NULL));
+ rnp_op_sign_set_compression(sign, "ZIP", 6);
+ /* signatures creation time - by default will be set to the current time as well */
+ rnp_op_sign_set_creation_time(sign, (uint32_t) time(NULL));
+ /* signatures expiration time - by default will be 0, i.e. never expire */
+ rnp_op_sign_set_expiration_time(sign, 365 * 24 * 60 * 60);
+ /* set hash algorithm - should be compatible for all signatures */
+ rnp_op_sign_set_hash(sign, RNP_ALGNAME_SHA256);
+
+ /* now add signatures. First locate the signing key, then add and setup signature */
+ /* RSA signature */
+ if (rnp_locate_key(ffi, "userid", "rsa@key", &key) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to locate signing key rsa@key.\n");
+ goto finish;
+ }
+
+ /* we do not need pointer to the signature so passing NULL as the last parameter */
+ if (rnp_op_sign_add_signature(sign, key, NULL) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to add signature for key rsa@key.\n");
+ goto finish;
+ }
+
+ /* do not forget to destroy key handle */
+ rnp_key_handle_destroy(key);
+ key = NULL;
+
+ /* EdDSA signature */
+ if (rnp_locate_key(ffi, "userid", "25519@key", &key) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to locate signing key 25519@key.\n");
+ goto finish;
+ }
+
+ if (rnp_op_sign_add_signature(sign, key, NULL) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to add signature for key 25519@key.\n");
+ goto finish;
+ }
+
+ rnp_key_handle_destroy(key);
+ key = NULL;
+
+ /* finally do signing */
+ if (rnp_op_sign_execute(sign) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to sign\n");
+ goto finish;
+ }
+
+ fprintf(stdout, "Signing succeeded. See file signed.asc.\n");
+
+ result = 0;
+finish:
+ rnp_input_destroy(keyfile);
+ rnp_key_handle_destroy(key);
+ rnp_op_sign_destroy(sign);
+ rnp_input_destroy(input);
+ rnp_output_destroy(output);
+ rnp_ffi_destroy(ffi);
+ return result;
+}
+
+int
+main(int argc, char **argv)
+{
+ return ffi_sign();
+}
diff --git a/src/examples/verify.c b/src/examples/verify.c
new file mode 100644
index 0000000..17f42a8
--- /dev/null
+++ b/src/examples/verify.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2018, [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/rnp.h>
+#include <string.h>
+
+#define RNP_SUCCESS 0
+
+/* example key provider which loads key from file based on its keyid */
+static void
+example_key_provider(rnp_ffi_t ffi,
+ void * app_ctx,
+ const char *identifier_type,
+ const char *identifier,
+ bool secret)
+{
+ rnp_input_t input = NULL;
+ char filename[32] = {0};
+ if (strcmp(identifier_type, "keyid")) {
+ if (strcmp(identifier_type, "fingerprint")) {
+ fprintf(stdout, "Unsupported key search: %s = %s\n", identifier_type, identifier);
+ return;
+ }
+ /* if we search by fp then keyid is last 16 chars */
+ if (strlen(identifier) < 40) {
+ fprintf(stdout, "Invalid fingerprint: %s\n", identifier);
+ return;
+ }
+ identifier += 24;
+ }
+
+ snprintf(filename, sizeof(filename), "key-%s-%s.asc", identifier, secret ? "sec" : "pub");
+
+ if (rnp_input_from_path(&input, filename) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to open key file %s\n", filename);
+ return;
+ }
+
+ if (rnp_load_keys(
+ ffi, "GPG", input, RNP_LOAD_SAVE_SECRET_KEYS | RNP_LOAD_SAVE_PUBLIC_KEYS) !=
+ RNP_SUCCESS) {
+ fprintf(stdout, "failed to load key from file %s\n", filename);
+ }
+ rnp_input_destroy(input);
+}
+
+static int
+ffi_verify()
+{
+ rnp_ffi_t ffi = NULL;
+ rnp_op_verify_t verify = NULL;
+ rnp_input_t input = NULL;
+ rnp_output_t output = NULL;
+ uint8_t * buf = NULL;
+ size_t buf_len = 0;
+ size_t sigcount = 0;
+ int result = 1;
+
+ /* initialize FFI object */
+ if (rnp_ffi_create(&ffi, "GPG", "GPG") != RNP_SUCCESS) {
+ return result;
+ }
+
+ /* we do not load any keys here since we'll use key provider */
+ rnp_ffi_set_key_provider(ffi, example_key_provider, NULL);
+
+ /* create file input and memory output objects for the signed message and verified
+ * message */
+ if (rnp_input_from_path(&input, "signed.asc") != RNP_SUCCESS) {
+ fprintf(stdout, "failed to open file 'signed.asc'. Did you run the sign example?\n");
+ goto finish;
+ }
+
+ if (rnp_output_to_memory(&output, 0) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to create output object\n");
+ goto finish;
+ }
+
+ if (rnp_op_verify_create(&verify, ffi, input, output) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to create verification context\n");
+ goto finish;
+ }
+
+ if (rnp_op_verify_execute(verify) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to execute verification operation\n");
+ goto finish;
+ }
+
+ /* now check signatures and get some info about them */
+ if (rnp_op_verify_get_signature_count(verify, &sigcount) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to get signature count\n");
+ goto finish;
+ }
+
+ for (size_t i = 0; i < sigcount; i++) {
+ rnp_op_verify_signature_t sig = NULL;
+ rnp_result_t sigstatus = RNP_SUCCESS;
+ rnp_key_handle_t key = NULL;
+ char * keyid = NULL;
+
+ if (rnp_op_verify_get_signature_at(verify, i, &sig) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to get signature %d\n", (int) i);
+ goto finish;
+ }
+
+ if (rnp_op_verify_signature_get_key(sig, &key) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to get signature's %d key\n", (int) i);
+ goto finish;
+ }
+
+ if (rnp_key_get_keyid(key, &keyid) != RNP_SUCCESS) {
+ fprintf(stdout, "failed to get key id %d\n", (int) i);
+ rnp_key_handle_destroy(key);
+ goto finish;
+ }
+
+ sigstatus = rnp_op_verify_signature_get_status(sig);
+ fprintf(stdout, "Status for signature from key %s : %d\n", keyid, (int) sigstatus);
+ rnp_buffer_destroy(keyid);
+ rnp_key_handle_destroy(key);
+ }
+
+ /* get the verified message from the output structure */
+ if (rnp_output_memory_get_buf(output, &buf, &buf_len, false) != RNP_SUCCESS) {
+ goto finish;
+ }
+ fprintf(stdout, "Verified message:\n%.*s\n", (int) buf_len, buf);
+
+ result = 0;
+finish:
+ rnp_op_verify_destroy(verify);
+ rnp_input_destroy(input);
+ rnp_output_destroy(output);
+ rnp_ffi_destroy(ffi);
+ return result;
+}
+
+int
+main(int argc, char **argv)
+{
+ return ffi_verify();
+}