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/examples | |
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 '')
-rw-r--r-- | src/examples/CMakeLists.txt | 140 | ||||
-rw-r--r-- | src/examples/README.md | 5 | ||||
-rw-r--r-- | src/examples/decrypt.c | 138 | ||||
-rw-r--r-- | src/examples/dump.c | 163 | ||||
-rw-r--r-- | src/examples/encrypt.c | 133 | ||||
-rw-r--r-- | src/examples/generate.c | 330 | ||||
-rw-r--r-- | src/examples/sign.c | 168 | ||||
-rw-r--r-- | src/examples/verify.c | 165 |
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(); +} |