Bug 1675516 - RNP 2020-10-30. r=rjl
authorKai Engert <kaie@kuix.de>
Thu, 05 Nov 2020 14:20:01 +0000
changeset 30997 0f38d3b698bbc57c97fde4f2c5bdbad6da3485b5
parent 30996 f8df8132c53b93f499a1abe10df932692c61932b
child 30998 f9cf8c5973a6ca4ad8365732d057f6ba7d25ecc3
push id18156
push userthunderbird@calypsoblue.org
push dateTue, 10 Nov 2020 18:31:36 +0000
treeherdercomm-central@f9cf8c5973a6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrjl
bugs1675516
Bug 1675516 - RNP 2020-10-30. r=rjl Differential Revision: https://phabricator.services.mozilla.com/D96563
third_party/README.rnp
third_party/rnp/CMakeSettings.json
third_party/rnp/include/rekey/rnp_key_store.h
third_party/rnp/include/repgp/repgp_def.h
third_party/rnp/include/rnp/rnp.h
third_party/rnp/src/common/file-utils.cpp
third_party/rnp/src/common/file-utils.h
third_party/rnp/src/fuzzing/CMakeLists.txt
third_party/rnp/src/fuzzing/keyimport.c
third_party/rnp/src/fuzzing/sigimport.c
third_party/rnp/src/lib/CMakeLists.txt
third_party/rnp/src/lib/config.h.in
third_party/rnp/src/lib/crypto.cpp
third_party/rnp/src/lib/crypto/s2k.cpp
third_party/rnp/src/lib/ffi-priv-types.h
third_party/rnp/src/lib/generate-key.cpp
third_party/rnp/src/lib/pgp-key.cpp
third_party/rnp/src/lib/pgp-key.h
third_party/rnp/src/lib/rnp.cpp
third_party/rnp/src/lib/types.h
third_party/rnp/src/lib/version.h
third_party/rnp/src/librekey/key_store_g10.cpp
third_party/rnp/src/librekey/key_store_g10.h
third_party/rnp/src/librekey/key_store_kbx.cpp
third_party/rnp/src/librekey/key_store_pgp.cpp
third_party/rnp/src/librekey/key_store_pgp.h
third_party/rnp/src/librekey/rnp_key_store.cpp
third_party/rnp/src/librepgp/stream-armor.cpp
third_party/rnp/src/librepgp/stream-common.cpp
third_party/rnp/src/librepgp/stream-common.h
third_party/rnp/src/librepgp/stream-ctx.cpp
third_party/rnp/src/librepgp/stream-ctx.h
third_party/rnp/src/librepgp/stream-dump.cpp
third_party/rnp/src/librepgp/stream-key.cpp
third_party/rnp/src/librepgp/stream-key.h
third_party/rnp/src/librepgp/stream-packet.cpp
third_party/rnp/src/librepgp/stream-parse.cpp
third_party/rnp/src/librepgp/stream-sig.cpp
third_party/rnp/src/librepgp/stream-sig.h
third_party/rnp/src/librepgp/stream-write.cpp
third_party/rnp/src/rnp/fficli.h
third_party/rnp/src/rnpkeys/rnpkeys.cpp
third_party/rnp/src/tests/CMakeLists.txt
third_party/rnp/src/tests/data/issue1188/armored_revocation_signature.pgp
third_party/rnp/src/tests/data/keyrings/1/pubring.gpg.asc
third_party/rnp/src/tests/data/test_fuzz_keyimport/crash_25f06f13b48d58a5faf6c36fae7fcbd958359199
third_party/rnp/src/tests/data/test_fuzz_keyimport/crash_e932261875271ccf497715de56adf7caf30ca8a7
third_party/rnp/src/tests/data/test_key_edge_cases/alice-s2k-101-3.pgp
third_party/rnp/src/tests/data/test_key_edge_cases/alice-s2k-101-unknown.pgp
third_party/rnp/src/tests/data/test_key_edge_cases/key-unhashed-subpkts.pgp
third_party/rnp/src/tests/data/test_key_validity/case5/generate.cpp
third_party/rnp/src/tests/data/test_stream_key_load/key0-sub01.pgp
third_party/rnp/src/tests/data/test_stream_key_load/key0-sub02.pgp
third_party/rnp/src/tests/data/test_stream_key_load/key0-sub1.pgp
third_party/rnp/src/tests/ffi-enc.cpp
third_party/rnp/src/tests/ffi.cpp
third_party/rnp/src/tests/file-utils.cpp
third_party/rnp/src/tests/fuzz_keyimport.cpp
third_party/rnp/src/tests/generatekey.cpp
third_party/rnp/src/tests/issues/oss-fuzz-25489.cpp
third_party/rnp/src/tests/key-prefs.cpp
third_party/rnp/src/tests/load-pgp.cpp
third_party/rnp/src/tests/rnp_tests.h
third_party/rnp/src/tests/streams.cpp
third_party/rnp/src/tests/support.cpp
third_party/rnp/src/tests/user-prefs.cpp
--- a/third_party/README.rnp
+++ b/third_party/README.rnp
@@ -1,12 +1,12 @@
 Directory ./rnp contains a copy of rnp which has been obtained from:
 https://github.com/rnpgp/rnp
 
-[commit 49a675e374e6e298e432cffa171d2cd9378b6706]
+[commit a2c5ecd3a84a33450f5d8cda09cf5549410b5e70]
 
 For licensing information, please refer to the included documentation.
 
 To update this copy, run "update_rnp.sh" in this directory from this directory
 within a complete build tree (including mozilla-central) as "mach python" is
 used.
 
 update_rnp.sh will generate rnp/src/lib/version.h from rnp/src/lib/version.h.in
deleted file mode 100644
--- a/third_party/rnp/CMakeSettings.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
-  "configurations": [
-    {
-      "name": "x64-Debug",
-      "generator": "Visual Studio 16 2019 Win64",
-      "configurationType": "Debug",
-      "inheritEnvironments": [ "msvc_x64_x64" ],
-      "buildRoot": "${projectDir}\\..\\${name}",
-      "installRoot": "${projectDir}\\..\\install\\${name}",
-      "cmakeCommandArgs": "-DCMAKE_FIND_DEBUG_MODE=ON",
-      "buildCommandArgs": "",
-      "ctestCommandArgs": "",
-      "variables": [],
-      "cmakeToolchain": "${env.VCPKG_DIR}\\scripts\\buildsystems\\vcpkg.cmake",
-      "intelliSenseMode": "windows-msvc-x64"
-    },
-    {
-      "name": "x32-Debug",
-      "generator": "Visual Studio 16 2019",
-      "configurationType": "Debug",
-      "buildRoot": "${projectDir}\\..\\${name}",
-      "installRoot": "${projectDir}\\..\\install\\${name}",
-      "cmakeCommandArgs": "-DCMAKE_FIND_DEBUG_MODE=ON",
-      "buildCommandArgs": "",
-      "ctestCommandArgs": "",
-      "cmakeToolchain": "${env.VCPKG_DIR}\\scripts\\buildsystems\\vcpkg.cmake",
-      "inheritEnvironments": [ "msvc_x86" ],
-      "variables": [],
-      "intelliSenseMode": "windows-msvc-x64"
-    }
-  ]
-}
\ No newline at end of file
--- a/third_party/rnp/include/rekey/rnp_key_store.h
+++ b/third_party/rnp/include/rekey/rnp_key_store.h
@@ -127,17 +127,16 @@ typedef enum pgp_sig_import_status_t {
 
 typedef std::unordered_map<pgp_fingerprint_t, std::list<pgp_key_t>::iterator> pgp_key_fp_map_t;
 
 typedef struct rnp_key_store_t {
     std::string            path;
     pgp_key_store_format_t format;
     bool                   disable_validation =
       false; /* do not automatically validate keys, added to this key store */
-    bool skip_parsing_errors = false; /* do not fail on parsing errors */
 
     std::list<pgp_key_t> keys;
     pgp_key_fp_map_t     keybyfp;
 
     list blobs = NULL; // list of kbx_blob_t
 
     ~rnp_key_store_t();
     rnp_key_store_t() : path(""), format(PGP_KEY_STORE_UNKNOWN){};
--- a/third_party/rnp/include/repgp/repgp_def.h
+++ b/third_party/rnp/include/repgp/repgp_def.h
@@ -413,16 +413,22 @@ typedef enum {
     PGP_KF_AUTH = 0x20,            /* This key may be used for authentication. */
     PGP_KF_SHARED = 0x80,          /* The private component of this key may be in the
                                             possession of more than one person. */
     /* pseudo flags */
     PGP_KF_NONE = 0x00,
     PGP_KF_ENCRYPT = PGP_KF_ENCRYPT_COMMS | PGP_KF_ENCRYPT_STORAGE,
 } pgp_key_flags_t;
 
+typedef enum {
+    PGP_KEY_FEATURE_MDC = 0x01,
+    PGP_KEY_FEATURE_AEAD = 0x02,
+    PGP_KEY_FEATURE_V5 = 0x04
+} pgp_key_feature_t;
+
 /** Types of Compression */
 typedef enum {
     PGP_C_NONE = 0,
     PGP_C_ZIP = 1,
     PGP_C_ZLIB = 2,
     PGP_C_BZIP2 = 3,
     PGP_C_UNKNOWN = 255
 } pgp_compression_type_t;
--- a/third_party/rnp/include/rnp/rnp.h
+++ b/third_party/rnp/include/rnp/rnp.h
@@ -72,16 +72,17 @@ typedef uint32_t rnp_result_t;
 #define RNP_DUMP_GRIP (1U << 2)
 
 /**
  * Flags for the key loading/saving functions.
  */
 #define RNP_LOAD_SAVE_PUBLIC_KEYS (1U << 0)
 #define RNP_LOAD_SAVE_SECRET_KEYS (1U << 1)
 #define RNP_LOAD_SAVE_PERMISSIVE (1U << 8)
+#define RNP_LOAD_SAVE_SINGLE (1U << 9)
 
 /**
  * Flags for output structure creation.
  */
 #define RNP_OUTPUT_FILE_OVERWRITE (1U << 0)
 #define RNP_OUTPUT_FILE_RANDOM (1U << 1)
 
 /**
@@ -423,20 +424,27 @@ RNP_API rnp_result_t rnp_unload_keys(rnp
 
 /** import keys to the keyring and receive JSON list of the new/updated keys.
  *  Note: this will work only with keys in OpenPGP format, use rnp_load_keys for other formats.
  * @param ffi
  * @param input source to read from. Cannot be NULL.
  * @param flags see RNP_LOAD_SAVE_* constants. If RNP_LOAD_SAVE_PERMISSIVE is specified
  *              then import process will skip unrecognized or bad keys/signatures instead of
  *              failing the whole operation.
+ *              If flag RNP_LOAD_SAVE_SINGLE is set, then only first key will be loaded (subkey
+ *              or primary key with it's subkeys). In case RNP_LOAD_SAVE_PERMISSIVE and
+ *              erroneous first key on the stream RNP_SUCCESS will be returned, but results
+ *              will include an empty array. Also RNP_ERROR_EOF will be returned if the last
+ *              key was read.
  * @param results if not NULL then after the successfull execution will contain JSON with
  *                information about new and updated keys. You must free it using the
  *                rnp_buffer_destroy() function.
- * @return RNP_SUCCESS on success, or any other value on error.
+ * @return RNP_SUCCESS on success
+ *         RNP_ERROR_EOF if last key was read (if RNP_LOAD_SAVE_SINGLE was used)
+ *         any other value on error.
  */
 RNP_API rnp_result_t rnp_import_keys(rnp_ffi_t   ffi,
                                      rnp_input_t input,
                                      uint32_t    flags,
                                      char **     results);
 
 /** import standalone signatures to the keyring and receive JSON list of the updated keys.
  *
@@ -1361,16 +1369,18 @@ RNP_API rnp_result_t rnp_key_is_locked(r
  *             - "Encrypted" : secret key data is encrypted, using just CRC as integrity
  *                 protection.
  *             - "Encrypted-Hashed" : secret key data is encrypted, using the SHA1 hash as
  *                 an integrity protection.
  *             - "GPG-None" : secret key data is not available at all (this would happen if
  *                 secret key is exported from GnuPG via --export-secret-subkeys)
  *             - "GPG-Smartcard" : secret key data is stored on smartcard by GnuPG, so is not
  *                 available
+ *             - "Unknown" : key protection type is unknown, so secret key data is not
+ *                 available
  * @return RNP_SUCCESS on success, or any other value on error
  */
 RNP_API rnp_result_t rnp_key_get_protection_type(rnp_key_handle_t key, char **type);
 
 /**
  * @brief Get mode in which secret key data is encrypted.
  *
  * @param key key handle, cannot be NULL and should have secret part (see function
--- a/third_party/rnp/src/common/file-utils.cpp
+++ b/third_party/rnp/src/common/file-utils.cpp
@@ -23,24 +23,31 @@
  * 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.
  */
 /** File utilities
  *  @file
  */
 
 #include "file-utils.h"
-
-extern "C" {
+#include "config.h"
 #ifdef _MSC_VER
+#include <stdlib.h>
+#include <stdio.h>
 #include "uniwin.h"
+#include <errno.h>
+#include <locale>
+#include <codecvt>
+#include <random>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
 #else
 #include <sys/stat.h>
-#endif
-}
+#endif // _MSC_VER
 
 bool
 rnp_file_exists(const char *path)
 {
     struct stat st;
     return stat(path, &st) == 0 && S_ISREG(st.st_mode);
 }
 
@@ -51,8 +58,67 @@ rnp_filemtime(const char *path)
     struct stat st;
 
     if (stat(path, &st) != 0) {
         return 0;
     } else {
         return st.st_mtime;
     }
 }
+
+#ifdef _MSC_VER
+static const char letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+/** @private
+ *  generate a temporary file name based on TMPL.
+ *
+ *  @param tmpl filename template in UTF-8 ending in XXXXXX
+ *  @return file descriptor of newly created and opened file, or -1 on error
+ **/
+int
+rnp_mkstemp(char *tmpl)
+{
+    int       save_errno = errno;
+    const int mask_length = 6;
+    int       len = strlen(tmpl);
+    if (len < mask_length || strcmp(&tmpl[len - mask_length], "XXXXXX")) {
+        errno = EINVAL;
+        return -1;
+    }
+    std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8conv;
+    std::wstring tmpl_w = utf8conv.from_bytes(tmpl, tmpl + len - mask_length);
+
+    /* This is where the Xs start.  */
+    char *XXXXXX = &tmpl[len - mask_length];
+
+    std::random_device rd;
+    std::mt19937_64    rng(rd());
+
+    for (unsigned int countdown = TMP_MAX; --countdown;) {
+        unsigned long long v = rng();
+
+        XXXXXX[0] = letters[v % 36];
+        v /= 36;
+        XXXXXX[1] = letters[v % 36];
+        v /= 36;
+        XXXXXX[2] = letters[v % 36];
+        v /= 36;
+        XXXXXX[3] = letters[v % 36];
+        v /= 36;
+        XXXXXX[4] = letters[v % 36];
+        v /= 36;
+        XXXXXX[5] = letters[v % 36];
+
+        int flags = O_WRONLY | O_CREAT | O_EXCL | O_BINARY;
+        int fd =
+          _wopen((tmpl_w + utf8conv.from_bytes(XXXXXX)).c_str(), flags, _S_IREAD | _S_IWRITE);
+        if (fd != -1) {
+            errno = save_errno;
+            return fd;
+        } else if (errno != EEXIST)
+            return -1;
+    }
+
+    // We got out of the loop because we ran out of combinations to try.
+    errno = EEXIST;
+    return -1;
+}
+#endif // _MSC_VER
--- a/third_party/rnp/src/common/file-utils.h
+++ b/third_party/rnp/src/common/file-utils.h
@@ -27,9 +27,20 @@
 #ifndef RNP_FILE_UTILS_H_
 #define RNP_FILE_UTILS_H_
 
 #include <stdint.h>
 
 bool    rnp_file_exists(const char *path);
 int64_t rnp_filemtime(const char *path);
 
+/** @private
+ *  generate a temporary file name based on TMPL.  TMPL must match the
+ *  rules for mk[s]temp (i.e. end in "XXXXXX").  The name constructed
+ *  does not exist at the time of the call to mkstemp.  TMPL is
+ *  overwritten with the result.get the list item at specified index
+ *
+ *  @param tmpl filename template
+ *  @return file descriptor of newly created and opened file, or -1 on error
+ **/
+int rnp_mkstemp(char *tmpl);
+
 #endif
--- a/third_party/rnp/src/fuzzing/CMakeLists.txt
+++ b/third_party/rnp/src/fuzzing/CMakeLists.txt
@@ -55,16 +55,42 @@ target_include_directories(fuzz_keyring
     "${PROJECT_SOURCE_DIR}/src/lib"
 )
 
 target_link_libraries(fuzz_keyring
   PRIVATE
     librnp
 )
 
+add_executable(fuzz_keyimport keyimport.c)
+
+target_include_directories(fuzz_keyimport
+  PRIVATE
+    "${PROJECT_SOURCE_DIR}/src"
+    "${PROJECT_SOURCE_DIR}/src/lib"
+)
+
+target_link_libraries(fuzz_keyimport
+  PRIVATE
+    librnp
+)
+
+add_executable(fuzz_sigimport sigimport.c)
+
+target_include_directories(fuzz_sigimport
+  PRIVATE
+    "${PROJECT_SOURCE_DIR}/src"
+    "${PROJECT_SOURCE_DIR}/src/lib"
+)
+
+target_link_libraries(fuzz_sigimport
+  PRIVATE
+    librnp
+)
+
 add_executable(fuzz_verify verify.c)
 
 target_include_directories(fuzz_verify
   PRIVATE
     "${PROJECT_SOURCE_DIR}/src"
     "${PROJECT_SOURCE_DIR}/src/lib"
 )
 
@@ -108,12 +134,12 @@ target_include_directories(fuzz_keyring_
 )
 
 target_link_libraries(fuzz_keyring_g10
   PRIVATE
     librnp-static
 )
 
 if (ENABLE_SANITIZERS)
-  foreach(tgt fuzz_dump fuzz_keyring fuzz_verify fuzz_verify_detached fuzz_keyring_kbx fuzz_keyring_g10)
+  foreach(tgt fuzz_dump fuzz_keyring fuzz_keyimport fuzz_sigimport fuzz_verify fuzz_verify_detached fuzz_keyring_kbx fuzz_keyring_g10)
     set_target_properties(${tgt} PROPERTIES LINKER_LANGUAGE CXX)
   endforeach()
 endif()
new file mode 100644
--- /dev/null
+++ b/third_party/rnp/src/fuzzing/keyimport.c
@@ -0,0 +1,97 @@
+/*
+ * 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 <rnp/rnp.h>
+#include <rnp/rnp_err.h>
+
+#ifdef RNP_RUN_TESTS
+int keyimport_LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+int
+keyimport_LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+#else
+int
+LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+#endif
+{
+    rnp_input_t  input = NULL;
+    rnp_result_t ret = 0;
+    rnp_ffi_t    ffi = NULL;
+
+    /* try non-permissive import */
+    ret = rnp_input_from_memory(&input, data, size, false);
+    ret = rnp_ffi_create(&ffi, "GPG", "GPG");
+    char *results = NULL;
+    ret = rnp_import_keys(
+      ffi, input, RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS, &results);
+    rnp_buffer_destroy(results);
+    rnp_input_destroy(input);
+    rnp_ffi_destroy(ffi);
+
+    /* try permissive import */
+    ret = rnp_input_from_memory(&input, data, size, false);
+    ret = rnp_ffi_create(&ffi, "GPG", "GPG");
+    results = NULL;
+    ret = rnp_import_keys(ffi,
+                          input,
+                          RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS |
+                            RNP_LOAD_SAVE_PERMISSIVE,
+                          &results);
+    rnp_buffer_destroy(results);
+    rnp_input_destroy(input);
+    rnp_ffi_destroy(ffi);
+
+    /* try non-permissive iterative import */
+    ret = rnp_input_from_memory(&input, data, size, false);
+    ret = rnp_ffi_create(&ffi, "GPG", "GPG");
+    do {
+        results = NULL;
+        ret = rnp_import_keys(ffi,
+                              input,
+                              RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS |
+                                RNP_LOAD_SAVE_SINGLE,
+                              &results);
+        rnp_buffer_destroy(results);
+    } while (!ret);
+    rnp_input_destroy(input);
+    rnp_ffi_destroy(ffi);
+
+    /* try permissive iterative import */
+    ret = rnp_input_from_memory(&input, data, size, false);
+    ret = rnp_ffi_create(&ffi, "GPG", "GPG");
+    do {
+        results = NULL;
+        ret = rnp_import_keys(ffi,
+                              input,
+                              RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS |
+                                RNP_LOAD_SAVE_PERMISSIVE | RNP_LOAD_SAVE_SINGLE,
+                              &results);
+        rnp_buffer_destroy(results);
+    } while (!ret);
+    rnp_input_destroy(input);
+    rnp_ffi_destroy(ffi);
+
+    return 0;
+}
new file mode 100644
--- /dev/null
+++ b/third_party/rnp/src/fuzzing/sigimport.c
@@ -0,0 +1,51 @@
+/*
+ * 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 <rnp/rnp.h>
+
+#ifdef RNP_RUN_TESTS
+int sigimport_LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+int
+sigimport_LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+#else
+int
+LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+#endif
+{
+    rnp_input_t  input = NULL;
+    rnp_result_t ret = 0;
+    rnp_ffi_t    ffi = NULL;
+
+    ret = rnp_input_from_memory(&input, data, size, false);
+    ret = rnp_ffi_create(&ffi, "GPG", "GPG");
+    char *results = NULL;
+    ret = rnp_import_signatures(ffi, input, 0, &results);
+    rnp_buffer_destroy(results);
+    rnp_input_destroy(input);
+    rnp_ffi_destroy(ffi);
+
+    return 0;
+}
--- a/third_party/rnp/src/lib/CMakeLists.txt
+++ b/third_party/rnp/src/lib/CMakeLists.txt
@@ -42,20 +42,20 @@ check_include_file_cxx(limits.h HAVE_LIM
 check_include_file_cxx(stdint.h HAVE_STDINT_H)
 check_include_file_cxx(string.h HAVE_STRING_H)
 check_include_file_cxx(sys/cdefs.h HAVE_SYS_CDEFS_H)
 check_include_file_cxx(sys/cdefs.h HAVE_SYS_MMAN_H)
 check_include_file_cxx(sys/resource.h HAVE_SYS_RESOURCE_H)
 check_include_file_cxx(sys/stat.h HAVE_SYS_STAT_H)
 check_include_file_cxx(sys/types.h HAVE_SYS_TYPES_H)
 check_include_file_cxx(sys/param.h HAVE_SYS_PARAM_H)
-check_include_file_cxx(sys/time.h HAVE_SYS_TIME_H)
 check_include_file_cxx(unistd.h HAVE_UNISTD_H)
 check_include_file_cxx(sys/wait.h HAVE_SYS_WAIT_H)
 check_cxx_symbol_exists(mkdtemp "stdlib.h;unistd.h" HAVE_MKDTEMP)
+check_cxx_symbol_exists(mkstemp "stdlib.h;unistd.h" HAVE_MKSTEMP)
 check_cxx_symbol_exists(realpath stdlib.h HAVE_REALPATH)
 check_cxx_symbol_exists(O_BINARY fcntl.h HAVE_O_BINARY)
 check_cxx_symbol_exists(_O_BINARY fcntl.h HAVE__O_BINARY)
 check_cxx_symbol_exists(_tempnam stdio.h HAVE__TEMPNAM)
 set(HAVE_ZLIB_H "${ZLIB_FOUND}")
 set(HAVE_BZLIB_H "${BZIP2_FOUND}")
 configure_file(config.h.in config.h)
 # generate a version.h
--- a/third_party/rnp/src/lib/config.h.in
+++ b/third_party/rnp/src/lib/config.h.in
@@ -19,17 +19,17 @@
  * 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.
  */
 
-#define PACKAGE_STRING    "rnp 0.13.1+git20200913.49a675e3.MZLA"
+#define PACKAGE_STRING    "rnp 0.13.1+git20201030.a2c5ecd3.MZLA"
 #define PACKAGE_BUGREPORT "https://bugzilla.mozilla.org/enter_bug.cgi?product=Thunderbird"
 
 #undef HAVE_BZLIB_H
 #undef HAVE_ZLIB_H
 
 #undef HAVE_FCNTL_H
 #undef HAVE_INTTYPES_H
 #undef HAVE_LIMITS_H
@@ -37,19 +37,19 @@
 #undef HAVE_STRING_H
 #undef HAVE_SYS_CDEFS_H
 #undef HAVE_SYS_MMAN_H
 #undef HAVE_SYS_RESOURCE_H
 #undef HAVE_SYS_STAT_H
 #undef HAVE_SYS_TYPES_H
 #undef HAVE_UNISTD_H
 #undef HAVE_SYS_WAIT_H
-#undef HAVE_SYS_TIME_H
 #undef HAVE_SYS_PARAM_H
 #undef HAVE_MKDTEMP
+#undef HAVE_MKSTEMP
 #undef HAVE_REALPATH
 #undef HAVE_O_BINARY
 #undef HAVE__O_BINARY
 #undef HAVE__TEMPNAM
 
 /* Macro _GLIBCXX_USE_CXX11_ABI was first introduced with GCC 5.0, which
  * we assume to be bundled with a sane implementation of std::regex. */
 #if !defined(__GNUC__) || defined(_GLIBCXX_USE_CXX11_ABI) || defined(MSVC)
--- a/third_party/rnp/src/lib/crypto.cpp
+++ b/third_party/rnp/src/lib/crypto.cpp
@@ -193,16 +193,20 @@ key_material_equal(const pgp_key_materia
         RNP_LOG("unknown public key algorithm: %d", (int) key1->alg);
         return false;
     }
 }
 
 rnp_result_t
 validate_pgp_key_material(const pgp_key_material_t *material, rng_t *rng)
 {
+#ifdef FUZZERS_ENABLED
+    /* do not timeout on large keys during fuzzing */
+    return RNP_SUCCESS;
+#else
     switch (material->alg) {
     case PGP_PKA_RSA:
     case PGP_PKA_RSA_ENCRYPT_ONLY:
     case PGP_PKA_RSA_SIGN_ONLY:
         return rsa_validate_key(rng, &material->rsa, material->secret);
     case PGP_PKA_DSA:
         return dsa_validate_key(rng, &material->dsa, material->secret);
     case PGP_PKA_EDDSA:
@@ -216,16 +220,17 @@ validate_pgp_key_material(const pgp_key_
     case PGP_PKA_ELGAMAL:
     case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
         return elgamal_validate_key(rng, &material->eg, material->secret);
     default:
         RNP_LOG("unknown public key algorithm: %d", (int) material->alg);
     }
 
     return RNP_ERROR_BAD_PARAMETERS;
+#endif
 }
 
 size_t
 key_bitlength(const pgp_key_material_t *key)
 {
     switch (key->alg) {
     case PGP_PKA_RSA:
     case PGP_PKA_RSA_ENCRYPT_ONLY:
--- a/third_party/rnp/src/lib/crypto/s2k.cpp
+++ b/third_party/rnp/src/lib/crypto/s2k.cpp
@@ -26,17 +26,17 @@
  * 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 <botan/ffi.h>
 #include <stdio.h>
 #include "config.h"
-#ifdef HAVE_SYS_TIME_H
+#ifndef _MSC_VER
 #include <sys/time.h>
 #else
 #include "uniwin.h"
 #endif
 
 #include "crypto/s2k.h"
 #include "defaults.h"
 #include "rnp.h"
@@ -148,17 +148,17 @@ pgp_s2k_encode_iterations(size_t iterati
     }
     return 255;
 }
 
 /// Should this function be elsewhere?
 static uint64_t
 get_timestamp_usec()
 {
-#ifdef HAVE_SYS_TIME_H
+#ifndef _MSC_VER
     // TODO: Consider clock_gettime
     struct timeval tv;
     ::gettimeofday(&tv, NULL);
     return (static_cast<uint64_t>(tv.tv_sec) * 1000000) + static_cast<uint64_t>(tv.tv_usec);
 #else
     return GetTickCount64() * 1000;
 #endif
 }
--- a/third_party/rnp/src/lib/ffi-priv-types.h
+++ b/third_party/rnp/src/lib/ffi-priv-types.h
@@ -23,16 +23,17 @@
  * 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 <json.h>
 #include "utils.h"
+#include <list>
 
 struct rnp_key_handle_st {
     rnp_ffi_t        ffi;
     pgp_key_search_t locator;
     pgp_key_t *      pub;
     pgp_key_t *      sec;
 };
 
@@ -92,88 +93,94 @@ struct rnp_output_st {
     char *               dst_directory;
     rnp_output_writer_t *writer;
     rnp_output_closer_t *closer;
     void *               app_ctx;
     bool                 keep;
 };
 
 struct rnp_op_generate_st {
-    rnp_ffi_t  ffi;
-    bool       primary;
-    pgp_key_t *primary_sec;
-    pgp_key_t *primary_pub;
-    pgp_key_t *gen_sec;
-    pgp_key_t *gen_pub;
+    rnp_ffi_t  ffi{};
+    bool       primary{};
+    pgp_key_t *primary_sec{};
+    pgp_key_t *primary_pub{};
+    pgp_key_t *gen_sec{};
+    pgp_key_t *gen_pub{};
     /* password used to encrypt the key, if specified */
-    char *password;
+    char *password{};
     /* request password for key encryption via ffi's password provider */
-    bool request_password;
+    bool request_password{};
     /* we don't use top-level keygen action here for easier fields access */
-    rnp_keygen_crypto_params_t  crypto;
-    rnp_key_protection_params_t protection;
-    rnp_selfsig_cert_info_t     cert;
-    rnp_selfsig_binding_info_t  binding;
-};
+    rnp_keygen_crypto_params_t  crypto{};
+    rnp_key_protection_params_t protection{};
+    rnp_selfsig_cert_info_t     cert{};
+    rnp_selfsig_binding_info_t  binding{};
 
-struct rnp_op_sign_st {
-    rnp_ffi_t    ffi;
-    rnp_input_t  input;
-    rnp_output_t output;
-    rnp_ctx_t    rnpctx;
-    list         signatures;
+    ~rnp_op_generate_st();
 };
 
 struct rnp_op_sign_signature_st {
-    rnp_ffi_t         ffi;
-    rnp_signer_info_t signer;
+    rnp_ffi_t         ffi{};
+    rnp_signer_info_t signer{};
     bool              expiry_set : 1;
     bool              create_set : 1;
     bool              hash_set : 1;
 };
 
+typedef std::list<rnp_op_sign_signature_st> rnp_op_sign_signatures_t;
+
+struct rnp_op_sign_st {
+    rnp_ffi_t                ffi{};
+    rnp_input_t              input{};
+    rnp_output_t             output{};
+    rnp_ctx_t                rnpctx{};
+    rnp_op_sign_signatures_t signatures{};
+};
+
 struct rnp_op_verify_signature_st {
     rnp_ffi_t       ffi;
     rnp_result_t    verify_status;
     pgp_signature_t sig_pkt;
 };
 
 struct rnp_op_verify_st {
-    rnp_ffi_t    ffi;
-    rnp_input_t  input;
-    rnp_input_t  detached_input; /* for detached signature will be source file/data */
-    rnp_output_t output;
-    rnp_ctx_t    rnpctx;
+    rnp_ffi_t    ffi{};
+    rnp_input_t  input{};
+    rnp_input_t  detached_input{}; /* for detached signature will be source file/data */
+    rnp_output_t output{};
+    rnp_ctx_t    rnpctx{};
     /* these fields are filled after operation execution */
-    rnp_op_verify_signature_t signatures;
-    size_t                    signature_count;
-    char *                    filename;
-    uint32_t                  file_mtime;
+    rnp_op_verify_signature_t signatures{};
+    size_t                    signature_count{};
+    char *                    filename{};
+    uint32_t                  file_mtime{};
     /* encryption information */
-    bool           encrypted;
-    bool           mdc;
-    bool           validated;
-    pgp_aead_alg_t aead;
-    pgp_symm_alg_t salg;
+    bool           encrypted{};
+    bool           mdc{};
+    bool           validated{};
+    pgp_aead_alg_t aead{};
+    pgp_symm_alg_t salg{};
     /* recipient/symenc information */
-    rnp_recipient_handle_t recipients;
-    size_t                 recipient_count;
-    rnp_recipient_handle_t used_recipient;
-    rnp_symenc_handle_t    symencs;
-    size_t                 symenc_count;
-    rnp_symenc_handle_t    used_symenc;
-    size_t                 encrypted_layers;
+    rnp_recipient_handle_t recipients{};
+    size_t                 recipient_count{};
+    rnp_recipient_handle_t used_recipient{};
+    rnp_symenc_handle_t    symencs{};
+    size_t                 symenc_count{};
+    rnp_symenc_handle_t    used_symenc{};
+    size_t                 encrypted_layers{};
+
+    ~rnp_op_verify_st();
 };
 
 struct rnp_op_encrypt_st {
-    rnp_ffi_t    ffi;
-    rnp_input_t  input;
-    rnp_output_t output;
-    rnp_ctx_t    rnpctx;
-    list         signatures;
+    rnp_ffi_t                ffi{};
+    rnp_input_t              input{};
+    rnp_output_t             output{};
+    rnp_ctx_t                rnpctx{};
+    rnp_op_sign_signatures_t signatures{};
 };
 
 struct rnp_identifier_iterator_st {
     rnp_ffi_t                       ffi;
     pgp_key_search_type_t           type;
     rnp_key_store_t *               store;
     std::list<pgp_key_t>::iterator *keyp;
     unsigned                        uididx;
--- a/third_party/rnp/src/lib/generate-key.cpp
+++ b/third_party/rnp/src/lib/generate-key.cpp
@@ -153,79 +153,79 @@ pk_alg_default_flags(pgp_pubkey_alg_t al
 {
     // just use the full capabilities as the ultimate fallback
     return pgp_pk_alg_capabilities(alg);
 }
 
 // TODO: Similar as pgp_pick_hash_alg but different enough to
 //       keep another version. This will be changed when refactoring crypto
 static void
-adjust_hash_alg(rnp_keygen_crypto_params_t *crypto)
+adjust_hash_alg(rnp_keygen_crypto_params_t &crypto)
 {
-    if (!crypto->hash_alg) {
-        crypto->hash_alg = (pgp_hash_alg_t) DEFAULT_HASH_ALGS[0];
+    if (!crypto.hash_alg) {
+        crypto.hash_alg = (pgp_hash_alg_t) DEFAULT_HASH_ALGS[0];
     }
 
-    if ((crypto->key_alg != PGP_PKA_DSA) && (crypto->key_alg != PGP_PKA_ECDSA)) {
+    if ((crypto.key_alg != PGP_PKA_DSA) && (crypto.key_alg != PGP_PKA_ECDSA)) {
         return;
     }
 
-    pgp_hash_alg_t min_hash = (crypto->key_alg == PGP_PKA_ECDSA) ?
-                                ecdsa_get_min_hash(crypto->ecc.curve) :
-                                dsa_get_min_hash(crypto->dsa.q_bitlen);
+    pgp_hash_alg_t min_hash = (crypto.key_alg == PGP_PKA_ECDSA) ?
+                                ecdsa_get_min_hash(crypto.ecc.curve) :
+                                dsa_get_min_hash(crypto.dsa.q_bitlen);
 
-    if (pgp_digest_length(crypto->hash_alg) < pgp_digest_length(min_hash)) {
-        crypto->hash_alg = min_hash;
+    if (pgp_digest_length(crypto.hash_alg) < pgp_digest_length(min_hash)) {
+        crypto.hash_alg = min_hash;
     }
 }
 
 static void
-keygen_merge_crypto_defaults(rnp_keygen_crypto_params_t *crypto)
+keygen_merge_crypto_defaults(rnp_keygen_crypto_params_t &crypto)
 {
     // default to RSA
-    if (!crypto->key_alg) {
-        crypto->key_alg = PGP_PKA_RSA;
+    if (!crypto.key_alg) {
+        crypto.key_alg = PGP_PKA_RSA;
     }
 
-    switch (crypto->key_alg) {
+    switch (crypto.key_alg) {
     case PGP_PKA_RSA:
-        if (!crypto->rsa.modulus_bit_len) {
-            crypto->rsa.modulus_bit_len = DEFAULT_RSA_NUMBITS;
+        if (!crypto.rsa.modulus_bit_len) {
+            crypto.rsa.modulus_bit_len = DEFAULT_RSA_NUMBITS;
         }
         break;
 
     case PGP_PKA_SM2:
-        if (!crypto->hash_alg) {
-            crypto->hash_alg = PGP_HASH_SM3;
+        if (!crypto.hash_alg) {
+            crypto.hash_alg = PGP_HASH_SM3;
         }
-        if (!crypto->ecc.curve) {
-            crypto->ecc.curve = PGP_CURVE_SM2_P_256;
+        if (!crypto.ecc.curve) {
+            crypto.ecc.curve = PGP_CURVE_SM2_P_256;
         }
         break;
 
     case PGP_PKA_ECDH:
     case PGP_PKA_ECDSA: {
-        if (!crypto->hash_alg) {
-            crypto->hash_alg = (pgp_hash_alg_t) DEFAULT_HASH_ALGS[0];
+        if (!crypto.hash_alg) {
+            crypto.hash_alg = (pgp_hash_alg_t) DEFAULT_HASH_ALGS[0];
         }
         break;
     }
 
     case PGP_PKA_EDDSA:
-        if (!crypto->ecc.curve) {
-            crypto->ecc.curve = PGP_CURVE_ED25519;
+        if (!crypto.ecc.curve) {
+            crypto.ecc.curve = PGP_CURVE_ED25519;
         }
         break;
 
     case PGP_PKA_DSA: {
-        if (!crypto->dsa.p_bitlen) {
-            crypto->dsa.p_bitlen = DSA_DEFAULT_P_BITLEN;
+        if (!crypto.dsa.p_bitlen) {
+            crypto.dsa.p_bitlen = DSA_DEFAULT_P_BITLEN;
         }
-        if (!crypto->dsa.q_bitlen) {
-            crypto->dsa.q_bitlen = dsa_choose_qsize_by_psize(crypto->dsa.p_bitlen);
+        if (!crypto.dsa.q_bitlen) {
+            crypto.dsa.q_bitlen = dsa_choose_qsize_by_psize(crypto.dsa.p_bitlen);
         }
         break;
     }
     default:
         break;
     }
 
     adjust_hash_alg(crypto);
@@ -285,58 +285,56 @@ get_numbits(const rnp_keygen_crypto_para
     case PGP_PKA_ELGAMAL:
     case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
         return crypto->elgamal.key_bitlen;
     default:
         return 0;
     }
 }
 
-static bool
-set_default_user_prefs(pgp_user_prefs_t *prefs)
+static void
+set_default_user_prefs(pgp_user_prefs_t &prefs)
 {
-    if (!prefs->symm_algs &&
-        !pgp_user_prefs_set_symm_algs(
-          prefs, &DEFAULT_SYMMETRIC_ALGS[0], ARRAY_SIZE(DEFAULT_SYMMETRIC_ALGS))) {
-        return false;
+    if (prefs.symm_algs.empty()) {
+        prefs.set_symm_algs(
+          std::vector<uint8_t>(DEFAULT_SYMMETRIC_ALGS,
+                               DEFAULT_SYMMETRIC_ALGS + ARRAY_SIZE(DEFAULT_SYMMETRIC_ALGS)));
     }
-    if (!prefs->hash_algs && !pgp_user_prefs_set_hash_algs(
-                               prefs, &DEFAULT_HASH_ALGS[0], ARRAY_SIZE(DEFAULT_HASH_ALGS))) {
-        return false;
+    if (prefs.hash_algs.empty()) {
+        prefs.set_hash_algs(std::vector<uint8_t>(
+          DEFAULT_HASH_ALGS, DEFAULT_HASH_ALGS + ARRAY_SIZE(DEFAULT_HASH_ALGS)));
     }
-    if (!prefs->z_algs && !pgp_user_prefs_set_z_algs(prefs,
-                                                     &DEFAULT_COMPRESS_ALGS[0],
-                                                     ARRAY_SIZE(DEFAULT_COMPRESS_ALGS))) {
-        return false;
+    if (prefs.z_algs.empty()) {
+        prefs.set_z_algs(std::vector<uint8_t>(
+          DEFAULT_COMPRESS_ALGS, DEFAULT_COMPRESS_ALGS + ARRAY_SIZE(DEFAULT_COMPRESS_ALGS)));
     }
-    return true;
 }
 
 static const char *
 pgp_show_pka(pgp_pubkey_alg_t pka)
 {
     return pgp_str_from_map(pka, pubkey_alg_map);
 }
 
 static void
-keygen_primary_merge_defaults(rnp_keygen_primary_desc_t *desc)
+keygen_primary_merge_defaults(rnp_keygen_primary_desc_t &desc)
 {
-    keygen_merge_crypto_defaults(&desc->crypto);
-    set_default_user_prefs(&desc->cert.prefs);
+    keygen_merge_crypto_defaults(desc.crypto);
+    set_default_user_prefs(desc.cert.prefs);
 
-    if (!desc->cert.key_flags) {
+    if (!desc.cert.key_flags) {
         // set some default key flags if none are provided
-        desc->cert.key_flags = pk_alg_default_flags(desc->crypto.key_alg);
+        desc.cert.key_flags = pk_alg_default_flags(desc.crypto.key_alg);
     }
-    if (desc->cert.userid[0] == '\0') {
-        snprintf((char *) desc->cert.userid,
-                 sizeof(desc->cert.userid),
+    if (desc.cert.userid[0] == '\0') {
+        snprintf((char *) desc.cert.userid,
+                 sizeof(desc.cert.userid),
                  "%s %d-bit key <%s@localhost>",
-                 pgp_show_pka(desc->crypto.key_alg),
-                 get_numbits(&desc->crypto),
+                 pgp_show_pka(desc.crypto.key_alg),
+                 get_numbits(&desc.crypto),
                  getenv_logname());
     }
 }
 
 static void
 pgp_key_mark_valid(pgp_key_t *key)
 {
     key->valid = true;
@@ -350,96 +348,94 @@ pgp_key_mark_valid(pgp_key_t *key)
 
 bool
 pgp_generate_primary_key(rnp_keygen_primary_desc_t *desc,
                          bool                       merge_defaults,
                          pgp_key_t *                primary_sec,
                          pgp_key_t *                primary_pub,
                          pgp_key_store_format_t     secformat)
 {
-    bool                       ok = false;
-    pgp_transferable_key_t     tkeysec;
-    pgp_transferable_key_t     tkeypub;
-    pgp_transferable_userid_t *uid = NULL;
-
     // validate args
     if (!desc || !primary_pub || !primary_sec) {
-        goto end;
+        return false;
     }
     if (pgp_key_get_type(primary_sec) || pgp_key_get_type(primary_pub)) {
         RNP_LOG("invalid parameters (should be zeroed)");
-        goto end;
+        return false;
     }
 
     // merge some defaults in, if requested
     if (merge_defaults) {
-        keygen_primary_merge_defaults(desc);
+        try {
+            keygen_primary_merge_defaults(*desc);
+        } catch (const std::exception &e) {
+            RNP_LOG("%s", e.what());
+            return false;
+        }
     }
 
     // now validate the keygen fields
     if (!validate_keygen_primary(desc)) {
-        goto end;
+        return false;
     }
 
     // generate the raw key and fill tag/secret fields
+    pgp_transferable_key_t tkeysec;
     if (!pgp_generate_seckey(&desc->crypto, &tkeysec.key, true)) {
-        goto end;
+        return false;
     }
 
-    uid = transferable_key_add_userid(tkeysec, (char *) desc->cert.userid);
+    pgp_transferable_userid_t *uid =
+      transferable_key_add_userid(tkeysec, (char *) desc->cert.userid);
     if (!uid) {
         RNP_LOG("failed to add userid");
-        goto end;
+        return false;
     }
 
     if (!transferable_userid_certify(
           tkeysec.key, *uid, tkeysec.key, desc->crypto.hash_alg, desc->cert)) {
         RNP_LOG("failed to certify key");
-        goto end;
+        return false;
     }
 
+    pgp_transferable_key_t tkeypub;
     try {
         tkeypub = pgp_transferable_key_t(tkeysec, true);
     } catch (const std::exception &e) {
         RNP_LOG("failed to copy public key part: %s", e.what());
-        goto end;
+        return false;
     }
 
     if (!rnp_key_from_transferable_key(primary_pub, &tkeypub)) {
-        goto end;
+        return false;
     }
 
     switch (secformat) {
     case PGP_KEY_STORE_GPG:
     case PGP_KEY_STORE_KBX:
         if (!rnp_key_from_transferable_key(primary_sec, &tkeysec)) {
-            goto end;
+            return false;
         }
         break;
     case PGP_KEY_STORE_G10:
         if (!load_generated_g10_key(primary_sec, &tkeysec.key, NULL, primary_pub)) {
             RNP_LOG("failed to load generated key");
-            goto end;
+            return false;
         }
         break;
     default:
         RNP_LOG("invalid format");
-        goto end;
-        break;
+        return false;
     }
 
     /* mark it as valid */
     pgp_key_mark_valid(primary_pub);
     pgp_key_mark_valid(primary_sec);
     /* refresh key's data */
-    ok = pgp_key_refresh_data(primary_pub) && pgp_key_refresh_data(primary_sec);
-end:
-    // free any user preferences
-    pgp_free_user_prefs(&desc->cert.prefs);
-    return ok;
+    return pgp_key_refresh_data(primary_pub) && pgp_key_refresh_data(primary_sec);
 }
 
 static bool
 validate_keygen_subkey(rnp_keygen_subkey_desc_t *desc)
 {
     if (!desc->binding.key_flags) {
         RNP_LOG("key flags are required");
         return false;
@@ -447,22 +443,22 @@ validate_keygen_subkey(rnp_keygen_subkey
         // check the flags against the alg capabilities
         RNP_LOG("usage not permitted for pk algorithm");
         return false;
     }
     return true;
 }
 
 static void
-keygen_subkey_merge_defaults(rnp_keygen_subkey_desc_t *desc)
+keygen_subkey_merge_defaults(rnp_keygen_subkey_desc_t &desc)
 {
-    keygen_merge_crypto_defaults(&desc->crypto);
-    if (!desc->binding.key_flags) {
+    keygen_merge_crypto_defaults(desc.crypto);
+    if (!desc.binding.key_flags) {
         // set some default key flags if none are provided
-        desc->binding.key_flags = pk_alg_default_flags(desc->crypto.key_alg);
+        desc.binding.key_flags = pk_alg_default_flags(desc.crypto.key_alg);
     }
 }
 
 bool
 pgp_generate_subkey(rnp_keygen_subkey_desc_t *     desc,
                     bool                           merge_defaults,
                     pgp_key_t *                    primary_sec,
                     pgp_key_t *                    primary_pub,
@@ -490,17 +486,17 @@ pgp_generate_subkey(rnp_keygen_subkey_de
     }
     if (pgp_key_get_type(subkey_sec) || pgp_key_get_type(subkey_pub)) {
         RNP_LOG("invalid parameters (should be zeroed)");
         goto end;
     }
 
     // merge some defaults in, if requested
     if (merge_defaults) {
-        keygen_subkey_merge_defaults(desc);
+        keygen_subkey_merge_defaults(*desc);
     }
 
     // now validate the keygen fields
     if (!validate_keygen_subkey(desc)) {
         goto end;
     }
 
     ctx = {.op = PGP_OP_ADD_SUBKEY, .key = primary_sec};
--- a/third_party/rnp/src/lib/pgp-key.cpp
+++ b/third_party/rnp/src/lib/pgp-key.cpp
@@ -68,115 +68,16 @@
 #include <stdlib.h>
 #include <assert.h>
 #include <time.h>
 #include <algorithm>
 #include <stdexcept>
 #include "defaults.h"
 
 static bool
-pgp_user_prefs_set_arr(uint8_t **arr, size_t *arrlen, const uint8_t *val, size_t len)
-{
-    uint8_t *newarr = (uint8_t *) malloc(len);
-
-    if (len && !newarr) {
-        return false;
-    }
-
-    free(*arr);
-    memcpy(newarr, val, len);
-    *arrlen = len;
-    *arr = newarr;
-    return true;
-}
-
-static bool
-pgp_user_prefs_add_val(uint8_t **arr, size_t *arrlen, uint8_t val)
-{
-    /* do not add duplicate values */
-    for (size_t i = 0; i < *arrlen; i++) {
-        if ((*arr)[i] == val) {
-            return true;
-        }
-    }
-
-    uint8_t *newarr = (uint8_t *) realloc(*arr, *arrlen + 1);
-
-    if (!newarr) {
-        return false;
-    }
-
-    newarr[(*arrlen)++] = val;
-    *arr = newarr;
-    return true;
-}
-
-bool
-pgp_user_prefs_set_symm_algs(pgp_user_prefs_t *prefs, const uint8_t *algs, size_t len)
-{
-    return pgp_user_prefs_set_arr(&prefs->symm_algs, &prefs->symm_alg_count, algs, len);
-}
-
-bool
-pgp_user_prefs_set_hash_algs(pgp_user_prefs_t *prefs, const uint8_t *algs, size_t len)
-{
-    return pgp_user_prefs_set_arr(&prefs->hash_algs, &prefs->hash_alg_count, algs, len);
-}
-
-bool
-pgp_user_prefs_set_z_algs(pgp_user_prefs_t *prefs, const uint8_t *algs, size_t len)
-{
-    return pgp_user_prefs_set_arr(&prefs->z_algs, &prefs->z_alg_count, algs, len);
-}
-
-bool
-pgp_user_prefs_set_ks_prefs(pgp_user_prefs_t *prefs, const uint8_t *vals, size_t len)
-{
-    return pgp_user_prefs_set_arr(&prefs->ks_prefs, &prefs->ks_pref_count, vals, len);
-}
-
-bool
-pgp_user_prefs_add_symm_alg(pgp_user_prefs_t *prefs, pgp_symm_alg_t alg)
-{
-    return pgp_user_prefs_add_val(&prefs->symm_algs, &prefs->symm_alg_count, alg);
-}
-
-bool
-pgp_user_prefs_add_hash_alg(pgp_user_prefs_t *prefs, pgp_hash_alg_t alg)
-{
-    return pgp_user_prefs_add_val(&prefs->hash_algs, &prefs->hash_alg_count, alg);
-}
-
-bool
-pgp_user_prefs_add_z_alg(pgp_user_prefs_t *prefs, pgp_compression_type_t alg)
-{
-    return pgp_user_prefs_add_val(&prefs->z_algs, &prefs->z_alg_count, alg);
-}
-
-bool
-pgp_user_prefs_add_ks_pref(pgp_user_prefs_t *prefs, pgp_key_server_prefs_t val)
-{
-    return pgp_user_prefs_add_val(&prefs->ks_prefs, &prefs->ks_pref_count, val);
-}
-
-void
-pgp_free_user_prefs(pgp_user_prefs_t *prefs)
-{
-    if (!prefs) {
-        return;
-    }
-    free(prefs->symm_algs);
-    free(prefs->hash_algs);
-    free(prefs->z_algs);
-    free(prefs->ks_prefs);
-    free(prefs->key_server);
-    memset(prefs, 0, sizeof(*prefs));
-}
-
-static bool
 pgp_key_init_with_pkt(pgp_key_t *key, const pgp_key_pkt_t *pkt)
 {
     assert(!key->pkt.version);
     assert(is_key_pkt(pkt->tag));
     assert(pkt->material.alg);
     if (pgp_keyid(key->keyid, pkt) || pgp_fingerprint(key->fingerprint, pkt) ||
         !rnp_key_store_get_key_grip(&pkt->material, key->grip)) {
         return false;
@@ -221,56 +122,16 @@ pgp_key_from_pkt(pgp_key_t *key, const p
 static void
 pgp_key_clear_revokes(pgp_key_t *key)
 {
     key->revoked = false;
     key->revokes.clear();
     key->revocation = {};
 }
 
-static rnp_result_t
-pgp_userprefs_copy(pgp_user_prefs_t *dst, const pgp_user_prefs_t *src)
-{
-    rnp_result_t ret = RNP_ERROR_OUT_OF_MEMORY;
-
-    memset(dst, 0, sizeof(*dst));
-    if (src->symm_alg_count &&
-        !pgp_user_prefs_set_symm_algs(dst, src->symm_algs, src->symm_alg_count)) {
-        return ret;
-    }
-
-    if (src->hash_alg_count &&
-        !pgp_user_prefs_set_hash_algs(dst, src->hash_algs, src->hash_alg_count)) {
-        goto error;
-    }
-
-    if (src->z_alg_count && !pgp_user_prefs_set_z_algs(dst, src->z_algs, src->z_alg_count)) {
-        goto error;
-    }
-
-    if (src->ks_pref_count &&
-        !pgp_user_prefs_set_ks_prefs(dst, src->ks_prefs, src->ks_pref_count)) {
-        goto error;
-    }
-
-    if (src->key_server) {
-        size_t len = strlen((char *) src->key_server) + 1;
-        dst->key_server = (uint8_t *) malloc(len);
-        if (!dst->key_server) {
-            goto error;
-        }
-        memcpy(dst->key_server, src->key_server, len);
-    }
-
-    return RNP_SUCCESS;
-error:
-    pgp_free_user_prefs(dst);
-    return ret;
-}
-
 /**
  \ingroup HighLevel_KeyGeneral
 
  \brief Returns the public key in the given key.
  \param key
 
   \return Pointer to public key
 
@@ -649,66 +510,50 @@ pgp_key_get_subsig(const pgp_key_t *key,
 
 pgp_subsig_t *
 pgp_key_get_subsig(pgp_key_t *key, size_t idx)
 {
     return (idx < key->subsigs.size()) ? &key->subsigs[idx] : NULL;
 }
 
 bool
-pgp_subsig_from_signature(pgp_subsig_t *dst, const pgp_signature_t *sig)
+pgp_subsig_from_signature(pgp_subsig_t &dst, const pgp_signature_t &sig)
 {
     pgp_subsig_t subsig = {};
-    subsig.sig = *sig;
-    if (signature_has_trust(&subsig.sig)) {
-        signature_get_trust(&subsig.sig, &subsig.trustlevel, &subsig.trustamount);
-    }
-    uint8_t *algs = NULL;
-    size_t   count = 0;
-    if (signature_get_preferred_symm_algs(&subsig.sig, &algs, &count) &&
-        !pgp_user_prefs_set_symm_algs(&subsig.prefs, algs, count)) {
-        RNP_LOG("failed to alloc symm algs");
-        return false;
-    }
-    if (signature_get_preferred_hash_algs(&subsig.sig, &algs, &count) &&
-        !pgp_user_prefs_set_hash_algs(&subsig.prefs, algs, count)) {
-        RNP_LOG("failed to alloc hash algs");
-        return false;
+    subsig.sig = sig;
+    if (subsig.sig.has_subpkt(PGP_SIG_SUBPKT_TRUST)) {
+        subsig.trustlevel = subsig.sig.trust_level();
+        subsig.trustamount = subsig.sig.trust_amount();
     }
-    if (signature_get_preferred_z_algs(&subsig.sig, &algs, &count) &&
-        !pgp_user_prefs_set_z_algs(&subsig.prefs, algs, count)) {
-        RNP_LOG("failed to alloc z algs");
+    try {
+        subsig.prefs.set_symm_algs(subsig.sig.preferred_symm_algs());
+        subsig.prefs.set_hash_algs(subsig.sig.preferred_hash_algs());
+        subsig.prefs.set_z_algs(subsig.sig.preferred_z_algs());
+
+        if (subsig.sig.has_subpkt(PGP_SIG_SUBPKT_KEY_FLAGS)) {
+            subsig.key_flags = subsig.sig.key_flags();
+        }
+        if (subsig.sig.has_subpkt(PGP_SIG_SUBPKT_KEYSERV_PREFS)) {
+            subsig.prefs.set_ks_prefs({subsig.sig.key_server_prefs()});
+        }
+        if (subsig.sig.has_subpkt(PGP_SIG_SUBPKT_PREF_KEYSERV)) {
+            subsig.prefs.key_server = subsig.sig.key_server();
+        }
+    } catch (const std::exception &e) {
+        RNP_LOG("Failed to copy preferences: %s", e.what());
         return false;
     }
-    if (signature_has_key_flags(&subsig.sig)) {
-        subsig.key_flags = signature_get_key_flags(&subsig.sig);
-    }
-    if (signature_has_key_server_prefs(&subsig.sig)) {
-        uint8_t ks_pref = signature_get_key_server_prefs(&subsig.sig);
-        if (!pgp_user_prefs_set_ks_prefs(&subsig.prefs, &ks_pref, 1)) {
-            RNP_LOG("failed to alloc ks prefs");
-            return false;
-        }
-    }
-    if (signature_has_key_server(&subsig.sig)) {
-        subsig.prefs.key_server = (uint8_t *) signature_get_key_server(&subsig.sig);
-        if (!subsig.prefs.key_server) {
-            RNP_LOG("failed to alloc ks");
-            return false;
-        }
-    }
     /* add signature rawpacket */
     try {
-        subsig.rawpkt = pgp_rawpacket_t(*sig);
+        subsig.rawpkt = pgp_rawpacket_t(sig);
     } catch (const std::exception &e) {
         RNP_LOG("failed to build sig rawpacket: %s", e.what());
         return false;
     }
-
-    *dst = std::move(subsig);
+    dst = std::move(subsig);
     return true;
 }
 
 bool
 pgp_key_has_signature(const pgp_key_t *key, const pgp_signature_t *sig)
 {
     for (size_t i = 0; i < pgp_key_get_subsig_count(key); i++) {
         const pgp_subsig_t *subsig = pgp_key_get_subsig(key, i);
@@ -742,17 +587,17 @@ pgp_key_replace_signature(pgp_key_t *key
         newraw = *newsig;
     } catch (const std::exception &e) {
         RNP_LOG("failed to create rawpacket: %s", e.what());
         return NULL;
     }
 
     /* fill new subsig */
     pgp_subsig_t newsubsig = {};
-    if (!pgp_subsig_from_signature(&newsubsig, newsig)) {
+    if (!pgp_subsig_from_signature(newsubsig, *newsig)) {
         RNP_LOG("failed to fill subsig");
         return NULL;
     }
     newsubsig.uid = subsig->uid;
     /* replace rawpacket */
     try {
         newsubsig.rawpkt = pgp_rawpacket_t(*newsig);
     } catch (const std::exception &e) {
@@ -762,83 +607,76 @@ pgp_key_replace_signature(pgp_key_t *key
 
     *subsig = std::move(newsubsig);
     return subsig;
 }
 
 static bool
 pgp_sig_is_certification(const pgp_subsig_t *sig)
 {
-    pgp_sig_type_t type = signature_get_type(&sig->sig);
+    pgp_sig_type_t type = sig->sig.type();
     return (type == PGP_CERT_CASUAL) || (type == PGP_CERT_GENERIC) ||
            (type == PGP_CERT_PERSONA) || (type == PGP_CERT_POSITIVE);
 }
 
 static bool
 pgp_sig_self_signed(const pgp_key_t *key, const pgp_subsig_t *sig)
 {
     /* if we have fingerprint let's check it */
-    if (signature_has_keyfp(&sig->sig)) {
-        pgp_fingerprint_t sigfp = {};
-        if (signature_get_keyfp(&sig->sig, sigfp)) {
-            return pgp_key_get_fp(key) == sigfp;
-        }
+    if (sig->sig.has_keyfp()) {
+        return sig->sig.keyfp() == pgp_key_get_fp(key);
     }
-    if (!signature_has_keyid(&sig->sig)) {
+    if (!sig->sig.has_keyid()) {
         return false;
     }
-    pgp_key_id_t sigid = {};
-    if (!signature_get_keyid(&sig->sig, sigid)) {
-        return false;
-    }
-    return pgp_key_get_keyid(key) == sigid;
+    return pgp_key_get_keyid(key) == sig->sig.keyid();
 }
 
 static bool
 pgp_sig_is_self_signature(const pgp_key_t *key, const pgp_subsig_t *sig)
 {
     if (!pgp_key_is_primary_key(key) || !pgp_sig_is_certification(sig)) {
         return false;
     }
 
     return pgp_sig_self_signed(key, sig);
 }
 
 static bool
 pgp_sig_is_direct_self_signature(const pgp_key_t *key, const pgp_subsig_t *sig)
 {
-    if (!pgp_key_is_primary_key(key) || (signature_get_type(&sig->sig) != PGP_SIG_DIRECT)) {
+    if (!pgp_key_is_primary_key(key) || (sig->sig.type() != PGP_SIG_DIRECT)) {
         return false;
     }
 
     return pgp_sig_self_signed(key, sig);
 }
 
 static bool
 pgp_sig_is_key_revocation(const pgp_key_t *key, const pgp_subsig_t *sig)
 {
-    return pgp_key_is_primary_key(key) && (signature_get_type(&sig->sig) == PGP_SIG_REV_KEY);
+    return pgp_key_is_primary_key(key) && (sig->sig.type() == PGP_SIG_REV_KEY);
 }
 
 static bool
 pgp_sig_is_userid_revocation(const pgp_key_t *key, const pgp_subsig_t *sig)
 {
-    return pgp_key_is_primary_key(key) && (signature_get_type(&sig->sig) == PGP_SIG_REV_CERT);
+    return pgp_key_is_primary_key(key) && (sig->sig.type() == PGP_SIG_REV_CERT);
 }
 
 static bool
 pgp_sig_is_subkey_binding(const pgp_key_t *key, const pgp_subsig_t *sig)
 {
-    return pgp_key_is_subkey(key) && (signature_get_type(&sig->sig) == PGP_SIG_SUBKEY);
+    return pgp_key_is_subkey(key) && (sig->sig.type() == PGP_SIG_SUBKEY);
 }
 
 static bool
 pgp_sig_is_subkey_revocation(const pgp_key_t *key, const pgp_subsig_t *sig)
 {
-    return pgp_key_is_subkey(key) && (signature_get_type(&sig->sig) == PGP_SIG_REV_SUBKEY);
+    return pgp_key_is_subkey(key) && (sig->sig.type() == PGP_SIG_REV_SUBKEY);
 }
 
 pgp_subsig_t *
 pgp_key_latest_selfsig(pgp_key_t *key, pgp_sig_subpacket_type_t subpkt)
 {
     uint32_t      latest = 0;
     pgp_subsig_t *res = NULL;
 
@@ -847,21 +685,21 @@ pgp_key_latest_selfsig(pgp_key_t *key, p
         if (!sig->valid) {
             continue;
         }
         if (!pgp_sig_is_self_signature(key, sig) &&
             !pgp_sig_is_direct_self_signature(key, sig)) {
             continue;
         }
 
-        if (subpkt && !signature_get_subpkt(&sig->sig, subpkt)) {
+        if (subpkt && !sig->sig.get_subpkt(subpkt)) {
             continue;
         }
 
-        uint32_t creation = signature_get_creation(&sig->sig);
+        uint32_t creation = sig->sig.creation();
         if (creation >= latest) {
             latest = creation;
             res = sig;
         }
     }
     return res;
 }
 
@@ -875,17 +713,17 @@ pgp_key_latest_uid_selfcert(pgp_key_t *k
         pgp_subsig_t *sig = pgp_key_get_subsig(key, i);
         if (!sig->valid || (sig->uid != uid)) {
             continue;
         }
         if (!pgp_sig_is_self_signature(key, sig)) {
             continue;
         }
 
-        uint32_t creation = signature_get_creation(&sig->sig);
+        uint32_t creation = sig->sig.creation();
         if (creation >= latest) {
             latest = creation;
             res = sig;
         }
     }
     return res;
 }
 
@@ -899,17 +737,17 @@ pgp_key_latest_binding(pgp_key_t *subkey
         pgp_subsig_t *sig = pgp_key_get_subsig(subkey, i);
         if (validated && !sig->valid) {
             continue;
         }
         if (!pgp_sig_is_subkey_binding(subkey, sig)) {
             continue;
         }
 
-        uint32_t creation = signature_get_creation(&sig->sig);
+        uint32_t creation = sig->sig.creation();
         if (creation >= latest) {
             latest = creation;
             res = sig;
         }
     }
     return res;
 }
 
@@ -934,17 +772,17 @@ pgp_key_validate_signature(pgp_key_t *  
     pgp_signature_info_t sinfo = {};
     sinfo.sig = &sig->sig;
     sinfo.signer = signer;
     sinfo.signer_valid = true;
     if (pgp_sig_is_self_signature(key, sig) || pgp_sig_is_subkey_binding(key, sig)) {
         sinfo.ignore_expiry = true;
     }
 
-    pgp_sig_type_t stype = signature_get_type(&sig->sig);
+    pgp_sig_type_t stype = sig->sig.type();
     switch (stype) {
     case PGP_SIG_BINARY:
     case PGP_SIG_TEXT:
     case PGP_SIG_STANDALONE:
     case PGP_SIG_PRIMARY:
         RNP_LOG("Invalid key signature type: %d", (int) stype);
         return;
     case PGP_CERT_GENERIC:
@@ -1031,48 +869,46 @@ bool
 pgp_subkey_refresh_data(pgp_key_t *sub, pgp_key_t *key)
 {
     /* validate self-signatures if not done yet */
     if (key) {
         pgp_subkey_validate_self_signatures(sub, key);
     }
     pgp_subsig_t *sig = pgp_key_latest_binding(sub, key);
     /* subkey expiration */
-    sub->expiration = sig ? signature_get_key_expiration(&sig->sig) : 0;
+    sub->expiration = sig ? sig->sig.key_expiration() : 0;
     /* subkey flags */
-    if (sig && signature_has_key_flags(&sig->sig)) {
+    if (sig && sig->sig.has_subpkt(PGP_SIG_SUBPKT_KEY_FLAGS)) {
         sub->key_flags = sig->key_flags;
     } else {
         sub->key_flags = pgp_pk_alg_capabilities(pgp_key_get_alg(sub));
     }
     /* revocation */
     pgp_key_clear_revokes(sub);
     for (size_t i = 0; i < pgp_key_get_subsig_count(sub); i++) {
         sig = pgp_key_get_subsig(sub, i);
         if (!sig->valid || !pgp_sig_is_subkey_revocation(sub, sig)) {
             continue;
         }
         sub->revoked = true;
-        char *reason = NULL;
-        if (!signature_has_revocation_reason(&sig->sig)) {
-            RNP_LOG("Warning: no revocation reason in subkey revocation");
-            sub->revocation.code = PGP_REVOCATION_NO_REASON;
-        } else if (!signature_get_revocation_reason(
-                     &sig->sig, &sub->revocation.code, &reason)) {
-            return false;
-        }
+        try {
+            if (!sig->sig.has_subpkt(PGP_SIG_SUBPKT_REVOCATION_REASON)) {
+                RNP_LOG("Warning: no revocation reason in subkey revocation");
+                sub->revocation.code = PGP_REVOCATION_NO_REASON;
+            } else {
+                sub->revocation.code = sig->sig.revocation_code();
+                sub->revocation.reason = sig->sig.revocation_reason();
+            }
 
-        try {
-            sub->revocation.reason = (reason && strlen(reason)) ?
-                                       reason :
-                                       pgp_str_from_map(sub->revocation.code, ss_rr_code_map);
-            free(reason);
+            if (sub->revocation.reason.empty()) {
+                sub->revocation.reason =
+                  pgp_str_from_map(sub->revocation.code, ss_rr_code_map);
+            }
         } catch (const std::exception &e) {
             RNP_LOG("%s", e.what());
-            free(reason);
             return false;
         }
         break;
     }
     return true;
 }
 
 bool
@@ -1081,31 +917,31 @@ pgp_key_refresh_data(pgp_key_t *key)
     if (!pgp_key_is_primary_key(key)) {
         RNP_LOG("key must be primary");
         return false;
     }
     /* validate self-signatures if not done yet */
     pgp_key_validate_self_signatures(key);
     /* key expiration */
     pgp_subsig_t *sig = pgp_key_latest_selfsig(key, PGP_SIG_SUBPKT_UNKNOWN);
-    key->expiration = sig ? signature_get_key_expiration(&sig->sig) : 0;
+    key->expiration = sig ? sig->sig.key_expiration() : 0;
     /* key flags */
-    if (sig && signature_has_key_flags(&sig->sig)) {
+    if (sig && sig->sig.has_subpkt(PGP_SIG_SUBPKT_KEY_FLAGS)) {
         key->key_flags = sig->key_flags;
     } else {
         key->key_flags = pgp_pk_alg_capabilities(pgp_key_get_alg(key));
     }
     /* primary userid */
     key->uid0_set = false;
     for (size_t i = 0; i < pgp_key_get_subsig_count(key); i++) {
         sig = pgp_key_get_subsig(key, i);
         if (!sig->valid || !pgp_sig_is_self_signature(key, sig)) {
             continue;
         }
-        if (signature_get_primary_uid(&sig->sig)) {
+        if (sig->sig.primary_uid()) {
             key->uid0 = sig->uid;
             key->uid0_set = true;
             break;
         }
     }
     /* revocation(s) */
     pgp_key_clear_revokes(key);
     for (size_t i = 0; i < pgp_key_get_subsig_count(key); i++) {
@@ -1128,32 +964,29 @@ pgp_key_refresh_data(pgp_key_t *key)
             }
             revocation->uid = sig->uid;
         }
 
         if (!revocation) {
             continue;
         }
 
-        char *reason = NULL;
-        if (!signature_has_revocation_reason(&sig->sig)) {
-            RNP_LOG("Warning: no revocation reason in key/userid revocation");
-            revocation->code = PGP_REVOCATION_NO_REASON;
-        } else if (!signature_get_revocation_reason(&sig->sig, &revocation->code, &reason)) {
-            return false;
-        }
-
         try {
-            revocation->reason = (reason && strlen(reason)) ?
-                                   reason :
-                                   pgp_str_from_map(revocation->code, ss_rr_code_map);
-            free(reason);
+            if (!sig->sig.has_subpkt(PGP_SIG_SUBPKT_REVOCATION_REASON)) {
+                RNP_LOG("Warning: no revocation reason in key/userid revocation");
+                revocation->code = PGP_REVOCATION_NO_REASON;
+            } else {
+                revocation->code = sig->sig.revocation_code();
+                revocation->reason = sig->sig.revocation_reason();
+            }
+            if (revocation->reason.empty()) {
+                revocation->reason = pgp_str_from_map(revocation->code, ss_rr_code_map);
+            }
         } catch (const std::exception &e) {
             RNP_LOG("%s", e.what());
-            free(reason);
             return false;
         }
     }
 
     return true;
 }
 
 size_t
@@ -1602,28 +1435,27 @@ pgp_key_add_userid_certified(pgp_key_t *
     return rnp_key_add_transferable_userid(key, &uid) && pgp_key_refresh_data(key);
 }
 
 static bool
 update_sig_expiration(pgp_signature_t *dst, const pgp_signature_t *src, uint32_t expiry)
 {
     try {
         *dst = *src;
+        if (!expiry) {
+            dst->remove_subpkt(dst->get_subpkt(PGP_SIG_SUBPKT_KEY_EXPIRY));
+        } else {
+            dst->set_key_expiration(expiry);
+        }
+        dst->set_creation(time(NULL));
+        return true;
     } catch (const std::exception &e) {
         RNP_LOG("%s", e.what());
         return false;
     }
-    if (!expiry) {
-        pgp_sig_subpkt_t *subpkt = signature_get_subpkt(dst, PGP_SIG_SUBPKT_KEY_EXPIRY);
-        signature_remove_subpkt(dst, subpkt);
-    } else {
-        signature_set_key_expiration(dst, expiry);
-    }
-    signature_set_creation(dst, time(NULL));
-    return true;
 }
 
 bool
 pgp_key_set_expiration(pgp_key_t *                    key,
                        pgp_key_t *                    seckey,
                        uint32_t                       expiry,
                        const pgp_password_provider_t *prov)
 {
@@ -1635,17 +1467,17 @@ pgp_key_set_expiration(pgp_key_t *      
     /* locate the latest valid certification */
     pgp_subsig_t *subsig = pgp_key_latest_selfsig(key, PGP_SIG_SUBPKT_UNKNOWN);
     if (!subsig) {
         RNP_LOG("No valid self-signature");
         return false;
     }
 
     /* update signature and re-sign it */
-    if (!expiry && !signature_has_key_expiration(&subsig->sig)) {
+    if (!expiry && !subsig->sig.has_subpkt(PGP_SIG_SUBPKT_KEY_EXPIRY)) {
         return true;
     }
 
     bool locked = pgp_key_is_locked(seckey);
     if (locked && !pgp_key_unlock(seckey, prov)) {
         RNP_LOG("Failed to unlock secret key");
         return false;
     }
@@ -1701,17 +1533,17 @@ pgp_subkey_set_expiration(pgp_key_t *   
     }
 
     /* find the latest valid subkey binding */
     pgp_subsig_t *subsig = pgp_key_latest_binding(sub, true);
     if (!subsig) {
         RNP_LOG("No valid subkey binding");
         return false;
     }
-    if (!expiry && !signature_has_key_expiration(&subsig->sig)) {
+    if (!expiry && !subsig->sig.has_subpkt(PGP_SIG_SUBPKT_KEY_EXPIRY)) {
         return true;
     }
 
     bool res = false;
     bool subsign = pgp_key_get_flags(secsub) & PGP_KF_SIGN;
     bool locked = pgp_key_is_locked(primsec);
     if (locked && !pgp_key_unlock(primsec, prov)) {
         RNP_LOG("Failed to unlock primary key");
@@ -1883,24 +1715,28 @@ find_suitable_key(pgp_op_t            op
         return NULL;
     }
     if (pgp_key_get_flags(key) & desired_usage) {
         return key;
     }
     pgp_key_request_ctx_t ctx{.op = op, .secret = pgp_key_is_secret(key)};
     ctx.search.type = PGP_KEY_SEARCH_FINGERPRINT;
 
+    pgp_key_t *subkey = NULL;
     for (auto &fp : key->subkey_fps) {
         ctx.search.by.fingerprint = fp;
-        pgp_key_t *subkey = pgp_request_key(key_provider, &ctx);
-        if (subkey && (pgp_key_get_flags(subkey) & desired_usage)) {
-            return subkey;
+        pgp_key_t *cur = pgp_request_key(key_provider, &ctx);
+        if (!cur || !(pgp_key_get_flags(cur) & desired_usage) || !cur->valid) {
+            continue;
+        }
+        if (!subkey || (pgp_key_get_creation(cur) > pgp_key_get_creation(subkey))) {
+            subkey = cur;
         }
     }
-    return NULL;
+    return subkey;
 }
 
 pgp_hash_alg_t
 pgp_hash_adjust_alg_to_key(pgp_hash_alg_t hash, const pgp_key_pkt_t *pubkey)
 {
     if ((pubkey->alg != PGP_PKA_DSA) && (pubkey->alg != PGP_PKA_ECDSA)) {
         return hash;
     }
@@ -1917,17 +1753,17 @@ pgp_hash_adjust_alg_to_key(pgp_hash_alg_
     }
     return hash;
 }
 
 static bool
 is_key_expired(const pgp_key_t &key, const pgp_subsig_t &sig)
 {
     /* key expiration: absense of subpkt or 0 means it never expires */
-    uint32_t expiration = signature_get_key_expiration(&sig.sig);
+    uint32_t expiration = sig.sig.key_expiration();
     if (!expiration) {
         return false;
     }
     return pgp_key_get_creation(&key) + expiration < time(NULL);
 }
 
 static void
 pgp_key_validate_primary(pgp_key_t *key, rnp_key_store_t *keyring)
@@ -2071,16 +1907,72 @@ mem_dest_to_vector(pgp_dest_t *dst, std:
         dst_close(dst, true);
     } catch (const std::exception &e) {
         RNP_LOG("%s", e.what());
         dst_close(dst, true);
         throw;
     }
 }
 
+static void
+bytevec_append_uniq(std::vector<uint8_t> &vec, uint8_t val)
+{
+    if (std::find(vec.begin(), vec.end(), val) == vec.end()) {
+        vec.push_back(val);
+    }
+}
+
+void
+pgp_user_prefs_t::set_symm_algs(const std::vector<uint8_t> &algs)
+{
+    symm_algs = algs;
+}
+
+void
+pgp_user_prefs_t::add_symm_alg(pgp_symm_alg_t alg)
+{
+    bytevec_append_uniq(symm_algs, alg);
+}
+
+void
+pgp_user_prefs_t::set_hash_algs(const std::vector<uint8_t> &algs)
+{
+    hash_algs = algs;
+}
+
+void
+pgp_user_prefs_t::add_hash_alg(pgp_hash_alg_t alg)
+{
+    bytevec_append_uniq(hash_algs, alg);
+}
+
+void
+pgp_user_prefs_t::set_z_algs(const std::vector<uint8_t> &algs)
+{
+    z_algs = algs;
+}
+
+void
+pgp_user_prefs_t::add_z_alg(pgp_compression_type_t alg)
+{
+    bytevec_append_uniq(z_algs, alg);
+}
+
+void
+pgp_user_prefs_t::set_ks_prefs(const std::vector<uint8_t> &prefs)
+{
+    ks_prefs = prefs;
+}
+
+void
+pgp_user_prefs_t::add_ks_pref(pgp_key_server_prefs_t pref)
+{
+    bytevec_append_uniq(ks_prefs, pref);
+}
+
 pgp_rawpacket_t::pgp_rawpacket_t(const pgp_signature_t &sig)
 {
     pgp_dest_t dst = {};
 
     if (init_mem_dest(&dst, NULL, 0)) {
         throw std::bad_alloc();
     }
 
@@ -2122,93 +2014,16 @@ pgp_rawpacket_t::pgp_rawpacket_t(const p
         dst_close(&dst, true);
         throw std::bad_alloc();
     }
 
     mem_dest_to_vector(&dst, raw);
     tag = uid.tag;
 }
 
-pgp_subsig_t::pgp_subsig_t(const pgp_subsig_t &src)
-{
-    uid = src.uid;
-    sig = src.sig;
-    rawpkt = src.rawpkt;
-    trustlevel = src.trustlevel;
-    trustamount = src.trustamount;
-    key_flags = src.key_flags;
-    if (pgp_userprefs_copy(&prefs, &src.prefs)) {
-        throw std::bad_alloc();
-    }
-    validated = src.validated;
-    valid = src.valid;
-}
-
-pgp_subsig_t::pgp_subsig_t(pgp_subsig_t &&src)
-{
-    uid = src.uid;
-    sig = std::move(src.sig);
-    rawpkt = std::move(src.rawpkt);
-    trustlevel = src.trustlevel;
-    trustamount = src.trustamount;
-    key_flags = src.key_flags;
-    prefs = src.prefs;
-    src.prefs = {};
-    validated = src.validated;
-    valid = src.valid;
-}
-
-pgp_subsig_t &
-pgp_subsig_t::operator=(pgp_subsig_t &&src)
-{
-    if (&src == this) {
-        return *this;
-    }
-
-    pgp_free_user_prefs(&prefs);
-    uid = src.uid;
-    sig = std::move(src.sig);
-    rawpkt = std::move(src.rawpkt);
-    trustlevel = src.trustlevel;
-    trustamount = src.trustamount;
-    key_flags = src.key_flags;
-    prefs = src.prefs;
-    src.prefs = {};
-    validated = src.validated;
-    valid = src.valid;
-    return *this;
-}
-
-pgp_subsig_t &
-pgp_subsig_t::operator=(const pgp_subsig_t &src)
-{
-    if (&src == this) {
-        return *this;
-    }
-
-    pgp_free_user_prefs(&prefs);
-    uid = src.uid;
-    sig = src.sig;
-    rawpkt = src.rawpkt;
-    trustlevel = src.trustlevel;
-    trustamount = src.trustamount;
-    key_flags = src.key_flags;
-    if (pgp_userprefs_copy(&prefs, &src.prefs)) {
-        throw std::bad_alloc();
-    }
-    validated = src.validated;
-    valid = src.valid;
-    return *this;
-}
-
-pgp_subsig_t::~pgp_subsig_t()
-{
-    pgp_free_user_prefs(&prefs);
-}
-
 pgp_key_t::pgp_key_t(const pgp_key_t &src, bool pubonly)
 {
     /* Do some checks for g10 keys */
     if (src.format == PGP_KEY_STORE_G10) {
         if (pubonly) {
             RNP_LOG("attempt to copy public part from g10 key");
             throw std::invalid_argument("pubonly");
         }
--- a/third_party/rnp/src/lib/pgp-key.h
+++ b/third_party/rnp/src/lib/pgp-key.h
@@ -98,34 +98,16 @@ typedef struct rnp_key_store_t rnp_key_s
  * @brief Create pgp_key_t object from the OpenPGP key packet.
  *
  * @param key pointer to the key object, cannot be NULL.
  * @param pkt pointer to the key packet, cannot be NULL.
  * @return true if operation succeeded or false otherwise.
  */
 bool pgp_key_from_pkt(pgp_key_t *key, const pgp_key_pkt_t *pkt);
 
-void pgp_free_user_prefs(pgp_user_prefs_t *prefs);
-
-bool pgp_user_prefs_set_symm_algs(pgp_user_prefs_t *prefs, const uint8_t *algs, size_t len);
-
-bool pgp_user_prefs_add_symm_alg(pgp_user_prefs_t *prefs, pgp_symm_alg_t alg);
-
-bool pgp_user_prefs_set_hash_algs(pgp_user_prefs_t *prefs, const uint8_t *algs, size_t len);
-
-bool pgp_user_prefs_add_hash_alg(pgp_user_prefs_t *prefs, pgp_hash_alg_t alg);
-
-bool pgp_user_prefs_set_z_algs(pgp_user_prefs_t *prefs, const uint8_t *algs, size_t len);
-
-bool pgp_user_prefs_add_z_alg(pgp_user_prefs_t *prefs, pgp_compression_type_t alg);
-
-bool pgp_user_prefs_set_ks_prefs(pgp_user_prefs_t *prefs, const uint8_t *vals, size_t len);
-
-bool pgp_user_prefs_add_ks_pref(pgp_user_prefs_t *prefs, pgp_key_server_prefs_t val);
-
 const pgp_key_pkt_t *pgp_key_get_pkt(const pgp_key_t *);
 
 const pgp_key_material_t *pgp_key_get_material(const pgp_key_t *key);
 
 pgp_pubkey_alg_t pgp_key_get_alg(const pgp_key_t *key);
 
 size_t pgp_key_get_dsa_qbits(const pgp_key_t *key);
 
@@ -248,17 +230,17 @@ pgp_revoke_t *pgp_key_get_revoke(pgp_key
 
 pgp_subsig_t *pgp_key_add_subsig(pgp_key_t *);
 
 size_t pgp_key_get_subsig_count(const pgp_key_t *);
 
 const pgp_subsig_t *pgp_key_get_subsig(const pgp_key_t *, size_t);
 pgp_subsig_t *      pgp_key_get_subsig(pgp_key_t *, size_t);
 
-bool pgp_subsig_from_signature(pgp_subsig_t *subsig, const pgp_signature_t *sig);
+bool pgp_subsig_from_signature(pgp_subsig_t &subsig, const pgp_signature_t &sig);
 
 bool pgp_key_has_signature(const pgp_key_t *key, const pgp_signature_t *sig);
 
 pgp_subsig_t *pgp_key_replace_signature(pgp_key_t *      key,
                                         pgp_signature_t *oldsig,
                                         pgp_signature_t *newsig);
 
 /**
--- a/third_party/rnp/src/lib/rnp.cpp
+++ b/third_party/rnp/src/lib/rnp.cpp
@@ -22,22 +22,22 @@
  * 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 "crypto.h"
 #include "crypto/common.h"
-#include "list.h"
 #include "pgp-key.h"
 #include "defaults.h"
 #include <assert.h>
 #include <json_object.h>
 #include <json.h>
+#include <librekey/key_store_pgp.h>
 #include <librepgp/stream-ctx.h>
 #include <librepgp/stream-common.h>
 #include <librepgp/stream-armor.h>
 #include <librepgp/stream-parse.h>
 #include <librepgp/stream-write.h>
 #include <librepgp/stream-sig.h>
 #include <librepgp/stream-packet.h>
 #include <librepgp/stream-key.h>
@@ -127,21 +127,20 @@ find_key(rnp_ffi_t               ffi,
 static pgp_key_t *
 ffi_key_provider(const pgp_key_request_ctx_t *ctx, void *userdata)
 {
     rnp_ffi_t ffi = (rnp_ffi_t) userdata;
     return find_key(ffi, &ctx->search, ctx->secret ? KEY_TYPE_SECRET : KEY_TYPE_PUBLIC, true);
 }
 
 static void
-rnp_ctx_init_ffi(rnp_ctx_t *ctx, rnp_ffi_t ffi)
+rnp_ctx_init_ffi(rnp_ctx_t &ctx, rnp_ffi_t ffi)
 {
-    memset(ctx, 0, sizeof(*ctx));
-    ctx->rng = &ffi->rng;
-    ctx->ealg = DEFAULT_PGP_SYMM_ALG;
+    ctx.rng = &ffi->rng;
+    ctx.ealg = DEFAULT_PGP_SYMM_ALG;
 }
 
 static const pgp_map_t sig_type_map[] = {{PGP_SIG_BINARY, "binary"},
                                          {PGP_SIG_TEXT, "text"},
                                          {PGP_SIG_STANDALONE, "standalone"},
                                          {PGP_CERT_GENERIC, "certification (generic)"},
                                          {PGP_CERT_PERSONA, "certification (persona)"},
                                          {PGP_CERT_CASUAL, "certification (casual)"},
@@ -439,16 +438,20 @@ ffi_exception(FILE *fp, const char *func
     if (rnp_log_switch()) {
         fprintf(
           fp, "[%s()] Error 0x%08X (%s): %s\n", func, ret, rnp_result_to_string(ret), msg);
     }
     return ret;
 }
 
 #define FFI_GUARD_FP(fp)                                                            \
+    catch (rnp::rnp_exception & e)                                                  \
+    {                                                                               \
+        return ffi_exception((fp), __func__, e.what(), e.code());                   \
+    }                                                                               \
     catch (std::bad_alloc &)                                                        \
     {                                                                               \
         return ffi_exception((fp), __func__, "bad_alloc", RNP_ERROR_OUT_OF_MEMORY); \
     }                                                                               \
     catch (std::exception & e)                                                      \
     {                                                                               \
         return ffi_exception((fp), __func__, e.what());                             \
     }                                                                               \
@@ -1298,16 +1301,66 @@ try {
     if (flags & RNP_KEY_UNLOAD_SECRET) {
         rnp_key_store_clear(ffi->secring);
     }
 
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
+static rnp_result_t
+rnp_input_dearmor_if_needed(rnp_input_t input)
+{
+    if (!input) {
+        return RNP_ERROR_NULL_POINTER;
+    }
+    if (input->src_directory) {
+        return RNP_ERROR_BAD_PARAMETERS;
+    }
+    bool require_armor = false;
+    /* check whether we already have armored stream */
+    if (input->src.type == PGP_STREAM_ARMORED) {
+        if (!src_eof(&input->src)) {
+            return RNP_SUCCESS;
+        }
+        /* eof - probably next we have another armored message */
+        src_close(&input->src);
+        void *app_ctx = input->app_ctx;
+        *input = *(rnp_input_t) app_ctx;
+        free(app_ctx);
+        /* we should not mix armored data with binary */
+        require_armor = true;
+    }
+    if (src_eof(&input->src)) {
+        return RNP_ERROR_EOF;
+    }
+    if (!is_armored_source(&input->src)) {
+        return require_armor ? RNP_ERROR_BAD_FORMAT : RNP_SUCCESS;
+    }
+
+    rnp_input_t app_ctx = (rnp_input_t) calloc(1, sizeof(*input));
+    if (!app_ctx) {
+        return RNP_ERROR_OUT_OF_MEMORY;
+    }
+    *app_ctx = *input;
+
+    pgp_source_t armored;
+    rnp_result_t ret = init_armored_src(&armored, &app_ctx->src);
+    if (ret) {
+        /* original src may be changed during init_armored_src call, so copy it back */
+        input->src = app_ctx->src;
+        free(app_ctx);
+        return ret;
+    }
+
+    input->src = armored;
+    input->app_ctx = app_ctx;
+    return RNP_SUCCESS;
+}
+
 static const char *
 key_status_to_str(pgp_key_import_status_t status)
 {
     if (status == PGP_KEY_IMPORT_STATUS_UNKNOWN) {
         return "none";
     }
     const char *str = "none";
     ARRAY_LOOKUP_BY_ID(key_import_status_map, type, string, status, str);
@@ -1360,16 +1413,21 @@ try {
         FFI_LOG(ffi, "bad flags: need to specify public and/or secret keys");
         return RNP_ERROR_BAD_PARAMETERS;
     }
     bool skipbad = false;
     if (flags & RNP_LOAD_SAVE_PERMISSIVE) {
         skipbad = true;
         flags &= ~RNP_LOAD_SAVE_PERMISSIVE;
     }
+    bool single = false;
+    if (flags & RNP_LOAD_SAVE_SINGLE) {
+        single = true;
+        flags &= ~RNP_LOAD_SAVE_SINGLE;
+    }
     if (flags) {
         FFI_LOG(ffi, "unexpected flags remaining: 0x%X", flags);
         return RNP_ERROR_BAD_PARAMETERS;
     }
 
     rnp_result_t     ret = RNP_ERROR_GENERIC;
     rnp_key_store_t *tmp_store = NULL;
     rnp_result_t     tmpret;
@@ -1379,20 +1437,36 @@ try {
     // load keys to temporary keystore.
     try {
         tmp_store = new rnp_key_store_t(PGP_KEY_STORE_GPG, "");
     } catch (const std::exception &e) {
         FFI_LOG(ffi, "Failed to create key store: %s.", e.what());
         return RNP_ERROR_OUT_OF_MEMORY;
     }
 
-    tmp_store->skip_parsing_errors = skipbad;
-    if (!rnp_key_store_load_from_src(tmp_store, &input->src, NULL)) {
-        ret = RNP_ERROR_BAD_FORMAT;
-        goto done;
+    if (single) {
+        /* we need to init and handle dearmor on this layer since it may be used for the next
+         * keys import */
+        ret = rnp_input_dearmor_if_needed(input);
+        if (ret == RNP_ERROR_EOF) {
+            goto done;
+        }
+        if (ret) {
+            FFI_LOG(ffi, "Failed to init/check dearmor.");
+            goto done;
+        }
+        ret = rnp_key_store_pgp_read_key_from_src(*tmp_store, input->src, skipbad);
+        if (ret) {
+            goto done;
+        }
+    } else {
+        ret = rnp_key_store_pgp_read_from_src(tmp_store, &input->src, skipbad);
+        if (ret) {
+            goto done;
+        }
     }
     jsores = json_object_new_object();
     if (!jsores) {
         ret = RNP_ERROR_OUT_OF_MEMORY;
         goto done;
     }
     jsokeys = json_object_new_array();
     if (!obj_add_field_json(jsores, "keys", jsokeys)) {
@@ -1755,16 +1829,19 @@ try {
             free(*input);
             *input = NULL;
             return RNP_ERROR_OUT_OF_MEMORY;
         }
         memcpy(data, buf, buf_len);
     }
     rnp_result_t ret = init_mem_src(&(*input)->src, data, buf_len, do_copy);
     if (ret) {
+        if (do_copy) {
+            free(data);
+        }
         free(*input);
         *input = NULL;
         return ret;
     }
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
@@ -1819,17 +1896,21 @@ try {
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_input_destroy(rnp_input_t input)
 try {
     if (input) {
+        bool armored = input->src.type == PGP_STREAM_ARMORED;
         src_close(&input->src);
+        if (armored) {
+            rnp_input_destroy((rnp_input_t) input->app_ctx);
+        }
         free(input->src_directory);
         free(input);
     }
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
 rnp_result_t
@@ -2096,168 +2177,136 @@ try {
         free(output->dst_directory);
         free(output);
     }
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
 static rnp_result_t
-rnp_op_add_signature(rnp_ffi_t                ffi,
-                     list *                   signatures,
-                     rnp_key_handle_t         key,
-                     rnp_ctx_t *              ctx,
-                     rnp_op_sign_signature_t *sig)
+rnp_op_add_signature(rnp_ffi_t                 ffi,
+                     rnp_op_sign_signatures_t &signatures,
+                     rnp_key_handle_t          key,
+                     rnp_ctx_t &               ctx,
+                     rnp_op_sign_signature_t * sig)
 {
-    rnp_op_sign_signature_t newsig = NULL;
-
-    if (!signatures || !key) {
-        return RNP_ERROR_NULL_POINTER;
-    }
-
-    newsig = (rnp_op_sign_signature_t) list_append(signatures, NULL, sizeof(*newsig));
-    if (!newsig) {
-        return RNP_ERROR_OUT_OF_MEMORY;
-    }
-    newsig->signer.key = find_suitable_key(
+    if (!key) {
+        return RNP_ERROR_NULL_POINTER;
+    }
+
+    pgp_key_t *signkey = find_suitable_key(
       PGP_OP_SIGN, get_key_prefer_public(key), &key->ffi->key_provider, PGP_KF_SIGN);
-    if (newsig->signer.key && !pgp_key_is_secret(newsig->signer.key)) {
+    if (signkey && !pgp_key_is_secret(signkey)) {
         pgp_key_request_ctx_t ctx = {.op = PGP_OP_SIGN, .secret = true};
         ctx.search.type = PGP_KEY_SEARCH_GRIP;
-        ctx.search.by.grip = pgp_key_get_grip(newsig->signer.key);
-        newsig->signer.key = pgp_request_key(&key->ffi->key_provider, &ctx);
-    }
-    if (!newsig->signer.key) {
-        list_remove((list_item *) newsig);
+        ctx.search.by.grip = pgp_key_get_grip(signkey);
+        signkey = pgp_request_key(&key->ffi->key_provider, &ctx);
+    }
+    if (!signkey) {
         return RNP_ERROR_NO_SUITABLE_KEY;
     }
 
+    try {
+        signatures.emplace_back();
+    } catch (const std::exception &e) {
+        FFI_LOG(ffi, "%s", e.what());
+        return RNP_ERROR_BAD_PARAMETERS;
+    }
+    rnp_op_sign_signature_t newsig = &signatures.back();
+    newsig->signer.key = signkey;
     /* set default create/expire times */
-    newsig->signer.sigcreate = ctx->sigcreate;
-    newsig->signer.sigexpire = ctx->sigexpire;
+    newsig->signer.sigcreate = ctx.sigcreate;
+    newsig->signer.sigexpire = ctx.sigexpire;
     newsig->ffi = ffi;
 
     if (sig) {
         *sig = newsig;
     }
     return RNP_SUCCESS;
 }
 
 static rnp_result_t
-rnp_op_set_armor(rnp_ctx_t *ctx, bool armored)
+rnp_op_set_armor(rnp_ctx_t &ctx, bool armored)
 {
-    if (!ctx) {
-        return RNP_ERROR_NULL_POINTER;
-    }
-    ctx->armor = armored;
+    ctx.armor = armored;
     return RNP_SUCCESS;
 }
 
 static rnp_result_t
-rnp_op_set_compression(rnp_ffi_t ffi, rnp_ctx_t *ctx, const char *compression, int level)
+rnp_op_set_compression(rnp_ffi_t ffi, rnp_ctx_t &ctx, const char *compression, int level)
 {
-    if (!ctx || !compression) {
+    if (!compression) {
         return RNP_ERROR_NULL_POINTER;
     }
 
     pgp_compression_type_t zalg = PGP_C_UNKNOWN;
     if (!str_to_compression_alg(compression, &zalg)) {
         FFI_LOG(ffi, "Invalid compression: %s", compression);
         return RNP_ERROR_BAD_PARAMETERS;
     }
-    ctx->zalg = (int) zalg;
-    ctx->zlevel = level;
+    ctx.zalg = (int) zalg;
+    ctx.zlevel = level;
     return RNP_SUCCESS;
 }
 
 static rnp_result_t
-rnp_op_set_hash(rnp_ffi_t ffi, rnp_ctx_t *ctx, const char *hash)
+rnp_op_set_hash(rnp_ffi_t ffi, rnp_ctx_t &ctx, const char *hash)
 {
-    if (!ctx || !hash) {
-        return RNP_ERROR_NULL_POINTER;
-    }
-
-    if (!str_to_hash_alg(hash, &ctx->halg)) {
+    if (!hash) {
+        return RNP_ERROR_NULL_POINTER;
+    }
+
+    if (!str_to_hash_alg(hash, &ctx.halg)) {
         FFI_LOG(ffi, "Invalid hash: %s", hash);
         return RNP_ERROR_BAD_PARAMETERS;
     }
     return RNP_SUCCESS;
 }
 
 static rnp_result_t
-rnp_op_set_creation_time(rnp_ctx_t *ctx, uint32_t create)
+rnp_op_set_creation_time(rnp_ctx_t &ctx, uint32_t create)
 {
-    if (!ctx) {
-        return RNP_ERROR_NULL_POINTER;
-    }
-    ctx->sigcreate = create;
-    return RNP_SUCCESS;
-}
-
-static rnp_result_t
-rnp_op_set_expiration_time(rnp_ctx_t *ctx, uint32_t expire)
-{
-    if (!ctx) {
-        return RNP_ERROR_NULL_POINTER;
-    }
-    ctx->sigexpire = expire;
+    ctx.sigcreate = create;
     return RNP_SUCCESS;
 }
 
 static rnp_result_t
-rnp_op_set_file_name(rnp_ctx_t *ctx, const char *filename)
+rnp_op_set_expiration_time(rnp_ctx_t &ctx, uint32_t expire)
 {
-    if (!ctx) {
-        return RNP_ERROR_NULL_POINTER;
-    }
-    free(ctx->filename);
-    if (!filename) {
-        ctx->filename = NULL;
-        return RNP_SUCCESS;
-    }
-    ctx->filename = strdup(filename);
-    if (!ctx->filename) {
-        return RNP_ERROR_OUT_OF_MEMORY;
-    }
+    ctx.sigexpire = expire;
     return RNP_SUCCESS;
 }
 
 static rnp_result_t
-rnp_op_set_file_mtime(rnp_ctx_t *ctx, uint32_t mtime)
+rnp_op_set_file_name(rnp_ctx_t &ctx, const char *filename)
 {
-    if (!ctx) {
-        return RNP_ERROR_NULL_POINTER;
-    }
-    ctx->filemtime = mtime;
-    return RNP_SUCCESS;
-}
-
-static void
-rnp_op_signatures_destroy(list *signatures)
+    ctx.filename = filename ? filename : "";
+    return RNP_SUCCESS;
+}
+
+static rnp_result_t
+rnp_op_set_file_mtime(rnp_ctx_t &ctx, uint32_t mtime)
 {
-    list_destroy(signatures);
+    ctx.filemtime = mtime;
+    return RNP_SUCCESS;
 }
 
 rnp_result_t
 rnp_op_encrypt_create(rnp_op_encrypt_t *op,
                       rnp_ffi_t         ffi,
                       rnp_input_t       input,
                       rnp_output_t      output)
 try {
     // checks
     if (!op || !ffi || !input || !output) {
         return RNP_ERROR_NULL_POINTER;
     }
 
-    *op = (rnp_op_encrypt_t) calloc(1, sizeof(**op));
-    if (!*op) {
-        return RNP_ERROR_OUT_OF_MEMORY;
-    }
-
-    rnp_ctx_init_ffi(&(*op)->rnpctx, ffi);
+    *op = new rnp_op_encrypt_st();
+    rnp_ctx_init_ffi((*op)->rnpctx, ffi);
     (*op)->ffi = ffi;
     (*op)->input = input;
     (*op)->output = output;
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
 rnp_result_t
@@ -2267,62 +2316,60 @@ try {
     if (!op || !handle) {
         return RNP_ERROR_NULL_POINTER;
     }
 
     pgp_key_t *key = find_suitable_key(PGP_OP_ENCRYPT,
                                        get_key_prefer_public(handle),
                                        &handle->ffi->key_provider,
                                        PGP_KF_ENCRYPT);
-    if (!list_append(&op->rnpctx.recipients, &key, sizeof(key))) {
-        return RNP_ERROR_OUT_OF_MEMORY;
-    }
+    op->rnpctx.recipients.push_back(key);
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_encrypt_add_signature(rnp_op_encrypt_t         op,
                              rnp_key_handle_t         key,
                              rnp_op_sign_signature_t *sig)
 try {
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
-    return rnp_op_add_signature(op->ffi, &op->signatures, key, &op->rnpctx, sig);
+    return rnp_op_add_signature(op->ffi, op->signatures, key, op->rnpctx, sig);
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_encrypt_set_hash(rnp_op_encrypt_t op, const char *hash)
 try {
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
-    return rnp_op_set_hash(op->ffi, &op->rnpctx, hash);
+    return rnp_op_set_hash(op->ffi, op->rnpctx, hash);
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_encrypt_set_creation_time(rnp_op_encrypt_t op, uint32_t create)
 try {
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
-    return rnp_op_set_creation_time(&op->rnpctx, create);
+    return rnp_op_set_creation_time(op->rnpctx, create);
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_encrypt_set_expiration_time(rnp_op_encrypt_t op, uint32_t expire)
 try {
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
-    return rnp_op_set_expiration_time(&op->rnpctx, expire);
+    return rnp_op_set_expiration_time(op->rnpctx, expire);
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_encrypt_add_password(rnp_op_encrypt_t op,
                             const char *     password,
                             const char *     s2k_hash,
                             size_t           iterations,
@@ -2362,32 +2409,32 @@ try {
             pgp_password_ctx_t pswdctx = {.op = PGP_OP_ENCRYPT_SYM, .key = NULL};
             if (!pgp_request_password(
                   &op->ffi->pass_provider, &pswdctx, &ask_pass[0], ask_pass.size())) {
                 return RNP_ERROR_BAD_PASSWORD;
             }
             password = ask_pass.data();
         }
         return rnp_ctx_add_encryption_password(
-          &op->rnpctx, password, hash_alg, symm_alg, iterations);
+          op->rnpctx, password, hash_alg, symm_alg, iterations);
     } catch (const std::exception &e) {
         FFI_LOG(op->ffi, "%s", e.what());
         return RNP_ERROR_OUT_OF_MEMORY;
     }
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_encrypt_set_armor(rnp_op_encrypt_t op, bool armored)
 try {
     // checks
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
-    return rnp_op_set_armor(&op->rnpctx, armored);
+    return rnp_op_set_armor(op->rnpctx, armored);
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_encrypt_set_cipher(rnp_op_encrypt_t op, const char *cipher)
 try {
     // checks
     if (!op) {
@@ -2432,37 +2479,37 @@ FFI_GUARD
 
 rnp_result_t
 rnp_op_encrypt_set_compression(rnp_op_encrypt_t op, const char *compression, int level)
 try {
     // checks
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
-    return rnp_op_set_compression(op->ffi, &op->rnpctx, compression, level);
+    return rnp_op_set_compression(op->ffi, op->rnpctx, compression, level);
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_encrypt_set_file_name(rnp_op_encrypt_t op, const char *filename)
 try {
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
-    return rnp_op_set_file_name(&op->rnpctx, filename);
+    return rnp_op_set_file_name(op->rnpctx, filename);
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_encrypt_set_file_mtime(rnp_op_encrypt_t op, uint32_t mtime)
 try {
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
-    return rnp_op_set_file_mtime(&op->rnpctx, mtime);
+    return rnp_op_set_file_mtime(op->rnpctx, mtime);
 }
 FFI_GUARD
 
 static pgp_write_handler_t
 pgp_write_handler(pgp_password_provider_t *pass_provider,
                   rnp_ctx_t *              rnpctx,
                   void *                   param,
                   pgp_key_provider_t *     key_provider)
@@ -2472,42 +2519,35 @@ pgp_write_handler(pgp_password_provider_
     handler.password_provider = pass_provider;
     handler.ctx = rnpctx;
     handler.param = param;
     handler.key_provider = key_provider;
     return handler;
 }
 
 static rnp_result_t
-rnp_op_add_signatures(list opsigs, rnp_ctx_t *ctx)
+rnp_op_add_signatures(rnp_op_sign_signatures_t &opsigs, rnp_ctx_t &ctx)
 {
-    for (list_item *sig = list_front(opsigs); sig; sig = list_next(sig)) {
-        rnp_signer_info_t       sinfo = {};
-        rnp_op_sign_signature_t osig = (rnp_op_sign_signature_t) sig;
-
-        if (!osig->signer.key) {
+    for (auto &sig : opsigs) {
+        if (!sig.signer.key) {
             return RNP_ERROR_NO_SUITABLE_KEY;
         }
 
-        sinfo = osig->signer;
-        if (!osig->hash_set) {
-            sinfo.halg = ctx->halg;
-        }
-        if (!osig->expiry_set) {
-            sinfo.sigexpire = ctx->sigexpire;
-        }
-        if (!osig->create_set) {
-            sinfo.sigcreate = ctx->sigcreate;
-        }
-
-        if (!list_append(&ctx->signers, &sinfo, sizeof(sinfo))) {
-            return RNP_ERROR_OUT_OF_MEMORY;
-        }
-    }
-
+        rnp_signer_info_t sinfo = sig.signer;
+        if (!sig.hash_set) {
+            sinfo.halg = ctx.halg;
+        }
+        if (!sig.expiry_set) {
+            sinfo.sigexpire = ctx.sigexpire;
+        }
+        if (!sig.create_set) {
+            sinfo.sigcreate = ctx.sigcreate;
+        }
+        ctx.signers.push_back(sinfo);
+    }
     return RNP_SUCCESS;
 }
 
 rnp_result_t
 rnp_op_encrypt_execute(rnp_op_encrypt_t op)
 try {
     // checks
     if (!op || !op->input || !op->output) {
@@ -2517,18 +2557,18 @@ try {
     // set the default hash alg if none was specified
     if (!op->rnpctx.halg) {
         op->rnpctx.halg = DEFAULT_PGP_HASH_ALG;
     }
     pgp_write_handler_t handler =
       pgp_write_handler(&op->ffi->pass_provider, &op->rnpctx, NULL, &op->ffi->key_provider);
 
     rnp_result_t ret;
-    if (list_length(op->signatures)) {
-        if ((ret = rnp_op_add_signatures(op->signatures, &op->rnpctx))) {
+    if (!op->signatures.empty()) {
+        if ((ret = rnp_op_add_signatures(op->signatures, op->rnpctx))) {
             return ret;
         }
         ret = rnp_encrypt_sign_src(&handler, &op->input->src, &op->output->dst);
     } else {
         ret = rnp_encrypt_src(&handler, &op->input->src, &op->output->dst);
     }
 
     dst_flush(&op->output->dst);
@@ -2537,39 +2577,31 @@ try {
     op->output = NULL;
     return ret;
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_encrypt_destroy(rnp_op_encrypt_t op)
 try {
-    if (op) {
-        rnp_ctx_free(&op->rnpctx);
-        rnp_op_signatures_destroy(&op->signatures);
-        free(op);
-    }
+    delete op;
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_sign_create(rnp_op_sign_t *op, rnp_ffi_t ffi, rnp_input_t input, rnp_output_t output)
 try {
     // checks
     if (!op || !ffi || !input || !output) {
         return RNP_ERROR_NULL_POINTER;
     }
 
-    *op = (rnp_op_sign_t) calloc(1, sizeof(**op));
-    if (!*op) {
-        return RNP_ERROR_OUT_OF_MEMORY;
-    }
-
-    rnp_ctx_init_ffi(&(*op)->rnpctx, ffi);
+    *op = new rnp_op_sign_st();
+    rnp_ctx_init_ffi((*op)->rnpctx, ffi);
     (*op)->ffi = ffi;
     (*op)->input = input;
     (*op)->output = output;
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
 rnp_result_t
@@ -2601,17 +2633,17 @@ try {
 FFI_GUARD
 
 rnp_result_t
 rnp_op_sign_add_signature(rnp_op_sign_t op, rnp_key_handle_t key, rnp_op_sign_signature_t *sig)
 try {
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
-    return rnp_op_add_signature(op->ffi, &op->signatures, key, &op->rnpctx, sig);
+    return rnp_op_add_signature(op->ffi, op->signatures, key, op->rnpctx, sig);
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_sign_signature_set_hash(rnp_op_sign_signature_t sig, const char *hash)
 try {
     if (!sig) {
         return RNP_ERROR_NULL_POINTER;
@@ -2650,77 +2682,77 @@ try {
 FFI_GUARD
 
 rnp_result_t
 rnp_op_sign_set_armor(rnp_op_sign_t op, bool armored)
 try {
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
-    return rnp_op_set_armor(&op->rnpctx, armored);
+    return rnp_op_set_armor(op->rnpctx, armored);
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_sign_set_compression(rnp_op_sign_t op, const char *compression, int level)
 try {
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
-    return rnp_op_set_compression(op->ffi, &op->rnpctx, compression, level);
+    return rnp_op_set_compression(op->ffi, op->rnpctx, compression, level);
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_sign_set_hash(rnp_op_sign_t op, const char *hash)
 try {
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
-    return rnp_op_set_hash(op->ffi, &op->rnpctx, hash);
+    return rnp_op_set_hash(op->ffi, op->rnpctx, hash);
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_sign_set_creation_time(rnp_op_sign_t op, uint32_t create)
 try {
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
-    return rnp_op_set_creation_time(&op->rnpctx, create);
+    return rnp_op_set_creation_time(op->rnpctx, create);
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_sign_set_expiration_time(rnp_op_sign_t op, uint32_t expire)
 try {
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
-    return rnp_op_set_expiration_time(&op->rnpctx, expire);
+    return rnp_op_set_expiration_time(op->rnpctx, expire);
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_sign_set_file_name(rnp_op_sign_t op, const char *filename)
 try {
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
-    return rnp_op_set_file_name(&op->rnpctx, filename);
+    return rnp_op_set_file_name(op->rnpctx, filename);
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_sign_set_file_mtime(rnp_op_sign_t op, uint32_t mtime)
 try {
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
-    return rnp_op_set_file_mtime(&op->rnpctx, mtime);
+    return rnp_op_set_file_mtime(op->rnpctx, mtime);
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_sign_execute(rnp_op_sign_t op)
 try {
     // checks
     if (!op || !op->input || !op->output) {
@@ -2730,37 +2762,33 @@ try {
     // set the default hash alg if none was specified
     if (!op->rnpctx.halg) {
         op->rnpctx.halg = DEFAULT_PGP_HASH_ALG;
     }
     pgp_write_handler_t handler =
       pgp_write_handler(&op->ffi->pass_provider, &op->rnpctx, NULL, &op->ffi->key_provider);
 
     rnp_result_t ret;
-    if ((ret = rnp_op_add_signatures(op->signatures, &op->rnpctx))) {
+    if ((ret = rnp_op_add_signatures(op->signatures, op->rnpctx))) {
         return ret;
     }
     ret = rnp_sign_src(&handler, &op->input->src, &op->output->dst);
 
     dst_flush(&op->output->dst);
     op->output->keep = ret == RNP_SUCCESS;
     op->input = NULL;
     op->output = NULL;
     return ret;
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_sign_destroy(rnp_op_sign_t op)
 try {
-    if (op) {
-        rnp_ctx_free(&op->rnpctx);
-        rnp_op_signatures_destroy(&op->signatures);
-        free(op);
-    }
+    delete op;
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
 static void
 rnp_op_verify_on_signatures(const std::vector<pgp_signature_info_t> &sigs, void *param)
 {
     rnp_op_verify_t op = (rnp_op_verify_t) param;
@@ -2945,22 +2973,18 @@ rnp_op_verify_create(rnp_op_verify_t *op
                      rnp_ffi_t        ffi,
                      rnp_input_t      input,
                      rnp_output_t     output)
 try {
     if (!op || !ffi || !input || !output) {
         return RNP_ERROR_NULL_POINTER;
     }
 
-    *op = (rnp_op_verify_t) calloc(1, sizeof(**op));
-    if (!*op) {
-        return RNP_ERROR_OUT_OF_MEMORY;
-    }
-
-    rnp_ctx_init_ffi(&(*op)->rnpctx, ffi);
+    *op = new rnp_op_verify_st();
+    rnp_ctx_init_ffi((*op)->rnpctx, ffi);
     (*op)->ffi = ffi;
     (*op)->input = input;
     (*op)->output = output;
 
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
@@ -2969,22 +2993,18 @@ rnp_op_verify_detached_create(rnp_op_ver
                               rnp_ffi_t        ffi,
                               rnp_input_t      input,
                               rnp_input_t      signature)
 try {
     if (!op || !ffi || !input || !signature) {
         return RNP_ERROR_NULL_POINTER;
     }
 
-    *op = (rnp_op_verify_t) calloc(1, sizeof(**op));
-    if (!*op) {
-        return RNP_ERROR_OUT_OF_MEMORY;
-    }
-
-    rnp_ctx_init_ffi(&(*op)->rnpctx, ffi);
+    *op = new rnp_op_verify_st();
+    rnp_ctx_init_ffi((*op)->rnpctx, ffi);
     (*op)->rnpctx.detached = true;
     (*op)->ffi = ffi;
     (*op)->input = signature;
     (*op)->detached_input = input;
 
     return RNP_SUCCESS;
 }
 FFI_GUARD
@@ -3267,29 +3287,30 @@ try {
     *iterations = symenc->iterations;
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_verify_destroy(rnp_op_verify_t op)
 try {
-    if (op) {
-        rnp_ctx_free(&op->rnpctx);
-        delete[] op->signatures;
-        free(op->filename);
-        free(op->recipients);
-        free(op->used_recipient);
-        free(op->symencs);
-        free(op->used_symenc);
-        free(op);
-    }
-    return RNP_SUCCESS;
-}
-FFI_GUARD
+    delete op;
+    return RNP_SUCCESS;
+}
+FFI_GUARD
+
+rnp_op_verify_st::~rnp_op_verify_st()
+{
+    delete[] signatures;
+    free(filename);
+    free(recipients);
+    free(used_recipient);
+    free(symencs);
+    free(used_symenc);
+}
 
 rnp_result_t
 rnp_op_verify_signature_get_status(rnp_op_verify_signature_t sig)
 try {
     if (!sig) {
         return RNP_ERROR_NULL_POINTER;
     }
     return sig->verify_status;
@@ -3338,19 +3359,20 @@ try {
 FFI_GUARD
 
 rnp_result_t
 rnp_op_verify_signature_get_key(rnp_op_verify_signature_t sig, rnp_key_handle_t *key)
 try {
     rnp_ffi_t        ffi = sig->ffi;
     pgp_key_search_t search = {};
 
-    if (!signature_get_keyid(&sig->sig_pkt, search.by.keyid)) {
-        return RNP_ERROR_BAD_PARAMETERS;
-    }
+    if (!sig->sig_pkt.has_keyid()) {
+        return RNP_ERROR_BAD_PARAMETERS;
+    }
+    search.by.keyid = sig->sig_pkt.keyid();
     // create a search (since we'll use this later anyways)
     search.type = PGP_KEY_SEARCH_KEYID;
 
     // search the stores
     pgp_key_t *pub = rnp_key_store_search(ffi->pubring, &search, NULL);
     pgp_key_t *sec = rnp_key_store_search(ffi->secring, &search, NULL);
     if (!pub && !sec) {
         return RNP_ERROR_KEY_NOT_FOUND;
@@ -3370,20 +3392,20 @@ try {
 FFI_GUARD
 
 rnp_result_t
 rnp_op_verify_signature_get_times(rnp_op_verify_signature_t sig,
                                   uint32_t *                create,
                                   uint32_t *                expires)
 try {
     if (create) {
-        *create = signature_get_creation(&sig->sig_pkt);
+        *create = sig->sig_pkt.creation();
     }
     if (expires) {
-        *expires = signature_get_expiration(&sig->sig_pkt);
+        *expires = sig->sig_pkt.expiration();
     }
 
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
 static bool
 rnp_decrypt_dest_provider(pgp_parse_handler_t *handler,
@@ -3394,24 +3416,23 @@ rnp_decrypt_dest_provider(pgp_parse_hand
     *dst = &((rnp_output_t) handler->param)->dst;
     *closedst = false;
     return true;
 }
 
 rnp_result_t
 rnp_decrypt(rnp_ffi_t ffi, rnp_input_t input, rnp_output_t output)
 try {
-    rnp_ctx_t rnpctx;
-
     // checks
     if (!ffi || !input || !output) {
         return RNP_ERROR_NULL_POINTER;
     }
 
-    rnp_ctx_init_ffi(&rnpctx, ffi);
+    rnp_ctx_t rnpctx;
+    rnp_ctx_init_ffi(rnpctx, ffi);
     pgp_parse_handler_t handler;
     memset(&handler, 0, sizeof(handler));
     handler.password_provider = &ffi->pass_provider;
     handler.key_provider = &ffi->key_provider;
     handler.dest_provider = rnp_decrypt_dest_provider;
     handler.param = output;
     handler.ctx = &rnpctx;
 
@@ -3968,17 +3989,17 @@ pk_alg_allows_custom_curve(pgp_pubkey_al
     case PGP_PKA_SM2:
         return true;
     default:
         return false;
     }
 }
 
 static bool
-parse_preferences(json_object *jso, pgp_user_prefs_t *prefs)
+parse_preferences(json_object *jso, pgp_user_prefs_t &prefs)
 {
     static const struct {
         const char *   key;
         enum json_type type;
     } properties[] = {{"hashes", json_type_array},
                       {"ciphers", json_type_array},
                       {"compression", json_type_array},
                       {"key server", json_type_string}};
@@ -3989,66 +4010,62 @@ parse_preferences(json_object *jso, pgp_
 
         if (!json_object_object_get_ex(jso, key, &value)) {
             continue;
         }
 
         if (!json_object_is_type(value, properties[iprop].type)) {
             return false;
         }
-        if (!rnp_strcasecmp(key, "hashes")) {
-            int length = json_object_array_length(value);
-            for (int i = 0; i < length; i++) {
-                json_object *item = json_object_array_get_idx(value, i);
-                if (!json_object_is_type(item, json_type_string)) {
-                    return false;
-                }
-                pgp_hash_alg_t hash_alg = PGP_HASH_UNKNOWN;
-                if (!str_to_hash_alg(json_object_get_string(item), &hash_alg)) {
-                    return false;
-                }
-                if (!pgp_user_prefs_add_hash_alg(prefs, hash_alg)) {
-                    return false;
-                }
-            }
-        } else if (!rnp_strcasecmp(key, "ciphers")) {
-            int length = json_object_array_length(value);
-            for (int i = 0; i < length; i++) {
-                json_object *item = json_object_array_get_idx(value, i);
-                if (!json_object_is_type(item, json_type_string)) {
-                    return false;
+        try {
+            if (!rnp_strcasecmp(key, "hashes")) {
+                int length = json_object_array_length(value);
+                for (int i = 0; i < length; i++) {
+                    json_object *item = json_object_array_get_idx(value, i);
+                    if (!json_object_is_type(item, json_type_string)) {
+                        return false;
+                    }
+                    pgp_hash_alg_t hash_alg = PGP_HASH_UNKNOWN;
+                    if (!str_to_hash_alg(json_object_get_string(item), &hash_alg)) {
+                        return false;
+                    }
+                    prefs.add_hash_alg(hash_alg);
                 }
-                pgp_symm_alg_t symm_alg = PGP_SA_UNKNOWN;
-                if (!str_to_cipher(json_object_get_string(item), &symm_alg)) {
-                    return false;
+            } else if (!rnp_strcasecmp(key, "ciphers")) {
+                int length = json_object_array_length(value);
+                for (int i = 0; i < length; i++) {
+                    json_object *item = json_object_array_get_idx(value, i);
+                    if (!json_object_is_type(item, json_type_string)) {
+                        return false;
+                    }
+                    pgp_symm_alg_t symm_alg = PGP_SA_UNKNOWN;
+                    if (!str_to_cipher(json_object_get_string(item), &symm_alg)) {
+                        return false;
+                    }
+                    prefs.add_symm_alg(symm_alg);
                 }
-                if (!pgp_user_prefs_add_symm_alg(prefs, symm_alg)) {
-                    return false;
+            } else if (!rnp_strcasecmp(key, "compression")) {
+                int length = json_object_array_length(value);
+                for (int i = 0; i < length; i++) {
+                    json_object *item = json_object_array_get_idx(value, i);
+                    if (!json_object_is_type(item, json_type_string)) {
+                        return false;
+                    }
+                    pgp_compression_type_t z_alg = PGP_C_UNKNOWN;
+                    if (!str_to_compression_alg(json_object_get_string(item), &z_alg)) {
+                        return false;
+                    }
+                    prefs.add_z_alg(z_alg);
                 }
+            } else if (!rnp_strcasecmp(key, "key server")) {
+                prefs.key_server = json_object_get_string(value);
             }
-        } else if (!rnp_strcasecmp(key, "compression")) {
-            int length = json_object_array_length(value);
-            for (int i = 0; i < length; i++) {
-                json_object *item = json_object_array_get_idx(value, i);
-                if (!json_object_is_type(item, json_type_string)) {
-                    return false;
-                }
-                pgp_compression_type_t z_alg = PGP_C_UNKNOWN;
-                if (!str_to_compression_alg(json_object_get_string(item), &z_alg)) {
-                    return false;
-                }
-                if (!pgp_user_prefs_add_z_alg(prefs, z_alg)) {
-                    return false;
-                }
-            }
-        } else if (!rnp_strcasecmp(key, "key server")) {
-            prefs->key_server = (uint8_t *) strdup(json_object_get_string(value));
-            if (!prefs->key_server) {
-                return false;
-            }
+        } catch (const std::exception &e) {
+            RNP_LOG("%s", e.what());
+            return false;
         }
         // delete this field since it has been handled
         json_object_object_del(jso, key);
     }
     return true;
 }
 
 static bool
@@ -4220,17 +4237,17 @@ parse_keygen_primary(json_object *jso, r
             if (!json_object_is_type(value, json_type_int)) {
                 return false;
             }
             cert->key_expiration = json_object_get_int(value);
         } else if (!rnp_strcasecmp(key, "preferences")) {
             if (!json_object_is_type(value, json_type_object)) {
                 return false;
             }
-            if (!parse_preferences(value, &cert->prefs)) {
+            if (!parse_preferences(value, cert->prefs)) {
                 return false;
             }
             if (json_object_object_length(value) != 0) {
                 return false;
             }
         } else if (!rnp_strcasecmp(key, "protection")) {
             if (!json_object_is_type(value, json_type_object)) {
                 return false;
@@ -4611,17 +4628,16 @@ try {
         goto done;
     }
 
     ret = RNP_SUCCESS;
 done:
     json_object_put(jso);
     free(identifier_type);
     free(identifier);
-    pgp_free_user_prefs(&keygen_desc.primary.keygen.cert.prefs);
     return ret;
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_generate_key_ex(rnp_ffi_t         ffi,
                     const char *      key_alg,
                     const char *      sub_alg,
@@ -4832,21 +4848,17 @@ try {
     if (!str_to_pubkey_alg(alg, &key_alg)) {
         return RNP_ERROR_BAD_PARAMETERS;
     }
 
     if (!(pgp_pk_alg_capabilities(key_alg) & PGP_KF_SIGN)) {
         return RNP_ERROR_BAD_PARAMETERS;
     }
 
-    *op = (rnp_op_generate_t) calloc(1, sizeof(**op));
-    if (!*op) {
-        return RNP_ERROR_OUT_OF_MEMORY;
-    }
-
+    *op = new rnp_op_generate_st();
     (*op)->ffi = ffi;
     (*op)->primary = true;
     (*op)->crypto.key_alg = key_alg;
     (*op)->crypto.rng = &ffi->rng;
     (*op)->cert.key_flags = default_key_flags(key_alg, false);
 
     return RNP_SUCCESS;
 }
@@ -4880,21 +4892,17 @@ try {
         return RNP_ERROR_BAD_PARAMETERS;
     }
 
     pgp_pubkey_alg_t key_alg = PGP_PKA_NOTHING;
     if (!str_to_pubkey_alg(alg, &key_alg)) {
         return RNP_ERROR_BAD_PARAMETERS;
     }
 
-    *op = (rnp_op_generate_t) calloc(1, sizeof(**op));
-    if (!*op) {
-        return RNP_ERROR_OUT_OF_MEMORY;
-    }
-
+    *op = new rnp_op_generate_st();
     (*op)->ffi = ffi;
     (*op)->primary = false;
     (*op)->crypto.key_alg = key_alg;
     (*op)->crypto.rng = &ffi->rng;
     (*op)->binding.key_flags = default_key_flags(key_alg, true);
     (*op)->primary_sec = primary->sec;
     (*op)->primary_pub = primary->pub;
 
@@ -5123,19 +5131,17 @@ rnp_result_t
 rnp_op_generate_clear_pref_hashes(rnp_op_generate_t op)
 try {
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
     if (!op->primary) {
         return RNP_ERROR_BAD_PARAMETERS;
     }
-    if (!pgp_user_prefs_set_hash_algs(&op->cert.prefs, NULL, 0)) {
-        return RNP_ERROR_BAD_STATE;
-    }
+    op->cert.prefs.set_hash_algs({});
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_generate_add_pref_hash(rnp_op_generate_t op, const char *hash)
 try {
     if (!op || !hash) {
@@ -5143,35 +5149,31 @@ try {
     }
     if (!op->primary) {
         return RNP_ERROR_BAD_PARAMETERS;
     }
     pgp_hash_alg_t hash_alg = PGP_HASH_UNKNOWN;
     if (!str_to_hash_alg(hash, &hash_alg)) {
         return RNP_ERROR_BAD_PARAMETERS;
     }
-    if (!pgp_user_prefs_add_hash_alg(&op->cert.prefs, hash_alg)) {
-        return RNP_ERROR_BAD_STATE;
-    }
+    op->cert.prefs.add_hash_alg(hash_alg);
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_generate_clear_pref_compression(rnp_op_generate_t op)
 try {
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
     if (!op->primary) {
         return RNP_ERROR_BAD_PARAMETERS;
     }
-    if (!pgp_user_prefs_set_z_algs(&op->cert.prefs, NULL, 0)) {
-        return RNP_ERROR_BAD_STATE;
-    }
+    op->cert.prefs.set_z_algs({});
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_generate_add_pref_compression(rnp_op_generate_t op, const char *compression)
 try {
     if (!op || !compression) {
@@ -5179,35 +5181,31 @@ try {
     }
     if (!op->primary) {
         return RNP_ERROR_BAD_PARAMETERS;
     }
     pgp_compression_type_t z_alg = PGP_C_UNKNOWN;
     if (!str_to_compression_alg(compression, &z_alg)) {
         return RNP_ERROR_BAD_PARAMETERS;
     }
-    if (!pgp_user_prefs_add_z_alg(&op->cert.prefs, z_alg)) {
-        return RNP_ERROR_BAD_STATE;
-    }
+    op->cert.prefs.add_z_alg(z_alg);
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_generate_clear_pref_ciphers(rnp_op_generate_t op)
 try {
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
     if (!op->primary) {
         return RNP_ERROR_BAD_PARAMETERS;
     }
-    if (!pgp_user_prefs_set_symm_algs(&op->cert.prefs, NULL, 0)) {
-        return RNP_ERROR_BAD_STATE;
-    }
+    op->cert.prefs.set_symm_algs({});
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_generate_add_pref_cipher(rnp_op_generate_t op, const char *cipher)
 try {
     if (!op || !cipher) {
@@ -5215,41 +5213,31 @@ try {
     }
     if (!op->primary) {
         return RNP_ERROR_BAD_PARAMETERS;
     }
     pgp_symm_alg_t symm_alg = PGP_SA_UNKNOWN;
     if (!str_to_cipher(cipher, &symm_alg)) {
         return RNP_ERROR_BAD_PARAMETERS;
     }
-    if (!pgp_user_prefs_add_symm_alg(&op->cert.prefs, symm_alg)) {
-        return RNP_ERROR_BAD_STATE;
-    }
+    op->cert.prefs.add_symm_alg(symm_alg);
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_generate_set_pref_keyserver(rnp_op_generate_t op, const char *keyserver)
 try {
-    uint8_t *_keyserver = NULL;
     if (!op) {
         return RNP_ERROR_NULL_POINTER;
     }
     if (!op->primary) {
         return RNP_ERROR_BAD_PARAMETERS;
     }
-    if (keyserver) {
-        _keyserver = (uint8_t *) strdup(keyserver);
-        if (!_keyserver) {
-            return RNP_ERROR_OUT_OF_MEMORY;
-        }
-    }
-    free(op->cert.prefs.key_server);
-    op->cert.prefs.key_server = _keyserver;
+    op->cert.prefs.key_server = keyserver ? keyserver : "";
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_generate_execute(rnp_op_generate_t op)
 try {
     if (!op || !op->ffi) {
@@ -5349,28 +5337,29 @@ try {
     (*handle)->sec = op->gen_sec;
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_op_generate_destroy(rnp_op_generate_t op)
 try {
-    if (op) {
-        pgp_free_user_prefs(&op->cert.prefs);
-        if (op->password) {
-            pgp_forget(op->password, strlen(op->password) + 1);
-            free(op->password);
-            op->password = NULL;
-        }
-        free(op);
-    }
-    return RNP_SUCCESS;
-}
-FFI_GUARD
+    delete op;
+    return RNP_SUCCESS;
+}
+FFI_GUARD
+
+rnp_op_generate_st::~rnp_op_generate_st()
+{
+    if (password) {
+        pgp_forget(password, strlen(password) + 1);
+        free(password);
+        password = NULL;
+    }
+}
 
 rnp_result_t
 rnp_key_handle_destroy(rnp_key_handle_t key)
 try {
     // This does not free key->key which is owned by the keyring
     free(key);
     return RNP_SUCCESS;
 }
@@ -5463,17 +5452,17 @@ rnp_result_t
 rnp_key_add_uid(rnp_key_handle_t handle,
                 const char *     uid,
                 const char *     hash,
                 uint32_t         expiration,
                 uint8_t          key_flags,
                 bool             primary)
 try {
     rnp_result_t            ret = RNP_ERROR_GENERIC;
-    rnp_selfsig_cert_info_t info = {{0}};
+    rnp_selfsig_cert_info_t info = {};
     pgp_hash_alg_t          hash_alg = PGP_HASH_UNKNOWN;
     pgp_key_t *             public_key = NULL;
     pgp_key_t *             secret_key = NULL;
     pgp_key_pkt_t *         seckey = NULL;
     pgp_key_pkt_t *         decrypted_seckey = NULL;
 
     if (!handle || !uid || !hash) {
         return RNP_ERROR_NULL_POINTER;
@@ -5713,35 +5702,35 @@ rnp_result_t
 rnp_signature_get_creation(rnp_signature_handle_t handle, uint32_t *create)
 try {
     if (!handle || !create) {
         return RNP_ERROR_NULL_POINTER;
     }
     if (!handle->sig) {
         return RNP_ERROR_BAD_PARAMETERS;
     }
-    *create = signature_get_creation(&handle->sig->sig);
+    *create = handle->sig->sig.creation();
     return RNP_SUCCESS;
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_signature_get_keyid(rnp_signature_handle_t handle, char **result)
 try {
     if (!handle || !result) {
         return RNP_ERROR_NULL_POINTER;
     }
     if (!handle->sig) {
         return RNP_ERROR_BAD_PARAMETERS;
     }
-    pgp_key_id_t keyid = {};
-    if (!signature_get_keyid(&handle->sig->sig, keyid)) {
+    if (!handle->sig->sig.has_keyid()) {
         *result = NULL;
         return RNP_SUCCESS;
     }
+    pgp_key_id_t keyid = handle->sig->sig.keyid();
     return hex_encode_value(keyid.data(), keyid.size(), result, RNP_HEX_UPPERCASE);
 }
 FFI_GUARD
 
 rnp_result_t
 rnp_signature_get_signer(rnp_signature_handle_t sig, rnp_key_handle_t *key)
 try {
     char *       keyid = NULL;
@@ -6198,17 +6187,17 @@ try {
     if (!key || !type) {
         return RNP_ERROR_NULL_POINTER;
     }
     if (!key->sec) {
         return RNP_ERROR_BAD_PARAMETERS;
     }
 
     pgp_s2k_t & s2k = key->sec->pkt.sec_protection.s2k;
-    const char *res = NULL;
+    const char *res = "Unknown";
     if (s2k.usage == PGP_S2KU_NONE) {
         res = "None";
     }
     if ((s2k.usage == PGP_S2KU_ENCRYPTED) && (s2k.specifier != PGP_S2KS_EXPERIMENTAL)) {
         res = "Encrypted";
     }
     if ((s2k.usage == PGP_S2KU_ENCRYPTED_AND_HASHED) &&
         (s2k.specifier != PGP_S2KS_EXPERIMENTAL)) {
@@ -6540,16 +6529,19 @@ key_to_bytes(pgp_key_t *key, uint8_t **b
     if (!pgp_key_write_packets(key, &memdst)) {
         dst_close(&memdst, true);
         return RNP_ERROR_OUT_OF_MEMORY;
     }
 
     *buf_len = memdst.writeb;
     *buf = (uint8_t *) mem_dest_own_memory(&memdst);
     dst_close(&memdst, true);
+    if (*buf_len && !*buf) {
+        return RNP_ERROR_OUT_OF_MEMORY;
+    }
     return RNP_SUCCESS;
 }
 
 rnp_result_t
 rnp_get_public_key_data(rnp_key_handle_t handle, uint8_t **buf, size_t *buf_len)
 try {
     // checks
     if (!handle || !buf || !buf_len) {
@@ -6761,86 +6753,82 @@ add_json_sig_mpis(json_object *jso, cons
     default:
         // TODO: we could use info->unknown and add a hex string of raw data here
         return RNP_ERROR_NOT_SUPPORTED;
     }
     return RNP_SUCCESS;
 }
 
 static bool
-add_json_user_prefs(json_object *jso, const pgp_user_prefs_t *prefs)
+add_json_user_prefs(json_object *jso, const pgp_user_prefs_t &prefs)
 {
     // TODO: instead of using a string "Unknown" as a fallback for these,
     // we could add a string of hex/dec (or even an int)
-    if (prefs->symm_alg_count) {
+    if (!prefs.symm_algs.empty()) {
         json_object *jsoarr = json_object_new_array();
         if (!jsoarr) {
             return false;
         }
         json_object_object_add(jso, "ciphers", jsoarr);
-        for (unsigned i = 0; i < prefs->symm_alg_count; i++) {
-            const char *   name = "Unknown";
-            pgp_symm_alg_t alg = (pgp_symm_alg_t) prefs->symm_algs[i];
+        for (auto alg : prefs.symm_algs) {
+            const char *name = "Unknown";
             ARRAY_LOOKUP_BY_ID(symm_alg_map, type, string, alg, name);
             json_object *jsoname = json_object_new_string(name);
             if (!jsoname || json_object_array_add(jsoarr, jsoname)) {
                 return false;
             }
         }
     }
-    if (prefs->hash_alg_count) {
+    if (!prefs.hash_algs.empty()) {
         json_object *jsoarr = json_object_new_array();
         if (!jsoarr) {
             return false;
         }
         json_object_object_add(jso, "hashes", jsoarr);
-        for (unsigned i = 0; i < prefs->hash_alg_count; i++) {
-            const char *   name = "Unknown";
-            pgp_hash_alg_t alg = (pgp_hash_alg_t) prefs->hash_algs[i];
+        for (auto alg : prefs.hash_algs) {
+            const char *name = "Unknown";
             ARRAY_LOOKUP_BY_ID(hash_alg_map, type, string, alg, name);
             json_object *jsoname = json_object_new_string(name);
             if (!jsoname || json_object_array_add(jsoarr, jsoname)) {
                 return false;
             }
         }
     }
-    if (prefs->z_alg_count) {
+    if (!prefs.z_algs.empty()) {
         json_object *jsoarr = json_object_new_array();
         if (!jsoarr) {
             return false;
         }
         json_object_object_add(jso, "compression", jsoarr);
-        for (unsigned i = 0; i < prefs->z_alg_count; i++) {
-            const char *           name = "Unknown";
-            pgp_compression_type_t alg = (pgp_compression_type_t) prefs->z_algs[i];
+        for (auto alg : prefs.z_algs) {
+            const char *name = "Unknown";
             ARRAY_LOOKUP_BY_ID(compress_alg_map, type, string, alg, name);
             json_object *jsoname = json_object_new_string(name);
             if (!jsoname || json_object_array_add(jsoarr, jsoname)) {
                 return false;
             }
         }
     }
-    if (prefs->ks_pref_count) {
+    if (!prefs.ks_prefs.empty()) {
         json_object *jsoarr = json_object_new_array();
         if (!jsoarr) {
             return false;
         }
         json_object_object_add(jso, "key server preferences", jsoarr);
-        for (unsigned i = 0; i < prefs->ks_pref_count; i++) {
-            const char *           name = "Unknown";
-            pgp_key_server_prefs_t flag = (pgp_key_server_prefs_t) prefs->ks_prefs[i];
+        for (auto flag : prefs.ks_prefs) {
+            const char *name = "Unknown";
             ARRAY_LOOKUP_BY_ID(key_server_prefs_map, type, string, flag, name);
             json_object *jsoname = json_object_new_string(name);
             if (!jsoname || json_object_array_add(jsoarr, jsoname)) {
                 return false;
             }
         }
     }
-    if (prefs->key_server) {
-        if (!add_json_string_field(jso, "key server", (const char *) prefs->key_server)) {
+    if (!prefs.key_server.empty()) {
+        if (!add_json_string_field(jso, "key server", prefs.key_server.c_str())) {
             return false;
         }
     }
     return true;
 }
 
 static rnp_result_t
 add_json_subsig(json_object *jso, bool is_sub, uint32_t flags, const pgp_subsig_t *subsig)
@@ -6875,19 +6863,19 @@ add_json_subsig(json_object *jso, bool i
     if (!add_json_key_usage(jso, subsig->key_flags)) {
         return RNP_ERROR_OUT_OF_MEMORY;
     }
     // key flags (other)
     if (!add_json_key_flags(jso, subsig->key_flags)) {
         return RNP_ERROR_OUT_OF_MEMORY;
     }
     // preferences
-    const pgp_user_prefs_t *prefs = &subsig->prefs;
-    if (prefs->symm_alg_count || prefs->hash_alg_count || prefs->z_alg_count ||
-        prefs->ks_pref_count || prefs->key_server) {
+    const pgp_user_prefs_t &prefs = subsig->prefs;
+    if (!prefs.symm_algs.empty() || !prefs.hash_algs.empty() || !prefs.z_algs.empty() ||
+        !prefs.ks_prefs.empty() || !prefs.key_server.empty()) {
         json_object *jsoprefs = json_object_new_object();
         if (!jsoprefs) {
             return RNP_ERROR_OUT_OF_MEMORY;
         }
         json_object_object_add(jso, "preferences", jsoprefs);
         if (!add_json_user_prefs(jsoprefs, prefs)) {
             return RNP_ERROR_OUT_OF_MEMORY;
         }
@@ -6896,56 +6884,55 @@ add_json_subsig(json_object *jso, bool i
     // version
     json_object *jsoversion = json_object_new_int(sig->version);
     if (!jsoversion) {
         return RNP_ERROR_OUT_OF_MEMORY;
     }
     json_object_object_add(jso, "version", jsoversion);
     // signature type
     const char *type = "unknown";
-    ARRAY_LOOKUP_BY_ID(sig_type_map, type, string, sig->type, type);
+    ARRAY_LOOKUP_BY_ID(sig_type_map, type, string, sig->type(), type);
     if (!add_json_string_field(jso, "type", type)) {
         return RNP_ERROR_OUT_OF_MEMORY;
     }
     // signer key type
     const char *key_type = "unknown";
     ARRAY_LOOKUP_BY_ID(pubkey_alg_map, type, string, sig->palg, key_type);
     if (!add_json_string_field(jso, "key type", key_type)) {
         return RNP_ERROR_OUT_OF_MEMORY;
     }
     // hash
     const char *hash = "unknown";
     ARRAY_LOOKUP_BY_ID(hash_alg_map, type, string, sig->halg, hash);
     if (!add_json_string_field(jso, "hash", hash)) {
         return RNP_ERROR_OUT_OF_MEMORY;
     }
     // creation time
-    json_object *jsocreation_time = json_object_new_int64(signature_get_creation(sig));
+    json_object *jsocreation_time = json_object_new_int64(sig->creation());
     if (!jsocreation_time) {
         return RNP_ERROR_OUT_OF_MEMORY;
     }
     json_object_object_add(jso, "creation time", jsocreation_time);
     // expiration (seconds)
-    json_object *jsoexpiration = json_object_new_int64(signature_get_expiration(sig));
+    json_object *jsoexpiration = json_object_new_int64(sig->expiration());
     if (!jsoexpiration) {
         return RNP_ERROR_OUT_OF_MEMORY;
     }
     json_object_object_add(jso, "expiration", jsoexpiration);
     // signer
     json_object *jsosigner = NULL;
     // TODO: add signer fingerprint as well (no support internally yet)
-    if (signature_has_keyid(sig)) {
+    if (sig->has_keyid()) {
         jsosigner = json_object_new_object();
         if (!jsosigner) {
             return RNP_ERROR_OUT_OF_MEMORY;
         }
         char         keyid[PGP_KEY_ID_SIZE * 2 + 1];
-        pgp_key_id_t signer = {};
-        if (!signature_get_keyid(sig, signer) ||
-            !rnp_hex_encode(
+        pgp_key_id_t signer = sig->keyid();
+        if (!rnp_hex_encode(
               signer.data(), signer.size(), keyid, sizeof(keyid), RNP_HEX_UPPERCASE)) {
             return RNP_ERROR_GENERIC;
         }
         if (!add_json_string_field(jsosigner, "keyid", keyid)) {
             json_object_put(jsosigner);
             return RNP_ERROR_OUT_OF_MEMORY;
         }
     }
--- a/third_party/rnp/src/lib/types.h
+++ b/third_party/rnp/src/lib/types.h
@@ -107,16 +107,35 @@ template <> struct hash<pgp_fingerprint_
     }
 };
 } // namespace std
 
 typedef std::array<uint8_t, PGP_KEY_GRIP_SIZE> pgp_key_grip_t;
 
 typedef std::array<uint8_t, PGP_KEY_ID_SIZE> pgp_key_id_t;
 
+namespace rnp {
+class rnp_exception : public std::exception {
+    rnp_result_t code_;
+
+  public:
+    rnp_exception(rnp_result_t code = RNP_ERROR_GENERIC) : code_(code){};
+    virtual const char *
+    what() const throw()
+    {
+        return "rnp_exception";
+    };
+    rnp_result_t
+    code()
+    {
+        return code_;
+    };
+};
+} // namespace rnp
+
 /**
  * Type to keep public/secret key mpis without any openpgp-dependent data.
  */
 typedef struct pgp_key_material_t {
     pgp_pubkey_alg_t alg;    /* algorithm of the key */
     bool             secret; /* secret part of the key material is populated */
 
     union {
@@ -147,34 +166,35 @@ typedef struct pgp_encrypted_material_t 
         pgp_rsa_encrypted_t  rsa;
         pgp_eg_encrypted_t   eg;
         pgp_sm2_encrypted_t  sm2;
         pgp_ecdh_encrypted_t ecdh;
     };
 } pgp_encrypted_material_t;
 
 typedef struct pgp_s2k_t {
-    pgp_s2k_usage_t usage;
+    pgp_s2k_usage_t usage{};
 
     /* below fields may not all be valid, depending on the usage field above */
-    pgp_s2k_specifier_t specifier;
-    pgp_hash_alg_t      hash_alg;
+    pgp_s2k_specifier_t specifier{};
+    pgp_hash_alg_t      hash_alg{};
     uint8_t             salt[PGP_SALT_SIZE];
-    unsigned            iterations;
-
+    unsigned            iterations{};
     /* GnuPG custom s2k data */
-    pgp_s2k_gpg_extension_t gpg_ext_num;
-    uint8_t                 gpg_serial_len;
+    pgp_s2k_gpg_extension_t gpg_ext_num{};
+    uint8_t                 gpg_serial_len{};
     uint8_t                 gpg_serial[16];
+    /* Experimental s2k data */
+    std::vector<uint8_t> experimental{};
 } pgp_s2k_t;
 
 typedef struct pgp_key_protection_t {
-    pgp_s2k_t         s2k;         /* string-to-key kdf params */
-    pgp_symm_alg_t    symm_alg;    /* symmetric alg */
-    pgp_cipher_mode_t cipher_mode; /* block cipher mode */
+    pgp_s2k_t         s2k{};         /* string-to-key kdf params */
+    pgp_symm_alg_t    symm_alg{};    /* symmetric alg */
+    pgp_cipher_mode_t cipher_mode{}; /* block cipher mode */
     uint8_t           iv[PGP_MAX_BLOCK_SIZE];
 } pgp_key_protection_t;
 
 /** Struct to hold a key packet. May contain public or private key/subkey */
 typedef struct pgp_key_pkt_t {
     pgp_pkt_type_t   tag;           /* packet tag: public key/subkey or private key/subkey */
     pgp_version_t    version;       /* Key packet version */
     uint32_t         creation_time; /* Key creation time */
@@ -282,21 +302,17 @@ typedef struct pgp_sig_subpkt_t {
             const char *uid;
             unsigned    len;
         } signer; /* 5.2.3.22.  Signer's User ID */
         struct {
             pgp_revocation_type_t code;
             const char *          str;
             unsigned              len;
         } revocation_reason; /* 5.2.3.23.  Reason for Revocation */
-        struct {
-            bool mdc;
-            bool aead;
-            bool key_v5;
-        } features; /* 5.2.3.24.  Features */
+        uint8_t features;    /* 5.2.3.24.  Features */
         struct {
             pgp_pubkey_alg_t pkalg;
             pgp_hash_alg_t   halg;
             uint8_t *        hash;
             unsigned         hlen;
         } sig_target;         /* 5.2.3.25.  Signature Target */
         pgp_signature_t *sig; /* 5.2.3.27. Embedded Signature */
         struct {
@@ -311,46 +327,296 @@ typedef struct pgp_sig_subpkt_t {
           parsed(false), fields({}){};
     pgp_sig_subpkt_t(const pgp_sig_subpkt_t &src);
     pgp_sig_subpkt_t(pgp_sig_subpkt_t &&src);
     pgp_sig_subpkt_t &operator=(pgp_sig_subpkt_t &&src);
     pgp_sig_subpkt_t &operator=(const pgp_sig_subpkt_t &src);
     ~pgp_sig_subpkt_t();
 } pgp_sig_subpkt_t;
 
+typedef struct pgp_one_pass_sig_t pgp_one_pass_sig_t;
+
 typedef struct pgp_signature_t {
+  private:
+    pgp_sig_type_t       type_;
+    std::vector<uint8_t> preferred(pgp_sig_subpacket_type_t type) const;
+    void set_preferred(const std::vector<uint8_t> &data, pgp_sig_subpacket_type_t type);
+
+  public:
     pgp_version_t version;
     /* common v3 and v4 fields */
-    pgp_sig_type_t   type;
     pgp_pubkey_alg_t palg;
     pgp_hash_alg_t   halg;
     uint8_t          lbits[2];
     uint8_t *        hashed_data;
     size_t           hashed_len;
     uint8_t *        material_buf; /* raw signature material */
     size_t           material_len; /* raw signature material length */
 
     /* v3 - only fields */
     uint32_t     creation_time;
     pgp_key_id_t signer;
 
     /* v4 - only fields */
     std::vector<pgp_sig_subpkt_t> subpkts;
 
     pgp_signature_t()
-        : version(PGP_VUNKNOWN), type(PGP_SIG_BINARY), palg(PGP_PKA_NOTHING),
+        : type_(PGP_SIG_BINARY), version(PGP_VUNKNOWN), palg(PGP_PKA_NOTHING),
           halg(PGP_HASH_UNKNOWN), hashed_data(NULL), hashed_len(0), material_buf(NULL),
           material_len(0), creation_time(0){};
     pgp_signature_t(const pgp_signature_t &src);
     pgp_signature_t(pgp_signature_t &&src);
     pgp_signature_t &operator=(pgp_signature_t &&src);
     pgp_signature_t &operator=(const pgp_signature_t &src);
     bool             operator==(const pgp_signature_t &src) const;
     bool             operator!=(const pgp_signature_t &src) const;
     ~pgp_signature_t();
+
+    /* @brief Get signature's type */
+    pgp_sig_type_t
+    type() const
+    {
+        return type_;
+    };
+    void
+    set_type(pgp_sig_type_t atype)
+    {
+        type_ = atype;
+    };
+
+    /**
+     * @brief Get v4 signature's subpacket of the specified type and hashedness.
+     * @param stype subpacket type.
+     * @param hashed If true (default), then will search for subpacket only in hashed (i.e.
+     * covered by signature) area, otherwise will search in both hashed and non-hashed areas.
+     * @return pointer to the subpacket, or NULL if subpacket was not found.
+     */
+    pgp_sig_subpkt_t *      get_subpkt(pgp_sig_subpacket_type_t stype, bool hashed = true);
+    const pgp_sig_subpkt_t *get_subpkt(pgp_sig_subpacket_type_t stype,
+                                       bool                     hashed = true) const;
+    /* @brief Check whether v4 signature has subpacket of the specified type/hashedness */
+    bool has_subpkt(pgp_sig_subpacket_type_t stype, bool hashed = true) const;
+    /* @brief Check whether signature has signing key id (via v3 field, or v4 key id/key fp
+     * subpacket) */
+    bool has_keyid() const;
+    /**
+     * @brief Get signer's key id if available. Availability may be checked via has_keyid().
+     * @return signer's key id if available, or throws an exception otherwise.
+     */
+    pgp_key_id_t keyid() const;
+    /** @brief Set the signer's key id for the signature being populated. Version should be set
+     *         prior of setting key id. */
+    void set_keyid(const pgp_key_id_t &id);
+    /**
+     * @brief Check whether signature has valid issuer fingerprint subpacket.
+     * @return true if there is one, and it can be safely returned via keyfp() method or false
+     *         otherwise.
+     */
+    bool has_keyfp() const;
+    /**
+     * @brief Get signing key's fingerprint if it is available. Availability may be checked via
+     *        has_keyfp() method.
+     * @return fingerprint or throws an error if it is unavailable.
+     */
+    pgp_fingerprint_t keyfp() const;
+
+    /** @brief Set signing key's fingerprint. Works only for signatures with version 4 and up,
+     *         so version should be set prior to fingerprint. */
+    void set_keyfp(const pgp_fingerprint_t &fp);
+
+    /**
+     * @brief Get signature's creation time
+     * @return time in seconds since the Jan 1, 1970 UTC. 0 is the default value and returned
+     *         even if creation time is not available
+     */
+    uint32_t creation() const;
+
+    /**
+     * @brief Set signature's creation time
+     * @param ctime creation time in seconds since the Jan 1, 1970 UTC.
+     */
+    void set_creation(uint32_t ctime);
+
+    /**
+     * @brief Get the signature's expiration time
+     * @return expiration time in seconds since the creation time. 0 if signature never
+     * expires.
+     */
+    uint32_t expiration() const;
+
+    /**
+     * @brief Set the signature's expiration time
+     * @param etime expiration time
+     */
+    void set_expiration(uint32_t etime);
+
+    /**
+     * @brief Get the key expiration time
+     * @return expiration time in seconds since the creation time. 0 if key never expires.
+     */
+    uint32_t key_expiration() const;
+
+    /**
+     * @brief Set the key expiration time
+     * @param etime expiration time
+     */
+    void set_key_expiration(uint32_t etime);
+
+    /**
+     * @brief Get the key flags
+     * @return byte of key flags. If there is no corresponding subpackets then 0 is returned.
+     */
+    uint8_t key_flags() const;
+
+    /**
+     * @brief Set the key flags
+     * @param flags byte of key flags
+     */
+    void set_key_flags(uint8_t flags);
+
+    /**
+     * @brief Get the primary user id flag
+     * @return true if user id is marked as primary or false otherwise
+     */
+    bool primary_uid() const;
+
+    /**
+     * @brief Set the primary user id flag
+     * @param primary true if user id should be marked as primary
+     */
+    void set_primary_uid(bool primary);
+
+    /** @brief Get preferred symmetric algorithms if any. If there are no ones then empty
+     *         vector is returned. */
+    std::vector<uint8_t> preferred_symm_algs() const;
+
+    /** @brief Set the preferred symmetric algorithms. If empty vector is passed then
+     *         corresponding subpacket is deleted. */
+    void set_preferred_symm_algs(const std::vector<uint8_t> &algs);
+
+    /** @brief Get preferred hash algorithms if any. If there are no ones then empty vector is
+     *         returned.*/
+    std::vector<uint8_t> preferred_hash_algs() const;
+
+    /** @brief Set the preferred hash algorithms. If empty vector is passed then corresponding
+     *         subpacket is deleted. */
+    void set_preferred_hash_algs(const std::vector<uint8_t> &algs);
+
+    /** @brief Get preferred compression algorithms if any. If there are no ones then empty
+     *         vector is returned.*/
+    std::vector<uint8_t> preferred_z_algs() const;
+
+    /** @brief Set the preferred compression algorithms. If empty vector is passed then
+     *         corresponding subpacket is deleted. */
+    void set_preferred_z_algs(const std::vector<uint8_t> &algs);
+
+    /** @brief Get key server preferences flags. If subpacket is not available then 0 is
+     *         returned. */
+    uint8_t key_server_prefs() const;
+
+    /** @brief Set key server preferences flags. */
+    void set_key_server_prefs(uint8_t prefs);
+
+    /** @brief Get preferred key server URI, if available. Otherwise empty string is returned.
+     */
+    std::string key_server() const;
+
+    /** @brief Set preferred key server URI. If it is empty string then subpacket is deleted if
+     *         it is available. */
+    void set_key_server(const std::string &uri);
+
+    /** @brief Get trust level, if available. Otherwise will return 0. See RFC 4880, 5.2.3.14.
+     *         for the detailed information on trust level and amount.
+     */
+    uint8_t trust_level() const;
+
+    /** @brief Get trust amount, if available. Otherwise will return 0. See RFC 4880, 5.2.3.14.
+     *         for the detailed information on trust level and amount.
+     */
+    uint8_t trust_amount() const;
+
+    /** @brief Set the trust level and amount. See RFC 4880, 5.2.3.14.
+     *         for the detailed information on trust level and amount.
+     */
+    void set_trust(uint8_t level, uint8_t amount);
+
+    /** @brief check whether signature is revocable. True by default.
+     */
+    bool revocable() const;
+
+    /** @brief Set the signature's revocability status.
+     */
+    void set_revocable(bool status);
+
+    /** @brief Get the key/subkey revocation reason in humand-readable form. If there is no
+     * revocation reason subpacket, then empty string will be returned.
+     */
+    std::string revocation_reason() const;
+
+    /** @brief Get the key/subkey revocation code. If there is no revocation reason subpacket,
+     *         then PGP_REVOCATION_NO_REASON will be rerturned. See the RFC 4880, 5.2.3.24 for
+     *         the detailed explanation.
+     */
+    pgp_revocation_type_t revocation_code() const;
+
+    /** @brief Set the revocation reason and code for key/subkey revocation signature. See the
+     *         RFC 4880, 5.2.3.24 for the detailed explanation.
+     */
+    void set_revocation_reason(pgp_revocation_type_t code, const std::string &reason);
+
+    /**
+     * @brief Check whether signer's key supports certain feature(s). Makes sense only for
+     * self-signature, for more details see the RFC 4880bis, 5.2.3.25. If there is no
+     * corresponding subpacket then false will be returned.
+     * @param flags one or more flags, combined via bitwise OR operation.
+     * @return true if key is claimed to support all of the features listed in flags, or false
+     * otherwise
+     */
+    bool key_has_features(pgp_key_feature_t flags) const;
+
+    /**
+     * @brief Set the features supported by the signer's key, makes sense only for
+     * self-signature. For more details see the RFC 4880bis, 5.2.3.25.
+     * @param flags one or more flags, combined via bitwise OR operation.
+     */
+    void set_key_features(pgp_key_feature_t flags);
+
+    /** @brief Get signer's user id, if available. Otherwise empty string is returned. See the
+     *         RFC 4880bis, 5.2.3.23 for details.
+     */
+    std::string signer_uid() const;
+
+    /**
+     * @brief Set the signer's uid, responcible for the signature creation. See the RFC
+     * 4880bis, 5.2.3.23 for details.
+     */
+    void set_signer_uid(const std::string &uid);
+
+    /**
+     * @brief Add subpacket of the specified type to v4 signature
+     * @param type type of the subpacket
+     * @param datalen length of the subpacket body
+     * @param reuse replace already existing subpacket of the specified type if any
+     * @return reference to the subpacket structure or throws an exception
+     */
+    pgp_sig_subpkt_t &add_subpkt(pgp_sig_subpacket_type_t type, size_t datalen, bool reuse);
+
+    /**
+     * @brief Remove signature's subpacket
+     * @param subpkt subpacket to remove. If not in the subpackets list then no action is
+     * taken.
+     */
+    void remove_subpkt(pgp_sig_subpkt_t *subpkt);
+
+    /**
+     * @brief Check whether signature packet matches one-pass signature packet.
+     * @param onepass reference to the read one-pass signature packet
+     * @return true if sig corresponds to onepass or false otherwise
+     */
+    bool matches_onepass(const pgp_one_pass_sig_t &onepass) const;
 } pgp_signature_t;
 
 /** pgp_rawpacket_t */
 typedef struct pgp_rawpacket_t {
     pgp_pkt_type_t       tag;
     std::vector<uint8_t> raw;
 
     pgp_rawpacket_t() = default;
@@ -399,78 +665,76 @@ typedef enum {
     PGP_LDT_TEXT = 't',
     PGP_LDT_UTF8 = 'u',
     PGP_LDT_LOCAL = 'l',
     PGP_LDT_LOCAL2 = '1'
 } pgp_litdata_enum;
 
 /** public-key encrypted session key packet */
 typedef struct pgp_pk_sesskey_t {
-    unsigned         version;
-    pgp_key_id_t     key_id;
-    pgp_pubkey_alg_t alg;
+    unsigned         version{};
+    pgp_key_id_t     key_id{};
+    pgp_pubkey_alg_t alg{};
 
-    pgp_encrypted_material_t material;
+    pgp_encrypted_material_t material{};
 } pgp_pk_sesskey_t;
 
 /** pkp_sk_sesskey_t */
-typedef struct {
-    unsigned       version;
-    pgp_symm_alg_t alg;
-    pgp_s2k_t      s2k;
-    uint8_t        enckey[PGP_MAX_KEY_SIZE + PGP_AEAD_MAX_TAG_LEN + 1];
-    unsigned       enckeylen;
+typedef struct pgp_sk_sesskey_t {
+    unsigned       version{};
+    pgp_symm_alg_t alg{};
+    pgp_s2k_t      s2k{};
+    uint8_t        enckey[PGP_MAX_KEY_SIZE + PGP_AEAD_MAX_TAG_LEN + 1]{};
+    unsigned       enckeylen{};
     /* v5 specific fields */
-    pgp_aead_alg_t aalg;
-    uint8_t        iv[PGP_MAX_BLOCK_SIZE];
-    unsigned       ivlen;
+    pgp_aead_alg_t aalg{};
+    uint8_t        iv[PGP_MAX_BLOCK_SIZE]{};
+    unsigned       ivlen{};
 } pgp_sk_sesskey_t;
 
 /* user revocation info */
 typedef struct pgp_revoke_t {
     uint32_t              uid;    /* index in uid array */
     pgp_revocation_type_t code;   /* revocation code */
     std::string           reason; /* revocation reason */
 } pgp_revoke_t;
 
 typedef struct pgp_user_prefs_t {
     // preferred symmetric algs (pgp_symm_alg_t)
-    uint8_t *symm_algs;
-    size_t   symm_alg_count;
+    std::vector<uint8_t> symm_algs{};
     // preferred hash algs (pgp_hash_alg_t)
-    uint8_t *hash_algs;
-    size_t   hash_alg_count;
+    std::vector<uint8_t> hash_algs{};
     // preferred compression algs (pgp_compression_type_t)
-    uint8_t *z_algs;
-    size_t   z_alg_count;
+    std::vector<uint8_t> z_algs{};
     // key server preferences (pgp_key_server_prefs_t)
-    uint8_t *ks_prefs;
-    size_t   ks_pref_count;
+    std::vector<uint8_t> ks_prefs{};
     // preferred key server
-    uint8_t *key_server;
+    std::string key_server{};
+
+    void set_symm_algs(const std::vector<uint8_t> &algs);
+    void add_symm_alg(pgp_symm_alg_t alg);
+    void set_hash_algs(const std::vector<uint8_t> &algs);
+    void add_hash_alg(pgp_hash_alg_t alg);
+    void set_z_algs(const std::vector<uint8_t> &algs);
+    void add_z_alg(pgp_compression_type_t alg);
+    void set_ks_prefs(const std::vector<uint8_t> &prefs);
+    void add_ks_pref(pgp_key_server_prefs_t pref);
 } pgp_user_prefs_t;
 
 /** information about the signature */
 typedef struct pgp_subsig_t {
     uint32_t         uid;         /* index in userid array in key for certification sig */
     pgp_signature_t  sig;         /* signature packet */
     pgp_rawpacket_t  rawpkt;      /* signature's rawpacket */
     uint8_t          trustlevel;  /* level of trust */
     uint8_t          trustamount; /* amount of trust */
     uint8_t          key_flags;   /* key flags for certification/direct key sig */
     pgp_user_prefs_t prefs;       /* user preferences for certification sig */
     bool             validated;   /* signature was validated */
     bool             valid;       /* signature was validated and is valid */
-
-    pgp_subsig_t() = default;
-    pgp_subsig_t(const pgp_subsig_t &src);
-    pgp_subsig_t(pgp_subsig_t &&src);
-    pgp_subsig_t &operator=(pgp_subsig_t &&src);
-    pgp_subsig_t &operator=(const pgp_subsig_t &src);
-    ~pgp_subsig_t();
 } pgp_subsig_t;
 
 typedef struct pgp_userid_t {
     pgp_userid_pkt_t pkt;    /* User ID or User Attribute packet as it was loaded */
     pgp_rawpacket_t  rawpkt; /* Raw packet contents */
     std::string      str;    /* Human-readable representation of the userid */
 } pgp_userid_t;
 
@@ -503,31 +767,31 @@ typedef struct rnp_keygen_crypto_params_
         struct rnp_keygen_ecc_params_t     ecc;
         struct rnp_keygen_rsa_params_t     rsa;
         struct rnp_keygen_dsa_params_t     dsa;
         struct rnp_keygen_elgamal_params_t elgamal;
     };
 } rnp_keygen_crypto_params_t;
 
 typedef struct rnp_selfsig_cert_info_t {
-    uint8_t          userid[MAX_ID_LENGTH]; /* userid, required */
-    uint8_t          key_flags;             /* key flags */
-    uint32_t         key_expiration;        /* key expiration time (sec), 0 = no expiration */
-    pgp_user_prefs_t prefs;                 /* user preferences, optional */
-    unsigned         primary : 1;           /* mark this as the primary user id */
+    uint8_t          userid[MAX_ID_LENGTH]{}; /* userid, required */
+    uint8_t          key_flags{};             /* key flags */
+    uint32_t         key_expiration{}; /* key expiration time (sec), 0 = no expiration */
+    pgp_user_prefs_t prefs{};          /* user preferences, optional */
+    bool             primary : 1;      /* mark this as the primary user id */
 } rnp_selfsig_cert_info_t;
 
 typedef struct rnp_selfsig_binding_info_t {
     uint8_t  key_flags;
     uint32_t key_expiration;
 } rnp_selfsig_binding_info_t;
 
 typedef struct rnp_keygen_primary_desc_t {
-    rnp_keygen_crypto_params_t crypto;
-    rnp_selfsig_cert_info_t    cert;
+    rnp_keygen_crypto_params_t crypto{};
+    rnp_selfsig_cert_info_t    cert{};
 } rnp_keygen_primary_desc_t;
 
 typedef struct rnp_keygen_subkey_desc_t {
     rnp_keygen_crypto_params_t crypto;
     rnp_selfsig_binding_info_t binding;
 } rnp_keygen_subkey_desc_t;
 
 typedef struct rnp_key_protection_params_t {
--- a/third_party/rnp/src/lib/version.h
+++ b/third_party/rnp/src/lib/version.h
@@ -23,19 +23,19 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
 #define RNP_VERSION_MAJOR 0
 #define RNP_VERSION_MINOR 13
 #define RNP_VERSION_PATCH 1
 
 #define RNP_VERSION_STRING "0.13.1"
-#define RNP_VERSION_STRING_FULL "0.13.1+git20200913.49a675e3.MZLA"
+#define RNP_VERSION_STRING_FULL "0.13.1+git20201030.a2c5ecd3.MZLA"
 
-#define RNP_VERSION_COMMIT_TIMESTAMP 1600015711
+#define RNP_VERSION_COMMIT_TIMESTAMP 1604058640
 
 // using a 32-bit version with 10 bits per component
 #define RNP_VERSION_COMPONENT_MASK 0x3ff
 #define RNP_VERSION_MAJOR_SHIFT 20
 #define RNP_VERSION_MINOR_SHIFT 10
 #define RNP_VERSION_PATCH_SHIFT 0
 #define RNP_VERSION_CODE_FOR(major, minor, patch)                        \
     (((major & RNP_VERSION_COMPONENT_MASK) << RNP_VERSION_MAJOR_SHIFT) | \
--- a/third_party/rnp/src/librekey/key_store_g10.cpp
+++ b/third_party/rnp/src/librekey/key_store_g10.cpp
@@ -42,35 +42,16 @@
 #define G10_CBC_IV_SIZE 16
 
 #define G10_OCB_NONCE_SIZE 12
 
 #define G10_SHA1_HASH_SIZE 20
 
 #define G10_PROTECTED_AT_SIZE 15
 
-typedef struct {
-    size_t   len;
-    uint8_t *bytes;
-} s_exp_block_t;
-
-typedef struct sub_element_t sub_element_t;
-
-typedef struct {
-    list sub_elements; // list of sub_element_t
-} s_exp_t;
-
-struct sub_element_t {
-    bool is_block;
-    union {
-        s_exp_t       s_exp;
-        s_exp_block_t block;
-    };
-};
-
 typedef struct format_info {
     pgp_symm_alg_t    cipher;
     pgp_cipher_mode_t cipher_mode;
     pgp_hash_alg_t    hash_alg;
     const char *      botan_cipher_name;
     size_t            cipher_block_size;
     const char *      g10_type;
     size_t            iv_size;
@@ -169,17 +150,17 @@ parse_format(const char *format, size_t 
         if (strlen(formats[i].g10_type) == format_len &&
             !strncmp(formats[i].g10_type, format, format_len)) {
             return &formats[i];
         }
     }
     return NULL;
 }
 
-static void
+void
 destroy_s_exp(s_exp_t *s_exp)
 {
     if (s_exp == NULL) {
         return;
     }
 
     for (list_item *li = list_front(s_exp->sub_elements); li; li = list_next(li)) {
         sub_element_t *sub_el = (sub_element_t *) li;
@@ -257,29 +238,34 @@ add_sub_sexp_to_sexp(s_exp_t *s_exp, s_e
  * Supported format: (1:a2:ab(3:asd1:a))
  * It should be parsed to:
  *   - a
  *   - ab
  *   + - asd
  *     - a
  *
  */
-static bool
-parse_sexp(s_exp_t *s_exp, const char **r_bytes, size_t *r_length)
+bool
+parse_sexp(s_exp_t *s_exp, const char **r_bytes, size_t *r_length, size_t depth)
 {
     size_t      length = *r_length;
     const char *bytes = *r_bytes;
 
     s_exp_t new_s_exp = {0};
 
     if (!bytes || !length) {
         RNP_LOG("empty s-exp");
         return true;
     }
 
+    if (depth > SXP_MAX_DEPTH) {
+        RNP_LOG("sxp maximum recursion depth exceeded");
+        return false;
+    }
+
     if (*bytes != '(') { // doesn't start from (
         return false;
     }
 
     bytes++;
     length--;
 
     do {
@@ -291,17 +277,17 @@ parse_sexp(s_exp_t *s_exp, const char **
 
         if (*bytes == '(') {
             s_exp_t *new_sub_s_exp;
 
             if (!add_sub_sexp_to_sexp(&new_s_exp, &new_sub_s_exp)) {
                 return false;
             }
 
-            if (!parse_sexp(new_sub_s_exp, &bytes, &length)) {
+            if (!parse_sexp(new_sub_s_exp, &bytes, &length, depth + 1)) {
                 destroy_s_exp(&new_s_exp);
                 return false;
             }
 
             if (!length) {
                 RNP_LOG("No space for closing ) left.");
                 destroy_s_exp(&new_s_exp);
                 return false;
--- a/third_party/rnp/src/librekey/key_store_g10.h
+++ b/third_party/rnp/src/librekey/key_store_g10.h
@@ -25,17 +25,40 @@
  */
 
 #ifndef RNP_KEY_STORE_G10_H
 #define RNP_KEY_STORE_G10_H
 
 #include <rekey/rnp_key_store.h>
 #include <librepgp/stream-common.h>
 
+#define SXP_MAX_DEPTH 30
+
+typedef struct {
+    size_t   len;
+    uint8_t *bytes;
+} s_exp_block_t;
+
+typedef struct sub_element_t sub_element_t;
+
+typedef struct {
+    list sub_elements; // list of sub_element_t
+} s_exp_t;
+
+struct sub_element_t {
+    bool is_block;
+    union {
+        s_exp_t       s_exp;
+        s_exp_block_t block;
+    };
+};
+
 bool rnp_key_store_g10_from_src(rnp_key_store_t *, pgp_source_t *, const pgp_key_provider_t *);
 bool rnp_key_store_g10_key_to_dst(pgp_key_t *, pgp_dest_t *);
 bool g10_write_seckey(pgp_dest_t *dst, pgp_key_pkt_t *seckey, const char *password);
 pgp_key_pkt_t *g10_decrypt_seckey(const uint8_t *      data,
                                   size_t               data_len,
                                   const pgp_key_pkt_t *pubkey,
                                   const char *         password);
+bool parse_sexp(s_exp_t *s_exp, const char **r_bytes, size_t *r_length, size_t depth = 1);
+void destroy_s_exp(s_exp_t *s_exp);
 
 #endif // RNP_KEY_STORE_G10_H
--- a/third_party/rnp/src/librekey/key_store_kbx.cpp
+++ b/third_party/rnp/src/librekey/key_store_kbx.cpp
@@ -589,17 +589,17 @@ rnp_key_store_kbx_write_pgp(rnp_key_stor
         if (!pbuf(&memdst, pgp_key_get_fp(subkey).fingerprint, PGP_FINGERPRINT_SIZE) ||
             !pu32(&memdst, memdst.writeb - 8) || // offset to keyid (part of fpr for V4)
             !pu16(&memdst, 0) ||                 // flags, not used by GnuPG
             !pu16(&memdst, 0)) {                 // RFU
             goto finish;
         }
         // load signature expirations while we're at it
         for (i = 0; i < pgp_key_get_subsig_count(subkey); i++) {
-            expiration = signature_get_key_expiration(&pgp_key_get_subsig(subkey, i)->sig);
+            expiration = pgp_key_get_subsig(subkey, i)->sig.key_expiration();
             if (list_append(&subkey_sig_expirations, &expiration, sizeof(expiration)) ==
                 NULL) {
                 goto finish;
             };
         }
     }
 
     if (!pu16(&memdst, 0)) { // Zero size of serial number
@@ -630,17 +630,17 @@ rnp_key_store_kbx_write_pgp(rnp_key_stor
     }
 
     if (!pu16(&memdst, pgp_key_get_subsig_count(key) + list_length(subkey_sig_expirations)) ||
         !pu16(&memdst, 4)) {
         goto finish;
     }
 
     for (i = 0; i < pgp_key_get_subsig_count(key); i++) {
-        if (!pu32(&memdst, signature_get_key_expiration(&pgp_key_get_subsig(key, i)->sig))) {
+        if (!pu32(&memdst, pgp_key_get_subsig(key, i)->sig.key_expiration())) {
             goto finish;
         }
     }
     for (list_item *expiration_entry = list_front(subkey_sig_expirations); expiration_entry;
          expiration_entry = list_next(expiration_entry)) {
         expiration = *(uint32_t *) expiration_entry;
         if (!pu32(&memdst, expiration)) {
             goto finish;
--- a/third_party/rnp/src/librekey/key_store_pgp.cpp
+++ b/third_party/rnp/src/librekey/key_store_pgp.cpp
@@ -71,17 +71,17 @@ bool
 rnp_key_add_signature(pgp_key_t *key, const pgp_signature_t *sig)
 {
     pgp_subsig_t *subsig = pgp_key_add_subsig(key);
     if (!subsig) {
         RNP_LOG("Failed to add subsig");
         return false;
     }
     /* setup subsig and key from signature */
-    if (!pgp_subsig_from_signature(subsig, sig)) {
+    if (!pgp_subsig_from_signature(*subsig, *sig)) {
         return false;
     }
     subsig->uid = pgp_key_get_userid_count(key) - 1;
     return true;
 }
 
 static bool
 rnp_key_add_signatures(pgp_key_t *key, pgp_signature_list_t &signatures)
@@ -235,35 +235,63 @@ rnp_key_from_transferable_subkey(pgp_key
     if (primary && !pgp_key_link_subkey_fp(primary, subkey)) {
         return false;
     }
 
     return true;
 }
 
 rnp_result_t
-rnp_key_store_pgp_read_from_src(rnp_key_store_t *keyring, pgp_source_t *src)
+rnp_key_store_pgp_read_key_from_src(rnp_key_store_t &keyring,
+                                    pgp_source_t &   src,
+                                    bool             skiperrors)
+{
+    pgp_transferable_key_t key;
+    rnp_result_t           ret = process_pgp_key_auto(src, key, true, skiperrors);
+
+    if (ret && (!skiperrors || (ret != RNP_ERROR_BAD_FORMAT))) {
+        return ret;
+    }
+
+    /* check whether we have primary key */
+    if (key.key.tag != PGP_PKT_RESERVED) {
+        return rnp_key_store_add_transferable_key(&keyring, &key) ? RNP_SUCCESS :
+                                                                    RNP_ERROR_BAD_STATE;
+    }
+
+    /* we just skipped some unexpected packets and read nothing */
+    if (key.subkeys.empty()) {
+        return RNP_SUCCESS;
+    }
+
+    return rnp_key_store_add_transferable_subkey(&keyring, &key.subkeys.front(), NULL) ?
+             RNP_SUCCESS :
+             RNP_ERROR_BAD_STATE;
+}
+
+rnp_result_t
+rnp_key_store_pgp_read_from_src(rnp_key_store_t *keyring, pgp_source_t *src, bool skiperrors)
 {
     rnp_result_t ret = RNP_ERROR_GENERIC;
 
     /* check whether we have transferable subkey in source */
     if (is_subkey_pkt(stream_pkt_type(src))) {
         pgp_transferable_subkey_t tskey;
-        ret = process_pgp_subkey(*src, tskey, keyring->skip_parsing_errors);
+        ret = process_pgp_subkey(*src, tskey, skiperrors);
         if (ret) {
             return ret;
         }
         return rnp_key_store_add_transferable_subkey(keyring, &tskey, NULL) ?
                  RNP_SUCCESS :
                  RNP_ERROR_BAD_STATE;
     }
 
     /* process armored or raw transferable key packets sequence(s) */
     pgp_key_sequence_t keys;
-    if ((ret = process_pgp_keys(src, keys, keyring->skip_parsing_errors))) {
+    if ((ret = process_pgp_keys(src, keys, skiperrors))) {
         return ret;
     }
 
     for (auto &key : keys.keys) {
         if (!rnp_key_store_add_transferable_key(keyring, &key)) {
             return RNP_ERROR_BAD_STATE;
         }
     }
--- a/third_party/rnp/src/librekey/key_store_pgp.h
+++ b/third_party/rnp/src/librekey/key_store_pgp.h
@@ -54,17 +54,25 @@
 
 #ifndef KEY_STORE_PGP_H_
 #define KEY_STORE_PGP_H_
 
 #include <rekey/rnp_key_store.h>
 #include <librepgp/stream-common.h>
 #include <librepgp/stream-key.h>
 
-rnp_result_t rnp_key_store_pgp_read_from_src(rnp_key_store_t *keyring, pgp_source_t *src);
+/* Read the whole keyring from the src, processing all available keys or subkeys */
+rnp_result_t rnp_key_store_pgp_read_from_src(rnp_key_store_t *keyring,
+                                             pgp_source_t *   src,
+                                             bool             skiperrors = false);
+
+/* Read the first key or subkey from the src */
+rnp_result_t rnp_key_store_pgp_read_key_from_src(rnp_key_store_t &keyring,
+                                                 pgp_source_t &   src,
+                                                 bool             skiperrors = false);
 
 bool rnp_key_store_pgp_write_to_dst(rnp_key_store_t *key_store, pgp_dest_t *dst);
 
 bool rnp_key_store_add_transferable_subkey(rnp_key_store_t *          keyring,
                                            pgp_transferable_subkey_t *tskey,
                                            pgp_key_t *                pkey);
 
 bool rnp_key_store_add_transferable_key(rnp_key_store_t *       keyring,
--- a/third_party/rnp/src/librekey/rnp_key_store.cpp
+++ b/third_party/rnp/src/librekey/rnp_key_store.cpp
@@ -364,46 +364,40 @@ rnp_key_store_merge_key(pgp_key_t *dst, 
 
     *dst = std::move(tmpkey);
     return true;
 }
 
 static bool
 rnp_key_store_refresh_subkey_grips(rnp_key_store_t *keyring, pgp_key_t *key)
 {
-    pgp_key_id_t      keyid = {};
-    pgp_fingerprint_t keyfp = {};
-
     if (pgp_key_is_subkey(key)) {
         RNP_LOG("wrong argument");
         return false;
     }
 
     for (auto &skey : keyring->keys) {
         bool found = false;
 
         /* if we have primary_grip then we also added to subkey_grips */
         if (!pgp_key_is_subkey(&skey) || pgp_key_has_primary_fp(&skey)) {
             continue;
         }
 
         for (unsigned i = 0; i < pgp_key_get_subsig_count(&skey); i++) {
             const pgp_subsig_t *subsig = pgp_key_get_subsig(&skey, i);
 
-            if (subsig->sig.type != PGP_SIG_SUBKEY) {
+            if (subsig->sig.type() != PGP_SIG_SUBKEY) {
                 continue;
             }
-
-            if (signature_get_keyfp(&subsig->sig, keyfp) && (pgp_key_get_fp(key) == keyfp)) {
+            if (subsig->sig.has_keyfp() && (pgp_key_get_fp(key) == subsig->sig.keyfp())) {
                 found = true;
                 break;
             }
-
-            if (signature_get_keyid(&subsig->sig, keyid) &&
-                (pgp_key_get_keyid(key) != keyid)) {
+            if (subsig->sig.has_keyid() && (pgp_key_get_keyid(key) == subsig->sig.keyid())) {
                 found = true;
                 break;
             }
         }
 
         if (found && !pgp_key_link_subkey_fp(key, &skey)) {
             return false;
         }
@@ -503,27 +497,27 @@ rnp_key_store_add_key(rnp_key_store_t *k
             added_key = &keyring->keys.back();
             keyring->keybyfp[pgp_key_get_fp(srckey)] = std::prev(keyring->keys.end());
         } catch (const std::exception &e) {
             RNP_LOG("%s", e.what());
             return NULL;
         }
         try {
             *added_key = pgp_key_t(*srckey);
+            /* primary key may be added after subkeys, so let's handle this case correctly */
+            if (!rnp_key_store_refresh_subkey_grips(keyring, added_key)) {
+                RNP_LOG_KEY("failed to refresh subkey grips for %s", added_key);
+            }
         } catch (const std::exception &e) {
             RNP_LOG_KEY("key %s copying failed", srckey);
             RNP_LOG("%s", e.what());
             keyring->keys.pop_back();
             keyring->keybyfp.erase(pgp_key_get_fp(srckey));
             return NULL;
         }
-        /* primary key may be added after subkeys, so let's handle this case correctly */
-        if (!rnp_key_store_refresh_subkey_grips(keyring, added_key)) {
-            RNP_LOG_KEY("failed to refresh subkey grips for %s", added_key);
-        }
     }
 
     RNP_DLOG("keyc %lu", (long unsigned) rnp_key_store_get_key_count(keyring));
     /* validate all added keys if not disabled or already validated */
     if (!keyring->disable_validation && !added_key->validated) {
         pgp_key_revalidate_updated(added_key, keyring);
     } else if (!pgp_key_refresh_data(added_key)) {
         RNP_LOG_KEY("Failed to refresh key %s data", srckey);
@@ -568,35 +562,35 @@ rnp_key_store_import_key(rnp_key_store_t
     return exkey;
 }
 
 pgp_key_t *
 rnp_key_store_get_signer_key(rnp_key_store_t *store, const pgp_signature_t *sig)
 {
     pgp_key_search_t search = {};
     // prefer using the issuer fingerprint when available
-    if (signature_has_keyfp(sig) && signature_get_keyfp(sig, search.by.fingerprint)) {
+    if (sig->has_keyfp()) {
+        search.by.fingerprint = sig->keyfp();
         search.type = PGP_KEY_SEARCH_FINGERPRINT;
         return rnp_key_store_search(store, &search, NULL);
     }
     // fall back to key id search
-    if (signature_get_keyid(sig, search.by.keyid)) {
-        search.type = PGP_KEY_SEARCH_KEYID;
+    if (sig->has_keyid()) {
+        search.by.keyid = sig->keyid();
         return rnp_key_store_search(store, &search, NULL);
     }
     return NULL;
 }
 
 static pgp_sig_import_status_t
 rnp_key_store_import_subkey_signature(rnp_key_store_t *      keyring,
                                       pgp_key_t *            key,
                                       const pgp_signature_t *sig)
 {
-    pgp_sig_type_t sigtype = signature_get_type(sig);
-    if ((sigtype != PGP_SIG_SUBKEY) && (sigtype != PGP_SIG_REV_SUBKEY)) {
+    if ((sig->type() != PGP_SIG_SUBKEY) && (sig->type() != PGP_SIG_REV_SUBKEY)) {
         return PGP_SIG_IMPORT_STATUS_UNKNOWN;
     }
     pgp_key_t *primary = rnp_key_store_get_signer_key(keyring, sig);
     if (!primary || !pgp_key_has_primary_fp(key)) {
         RNP_LOG("No primary grip or primary key");
         return PGP_SIG_IMPORT_STATUS_UNKNOWN_KEY;
     }
     if (pgp_key_get_fp(primary) != pgp_key_get_primary_fp(key)) {
@@ -624,19 +618,18 @@ rnp_key_store_import_subkey_signature(rn
 pgp_sig_import_status_t
 rnp_key_store_import_key_signature(rnp_key_store_t *      keyring,
                                    pgp_key_t *            key,
                                    const pgp_signature_t *sig)
 {
     if (pgp_key_is_subkey(key)) {
         return rnp_key_store_import_subkey_signature(keyring, key, sig);
     }
-    pgp_sig_type_t sigtype = signature_get_type(sig);
-    if ((sigtype != PGP_SIG_DIRECT) && (sigtype != PGP_SIG_REV_KEY)) {
-        RNP_LOG("Wrong signature type: %d", (int) sigtype);
+    if ((sig->type() != PGP_SIG_DIRECT) && (sig->type() != PGP_SIG_REV_KEY)) {
+        RNP_LOG("Wrong signature type: %d", (int) sig->type());
         return PGP_SIG_IMPORT_STATUS_UNKNOWN;
     }
 
     pgp_key_t tmpkey;
     if (!pgp_key_from_pkt(&tmpkey, &key->pkt) || !rnp_key_add_signature(&tmpkey, sig) ||
         !pgp_key_refresh_data(&tmpkey)) {
         RNP_LOG("Failed to add signature to the key.");
         return PGP_SIG_IMPORT_STATUS_UNKNOWN;
@@ -658,19 +651,18 @@ rnp_key_store_import_signature(rnp_key_s
                                pgp_sig_import_status_t *status)
 {
     pgp_sig_import_status_t tmp_status = PGP_SIG_IMPORT_STATUS_UNKNOWN;
     if (!status) {
         status = &tmp_status;
     }
     *status = PGP_SIG_IMPORT_STATUS_UNKNOWN;
 
-    pgp_sig_type_t sigtype = signature_get_type(sig);
     /* we support only direct-key and key revocation signatures here */
-    if ((sigtype != PGP_SIG_DIRECT) && (sigtype != PGP_SIG_REV_KEY)) {
+    if ((sig->type() != PGP_SIG_DIRECT) && (sig->type() != PGP_SIG_REV_KEY)) {
         return NULL;
     }
 
     pgp_key_t *res_key = rnp_key_store_get_signer_key(keyring, sig);
     if (!res_key || !pgp_key_is_primary_key(res_key)) {
         *status = PGP_SIG_IMPORT_STATUS_UNKNOWN_KEY;
         return NULL;
     }
@@ -796,39 +788,36 @@ rnp_key_store_get_key_by_fpr(rnp_key_sto
         return NULL;
     }
     return &*it->second;
 }
 
 pgp_key_t *
 rnp_key_store_get_primary_key(rnp_key_store_t *keyring, const pgp_key_t *subkey)
 {
-    pgp_key_id_t      keyid = {};
-    pgp_fingerprint_t keyfp = {};
-
     if (!pgp_key_is_subkey(subkey)) {
         return NULL;
     }
 
     if (pgp_key_has_primary_fp(subkey)) {
         return rnp_key_store_get_key_by_fpr(keyring, pgp_key_get_primary_fp(subkey));
     }
 
     for (unsigned i = 0; i < pgp_key_get_subsig_count(subkey); i++) {
         const pgp_subsig_t *subsig = pgp_key_get_subsig(subkey, i);
-        if (subsig->sig.type != PGP_SIG_SUBKEY) {
+        if (subsig->sig.type() != PGP_SIG_SUBKEY) {
             continue;
         }
 
-        if (signature_get_keyfp(&subsig->sig, keyfp)) {
-            return rnp_key_store_get_key_by_fpr(keyring, keyfp);
+        if (subsig->sig.has_keyfp()) {
+            return rnp_key_store_get_key_by_fpr(keyring, subsig->sig.keyfp());
         }
 
-        if (signature_get_keyid(&subsig->sig, keyid)) {
-            return rnp_key_store_get_key_by_id(keyring, keyid, NULL);
+        if (subsig->sig.has_keyid()) {
+            return rnp_key_store_get_key_by_id(keyring, subsig->sig.keyid(), NULL);
         }
     }
 
     return NULL;
 }
 
 static bool
 grip_hash_mpi(pgp_hash_t *hash, const pgp_mpi_t *val, const char name, bool lzero)
--- a/third_party/rnp/src/librepgp/stream-armor.cpp
+++ b/third_party/rnp/src/librepgp/stream-armor.cpp
@@ -519,19 +519,54 @@ rnp_armor_guess_type(pgp_source_t *src)
         return PGP_ARMORED_SECRET_KEY;
     case PGP_PKT_SIGNATURE:
         return PGP_ARMORED_SIGNATURE;
     default:
         return PGP_ARMORED_UNKNOWN;
     }
 }
 
+static pgp_armored_msg_t
+rnp_armored_guess_type_by_readahead(pgp_source_t *src)
+{
+    if (!src->cache) {
+        return PGP_ARMORED_UNKNOWN;
+    }
+
+    pgp_source_t armorsrc = {0};
+    pgp_source_t memsrc = {0};
+    size_t       read;
+    // peek as much as the cache can take
+    bool cache_res = src_peek(src, NULL, sizeof(src->cache->buf), &read);
+    if (!cache_res || !read ||
+        init_mem_src(&memsrc,
+                     src->cache->buf + src->cache->pos,
+                     src->cache->len - src->cache->pos,
+                     false)) {
+        return PGP_ARMORED_UNKNOWN;
+    }
+    rnp_result_t res = init_armored_src(&armorsrc, &memsrc);
+    if (res) {
+        RNP_LOG("failed to parse armored data");
+        return PGP_ARMORED_UNKNOWN;
+    }
+    pgp_armored_msg_t guessed = rnp_armor_guess_type(&armorsrc);
+    src_close(&armorsrc);
+    src_close(&memsrc);
+    return guessed;
+}
+
 pgp_armored_msg_t
 rnp_armored_get_type(pgp_source_t *src)
 {
+    pgp_armored_msg_t guessed = rnp_armored_guess_type_by_readahead(src);
+    if (guessed != PGP_ARMORED_UNKNOWN) {
+        return guessed;
+    }
+
     char        hdr[128];
     const char *armhdr;
     size_t      armhdrlen;
     size_t      read;
 
     if (!src_peek(src, hdr, sizeof(hdr), &read) || (read < 20)) {
         return PGP_ARMORED_UNKNOWN;
     }
--- a/third_party/rnp/src/librepgp/stream-common.cpp
+++ b/third_party/rnp/src/librepgp/stream-common.cpp
@@ -1,10 +1,10 @@
 /*
- * Copyright (c) 2017, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2017-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.
  *
@@ -37,16 +37,19 @@
 #include <stdarg.h>
 #include <errno.h>
 #ifdef HAVE_FCNTL_H
 #include <fcntl.h>
 #endif
 #ifdef HAVE_LIMITS_H
 #include <limits.h>
 #endif
+#ifndef HAVE_MKSTEMP
+#include "file-utils.h"
+#endif
 #include <rnp/rnp_def.h>
 #include "rnp.h"
 #include "stream-common.h"
 #include "types.h"
 #include <algorithm>
 
 bool
 src_read(pgp_source_t *src, void *buf, size_t len, size_t *readres)
@@ -340,32 +343,32 @@ src_peek_line(pgp_source_t *src, char *b
     } while (scan_pos < len);
     return false;
 }
 
 bool
 init_src_common(pgp_source_t *src, size_t paramsize)
 {
     memset(src, 0, sizeof(*src));
-
-    if ((src->cache = (pgp_source_cache_t *) calloc(1, sizeof(pgp_source_cache_t))) == NULL) {
+    src->cache = (pgp_source_cache_t *) calloc(1, sizeof(*src->cache));
+    if (!src->cache) {
         RNP_LOG("cache allocation failed");
         return false;
     }
     src->cache->readahead = true;
-
-    if (paramsize > 0) {
-        if ((src->param = calloc(1, paramsize)) == NULL) {
-            RNP_LOG("param allocation failed");
-            free(src->cache);
-            src->cache = NULL;
-            return false;
-        }
+    if (!paramsize) {
+        return true;
     }
-
+    src->param = calloc(1, paramsize);
+    if (!src->param) {
+        RNP_LOG("param allocation failed");
+        free(src->cache);
+        src->cache = NULL;
+        return false;
+    }
     return true;
 }
 
 typedef struct pgp_source_file_param_t {
     int fd;
 } pgp_source_file_param_t;
 
 static bool
@@ -507,24 +510,25 @@ mem_src_close(pgp_source_t *src)
         free(src->param);
         src->param = NULL;
     }
 }
 
 rnp_result_t
 init_mem_src(pgp_source_t *src, const void *mem, size_t len, bool free)
 {
-    pgp_source_mem_param_t *param;
-
+    if (!mem && len) {
+        return RNP_ERROR_NULL_POINTER;
+    }
     /* this is actually double buffering, but then src_peek will fail */
     if (!init_src_common(src, sizeof(pgp_source_mem_param_t))) {
         return RNP_ERROR_OUT_OF_MEMORY;
     }
 
-    param = (pgp_source_mem_param_t *) src->param;
+    pgp_source_mem_param_t *param = (pgp_source_mem_param_t *) src->param;
     param->memory = mem;
     param->len = len;
     param->pos = 0;
     param->free = free;
     src->read = mem_src_read;
     src->close = mem_src_close;
     src->finish = NULL;
     src->size = len;
@@ -607,26 +611,24 @@ mem_src_get_memory(pgp_source_t *src)
     param = (pgp_source_mem_param_t *) src->param;
     return param->memory;
 }
 
 bool
 init_dst_common(pgp_dest_t *dst, size_t paramsize)
 {
     memset(dst, 0, sizeof(*dst));
-
-    if (paramsize > 0) {
-        if ((dst->param = calloc(1, paramsize)) == NULL) {
+    if (paramsize) {
+        dst->param = calloc(1, paramsize);
+        if (!dst->param) {
             RNP_LOG("allocation failed");
             return false;
         }
     }
-
     dst->werr = RNP_SUCCESS;
-
     return true;
 }
 
 void
 dst_write(pgp_dest_t *dst, const void *buf, size_t len)
 {
     /* we call write function only if all previous calls succeeded */
     if ((len > 0) && (dst->write) && (dst->werr == RNP_SUCCESS)) {
@@ -758,16 +760,39 @@ file_dst_close(pgp_dest_t *dst, bool dis
             unlink(param->path);
         }
     }
 
     free(param);
     dst->param = NULL;
 }
 
+static rnp_result_t
+init_fd_dest(pgp_dest_t *dst, int fd, const char *path)
+{
+    pgp_dest_file_param_t *param;
+    size_t                 path_len = strlen(path);
+    if (path_len >= sizeof(param->path)) {
+        RNP_LOG("path too long");
+        return RNP_ERROR_BAD_PARAMETERS;
+    }
+    if (!init_dst_common(dst, sizeof(*param))) {
+        return RNP_ERROR_OUT_OF_MEMORY;
+    }
+
+    param = (pgp_dest_file_param_t *) dst->param;
+    param->fd = fd;
+    memcpy(param->path, path, path_len + 1);
+    dst->write = file_dst_write;
+    dst->close = file_dst_close;
+    dst->type = PGP_STREAM_FILE;
+
+    return RNP_SUCCESS;
+}
+
 rnp_result_t
 init_file_dest(pgp_dest_t *dst, const char *path, bool overwrite)
 {
     int                    fd;
     int                    flags;
     struct stat            st;
     pgp_dest_file_param_t *param;
 
@@ -797,35 +822,27 @@ init_file_dest(pgp_dest_t *dst, const ch
     flags |= overwrite ? O_TRUNC : O_EXCL;
 #ifdef HAVE_O_BINARY
     flags |= O_BINARY;
 #else
 #ifdef HAVE__O_BINARY
     flags |= _O_BINARY;
 #endif
 #endif
-    fd = open(path, flags, 0600);
+    fd = open(path, flags, S_IRUSR | S_IWUSR);
     if (fd < 0) {
         RNP_LOG("failed to create file '%s'. Error %d.", path, errno);
         return RNP_ERROR_WRITE;
     }
 
-    if (!init_dst_common(dst, sizeof(*param))) {
+    rnp_result_t res = init_fd_dest(dst, fd, path);
+    if (res) {
         close(fd);
-        return RNP_ERROR_OUT_OF_MEMORY;
     }
-
-    param = (pgp_dest_file_param_t *) dst->param;
-    param->fd = fd;
-    memcpy(param->path, path, path_len + 1);
-    dst->write = file_dst_write;
-    dst->close = file_dst_close;
-    dst->type = PGP_STREAM_FILE;
-
-    return RNP_SUCCESS;
+    return res;
 }
 
 #define TMPDST_SUFFIX ".rnp-tmp.XXXXXX"
 
 static rnp_result_t
 file_tmpdst_finish(pgp_dest_t *dst)
 {
     pgp_dest_file_param_t *param = (pgp_dest_file_param_t *) dst->param;
@@ -841,17 +858,17 @@ file_tmpdst_finish(pgp_dest_t *dst)
     plen = strnlen(param->path, sizeof(param->path));
     if (plen < strlen(TMPDST_SUFFIX)) {
         return RNP_ERROR_BAD_PARAMETERS;
     }
     strncpy(origpath, param->path, plen - strlen(TMPDST_SUFFIX));
 
     /* rename the temporary file */
     close(param->fd);
-    param->fd = 0;
+    param->fd = -1;
 
     /* check if file already exists */
     if (!stat(origpath, &st)) {
         if (!param->overwrite) {
             RNP_LOG("target path already exists");
             return RNP_ERROR_BAD_STATE;
         }
 #ifdef _WIN32
@@ -905,19 +922,27 @@ init_tmpfile_dest(pgp_dest_t *dst, const
     rnp_result_t           res = RNP_ERROR_GENERIC;
     int                    ires = 0;
 
     ires = snprintf(tmp, sizeof(tmp), "%s%s", path, TMPDST_SUFFIX);
     if ((ires < 0) || ((size_t) ires >= sizeof(tmp))) {
         RNP_LOG("failed to build file path");
         return RNP_ERROR_BAD_PARAMETERS;
     }
-    mktemp(tmp);
-
-    if ((res = init_file_dest(dst, tmp, overwrite))) {
+#ifdef HAVE_MKSTEMP
+    int fd = mkstemp(tmp);
+#else
+    int fd = rnp_mkstemp(tmp);
+#endif
+    if (fd < 0) {
+        RNP_LOG("failed to create temporary file with tempate '%s'. Error %d.", tmp, errno);
+        return RNP_ERROR_WRITE;
+    }
+    if ((res = init_fd_dest(dst, fd, tmp))) {
+        close(fd);
         return res;
     }
 
     /* now let's change some parameters to handle temporary file correctly */
     param = (pgp_dest_file_param_t *) dst->param;
     param->overwrite = overwrite;
     dst->finish = file_tmpdst_finish;
     dst->close = file_tmpdst_close;
@@ -1068,18 +1093,27 @@ mem_dest_own_memory(pgp_dest_t *dst)
     if (!param) {
         RNP_LOG("null param");
         return NULL;
     }
 
     dst_finish(dst);
 
     if (param->free) {
-        /* it may be larger then required */
-        param->memory = realloc(param->memory, dst->writeb);
+        if (!dst->writeb) {
+            free(param->memory);
+            param->memory = NULL;
+            return param->memory;
+        }
+        /* it may be larger then required - let's truncate */
+        void *newalloc = realloc(param->memory, dst->writeb);
+        if (!newalloc) {
+            return NULL;
+        }
+        param->memory = newalloc;
         param->allocated = dst->writeb;
         param->free = false;
         return param->memory;
     }
 
     /* in this case we should copy the memory */
     void *res = malloc(dst->writeb);
     if (res) {
--- a/third_party/rnp/src/librepgp/stream-common.h
+++ b/third_party/rnp/src/librepgp/stream-common.h
@@ -338,17 +338,19 @@ void mem_dest_discard_overflow(pgp_dest_
  *  @param dst pre-allocated and initialized memory dest
  *  @return pointer to the memory area or NULL if memory was not allocated
  **/
 void *mem_dest_get_memory(pgp_dest_t *dst);
 
 /** @brief get ownership on the memory dest's contents. This must be called only before
  *         closing the dest
  *  @param dst pre-allocated and initialized memory dest
- *  @return pointer to the memory area or NULL if memory was not allocated
+ *  @return pointer to the memory area or NULL if memory was not allocated (i.e. nothing was
+ *          written to the destination). Also NULL will be returned on possible (re-)allocation
+ *          failure, this case can be identified by non-zero dst->writeb.
  **/
 void *mem_dest_own_memory(pgp_dest_t *dst);
 
 /** @brief init null destination which silently discards all the output
  *  @param dst pre-allocated dest structure
  *  @return RNP_SUCCESS or error code
  **/
 rnp_result_t init_null_dest(pgp_dest_t *dst);
--- a/third_party/rnp/src/librepgp/stream-ctx.cpp
+++ b/third_party/rnp/src/librepgp/stream-ctx.cpp
@@ -1,10 +1,10 @@
 /*
- * Copyright (c) 2019, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2019-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.
  *
@@ -22,39 +22,40 @@
  * 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 <assert.h>
 #include "defaults.h"
+#include "utils.h"
 #include "stream-ctx.h"
 
 rng_t *
 rnp_ctx_rng_handle(const rnp_ctx_t *ctx)
 {
     assert(ctx->rng);
     return ctx->rng;
 }
 
 rnp_result_t
-rnp_ctx_add_encryption_password(rnp_ctx_t *    ctx,
+rnp_ctx_add_encryption_password(rnp_ctx_t &    ctx,
                                 const char *   password,
                                 pgp_hash_alg_t halg,
                                 pgp_symm_alg_t ealg,
                                 int            iterations)
 {
     rnp_symmetric_pass_info_t info = {};
 
     info.s2k.usage = PGP_S2KU_ENCRYPTED_AND_HASHED;
     info.s2k.specifier = PGP_S2KS_ITERATED_AND_SALTED;
     info.s2k.hash_alg = halg;
 
-    if (!rng_get_data(ctx->rng, info.s2k.salt, sizeof(info.s2k.salt))) {
+    if (!rng_get_data(ctx.rng, info.s2k.salt, sizeof(info.s2k.salt))) {
         return RNP_ERROR_GENERIC;
     }
     if (iterations == 0) {
         iterations = pgp_s2k_compute_iters(halg, DEFAULT_S2K_MSEC, DEFAULT_S2K_TUNE_MSEC);
     }
     if (!iterations) {
         return RNP_ERROR_BAD_PARAMETERS;
     }
@@ -69,24 +70,21 @@ rnp_ctx_add_encryption_password(rnp_ctx_
      * end up being used with until later.
      *
      * An alternative would be to keep a list of actual passwords and s2k params,
      * and save the key derivation for later.
      */
     if (!pgp_s2k_derive_key(&info.s2k, password, info.key, sizeof(info.key))) {
         return RNP_ERROR_GENERIC;
     }
-    if (!list_append(&ctx->passwords, &info, sizeof(info))) {
-        pgp_forget(&info, sizeof(info));
+    try {
+        ctx.passwords.push_back(info);
+    } catch (const std::exception &e) {
+        RNP_LOG("%s", e.what());
         return RNP_ERROR_OUT_OF_MEMORY;
     }
     return RNP_SUCCESS;
 }
 
-/* free operation context */
-void
-rnp_ctx_free(rnp_ctx_t *ctx)
+rnp_symmetric_pass_info_t::~rnp_symmetric_pass_info_t()
 {
-    free(ctx->filename);
-    list_destroy(&ctx->recipients);
-    list_destroy(&ctx->signers);
-    list_destroy(&ctx->passwords);
+    pgp_forget(key, sizeof(key));
 }
--- a/third_party/rnp/src/librepgp/stream-ctx.h
+++ b/third_party/rnp/src/librepgp/stream-ctx.h
@@ -1,10 +1,10 @@
 /*
- * Copyright (c) 2019, [Ribose Inc](https://www.ribose.com).
+ * Copyright (c) 2019-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.
  *
@@ -26,32 +26,42 @@
 
 #ifndef STREAM_CTX_H_
 #define STREAM_CTX_H_
 
 #include <stdint.h>
 #include <stdbool.h>
 #include <sys/types.h>
 #include "types.h"
+#include <string>
+#include <list>
 
 typedef enum rnp_operation_t {
     RNP_OP_UNKNOWN = 0,
     RNP_OP_DECRYPT_VERIFY = 1,
     RNP_OP_ENCRYPT_SIGN = 2,
     RNP_OP_ARMOR = 3
 } rnp_operation_t;
 
 /* signature info structure */
 typedef struct rnp_signer_info_t {
-    pgp_key_t *    key;
-    pgp_hash_alg_t halg;
-    int64_t        sigcreate;
-    uint64_t       sigexpire;
+    pgp_key_t *    key{};
+    pgp_hash_alg_t halg{};
+    int64_t        sigcreate{};
+    uint64_t       sigexpire{};
 } rnp_signer_info_t;
 
+typedef struct rnp_symmetric_pass_info_t {
+    pgp_s2k_t      s2k{};
+    pgp_symm_alg_t s2k_cipher{};
+    uint8_t        key[PGP_MAX_KEY_SIZE]{};
+
+    ~rnp_symmetric_pass_info_t();
+} rnp_symmetric_pass_info_t;
+
 /** rnp operation context : contains configuration data about the currently ongoing operation.
  *
  *  Common fields which make sense for every operation:
  *  - overwrite : silently overwrite output file if exists
  *  - armor : except cleartext signing, which outputs text in clear and always armor signature,
  *    this controls whether output is armored (base64-encoded). For armor/dearmor operation it
  *    controls the direction of the conversion (true means enarmor, false - dearmor),
  *  - rng : random number generator
@@ -77,47 +87,46 @@ typedef struct rnp_signer_info_t {
  *  - filename, filemtime, zalg, zlevel : only for attached signatures, see previous
  *
  *  For data decryption and/or verification there is not much of fields:
  *  - discard: dicard the output data (i.e. just decrypt and/or verify signatures)
  *
  */
 
 typedef struct rnp_ctx_t {
-    char *          filename;   /* name of the input file to store in literal data packet */
-    int64_t         filemtime;  /* file modification time to store in literal data packet */
-    int64_t         sigcreate;  /* signature creation time */
-    uint64_t        sigexpire;  /* signature expiration time */
-    bool            clearsign;  /* cleartext signature */
-    bool            detached;   /* detached signature */
-    pgp_hash_alg_t  halg;       /* hash algorithm */
-    pgp_symm_alg_t  ealg;       /* encryption algorithm */
-    int             zalg;       /* compression algorithm used */
-    int             zlevel;     /* compression level */
-    pgp_aead_alg_t  aalg;       /* non-zero to use AEAD */
-    int             abits;      /* AEAD chunk bits */
-    bool            overwrite;  /* allow to overwrite output file if exists */
-    bool            armor;      /* whether to use ASCII armor on output */
-    list            recipients; /* recipients of the encrypted message */
-    list            passwords;  /* list of rnp_symmetric_pass_info_t */
-    list            signers;    /* list of rnp_signer_info_t structures */
-    bool            discard;    /* discard the output */
-    rng_t *         rng;        /* pointer to rng_t */
-    rnp_operation_t operation;  /* current operation type */
+    std::string    filename{};  /* name of the input file to store in literal data packet */
+    int64_t        filemtime{}; /* file modification time to store in literal data packet */
+    int64_t        sigcreate{}; /* signature creation time */
+    uint64_t       sigexpire{}; /* signature expiration time */
+    bool           clearsign{}; /* cleartext signature */
+    bool           detached{};  /* detached signature */
+    pgp_hash_alg_t halg{};      /* hash algorithm */
+    pgp_symm_alg_t ealg{};      /* encryption algorithm */
+    int            zalg{};      /* compression algorithm used */
+    int            zlevel{};    /* compression level */
+    pgp_aead_alg_t aalg{};      /* non-zero to use AEAD */
+    int            abits{};     /* AEAD chunk bits */
+    bool           overwrite{}; /* allow to overwrite output file if exists */
+    bool           armor{};     /* whether to use ASCII armor on output */
+    std::list<pgp_key_t *> recipients{};              /* recipients of the encrypted message */
+    std::list<rnp_symmetric_pass_info_t> passwords{}; /* passwords to encrypt message */
+    std::list<rnp_signer_info_t>         signers{};   /* keys to which sign message */
+    bool                                 discard{};   /* discard the output */
+    rng_t *                              rng{};       /* pointer to rng_t */
+    rnp_operation_t                      operation{}; /* current operation type */
+
+    rnp_ctx_t() = default;
+    rnp_ctx_t(const rnp_ctx_t &) = delete;
+    rnp_ctx_t(rnp_ctx_t &&) = delete;
+
+    rnp_ctx_t &operator=(const rnp_ctx_t &) = delete;
+    rnp_ctx_t &operator=(rnp_ctx_t &&) = delete;
 } rnp_ctx_t;
 
-typedef struct rnp_symmetric_pass_info_t {
-    pgp_s2k_t      s2k;
-    pgp_symm_alg_t s2k_cipher;
-    uint8_t        key[PGP_MAX_KEY_SIZE];
-} rnp_symmetric_pass_info_t;
-
-/* free rnp operation context */
-void             rnp_ctx_free(rnp_ctx_t *);
 struct rng_st_t *rnp_ctx_rng_handle(const rnp_ctx_t *ctx);
 
-rnp_result_t rnp_ctx_add_encryption_password(rnp_ctx_t *    ctx,
+rnp_result_t rnp_ctx_add_encryption_password(rnp_ctx_t &    ctx,
                                              const char *   password,
                                              pgp_hash_alg_t halg,
                                              pgp_symm_alg_t ealg,
                                              int            iterations);
 
 #endif
\ No newline at end of file
--- a/third_party/rnp/src/librepgp/stream-dump.cpp
+++ b/third_party/rnp/src/librepgp/stream-dump.cpp
@@ -442,16 +442,24 @@ dst_print_s2k(pgp_dest_t *dst, pgp_s2k_t
         dst_printf(dst, "GPG extension num: %d\n", (int) s2k->gpg_ext_num);
         if (s2k->gpg_ext_num == PGP_S2K_GPG_SMARTCARD) {
             static_assert(sizeof(s2k->gpg_serial) == 16, "invalid s2k->gpg_serial size");
             size_t slen = s2k->gpg_serial_len > 16 ? 16 : s2k->gpg_serial_len;
             dst_print_hex(dst, "card serial number", s2k->gpg_serial, slen, true);
         }
         return;
     }
+    if (s2k->specifier == PGP_S2KS_EXPERIMENTAL) {
+        dst_print_hex(dst,
+                      "Unknown experimental s2k",
+                      s2k->experimental.data(),
+                      s2k->experimental.size(),
+                      true);
+        return;
+    }
     dst_print_halg(dst, "s2k hash algorithm", s2k->hash_alg);
     if ((s2k->specifier == PGP_S2KS_SALTED) ||
         (s2k->specifier == PGP_S2KS_ITERATED_AND_SALTED)) {
         dst_print_hex(dst, "s2k salt", s2k->salt, PGP_SALT_SIZE, false);
     }
     if (s2k->specifier == PGP_S2KS_ITERATED_AND_SALTED) {
         size_t real_iter = pgp_s2k_decode_iterations(s2k->iterations);
         dst_printf(dst, "s2k iterations: %zu (encoded as %u)\n", real_iter, s2k->iterations);
@@ -623,19 +631,19 @@ signature_dump_subpacket(rnp_dump_ctx_t 
         dst_print_raw(dst,
                       "message",
                       subpkt.fields.revocation_reason.str,
                       subpkt.fields.revocation_reason.len);
         break;
     }
     case PGP_SIG_SUBPKT_FEATURES:
         dst_printf(dst, "%s: 0x%02x ( ", sname, subpkt.data[0]);
-        dst_printf(dst, "%s", subpkt.fields.features.mdc ? "mdc " : "");
-        dst_printf(dst, "%s", subpkt.fields.features.aead ? "aead " : "");
-        dst_printf(dst, "%s", subpkt.fields.features.key_v5 ? "v5 keys " : "");
+        dst_printf(dst, "%s", subpkt.fields.features & PGP_KEY_FEATURE_MDC ? "mdc " : "");
+        dst_printf(dst, "%s", subpkt.fields.features & PGP_KEY_FEATURE_AEAD ? "aead " : "");
+        dst_printf(dst, "%s", subpkt.fields.features & PGP_KEY_FEATURE_V5 ? "v5 keys " : "");
         dst_printf(dst, ")\n");
         break;
     case PGP_SIG_SUBPKT_EMBEDDED_SIGNATURE:
         dst_printf(dst, "%s:\n", sname);
         stream_dump_signature_pkt(ctx, subpkt.fields.sig, dst);
         break;
     case PGP_SIG_SUBPKT_ISSUER_FPR:
         dst_print_hex(
@@ -687,17 +695,17 @@ signature_dump_subpackets(rnp_dump_ctx_t
 }
 
 static void
 stream_dump_signature_pkt(rnp_dump_ctx_t *ctx, pgp_signature_t *sig, pgp_dest_t *dst)
 {
     indent_dest_increase(dst);
 
     dst_printf(dst, "version: %d\n", (int) sig->version);
-    dst_print_sig_type(dst, "type", sig->type);
+    dst_print_sig_type(dst, "type", sig->type());
     if (sig->version < PGP_V4) {
         dst_print_time(dst, "creation time", sig->creation_time);
         dst_print_keyid(dst, "signing key id", sig->signer);
     }
     dst_print_palg(dst, NULL, sig->palg);
     dst_print_halg(dst, NULL, sig->halg);
 
     if (sig->version >= PGP_V4) {
@@ -1447,16 +1455,20 @@ obj_add_s2k_json(json_object *obj, pgp_s
         }
         if (s2k->gpg_ext_num == PGP_S2K_GPG_SMARTCARD) {
             size_t slen = s2k->gpg_serial_len > 16 ? 16 : s2k->gpg_serial_len;
             if (!obj_add_hex_json(s2k_obj, "card serial number", s2k->gpg_serial, slen)) {
                 return false;
             }
         }
     }
+    if (s2k->specifier == PGP_S2KS_EXPERIMENTAL) {
+        return obj_add_hex_json(
+          s2k_obj, "unknown experimental", s2k->experimental.data(), s2k->experimental.size());
+    }
     if (!obj_add_intstr_json(s2k_obj, "hash algorithm", s2k->hash_alg, hash_alg_map)) {
         return false;
     }
     if (((s2k->specifier == PGP_S2KS_SALTED) ||
          (s2k->specifier == PGP_S2KS_ITERATED_AND_SALTED)) &&
         !obj_add_hex_json(s2k_obj, "salt", s2k->salt, PGP_SALT_SIZE)) {
         return false;
     }
@@ -1605,21 +1617,27 @@ signature_dump_subpacket_json(rnp_dump_c
         return obj_add_field_json(
           obj,
           "message",
           json_object_new_string_len(subpkt.fields.revocation_reason.str,
                                      subpkt.fields.revocation_reason.len));
     }
     case PGP_SIG_SUBPKT_FEATURES:
         return obj_add_field_json(
-                 obj, "mdc", json_object_new_boolean(subpkt.fields.features.mdc)) &&
+                 obj,
+                 "mdc",
+                 json_object_new_boolean(subpkt.fields.features & PGP_KEY_FEATURE_MDC)) &&
                obj_add_field_json(
-                 obj, "aead", json_object_new_boolean(subpkt.fields.features.aead)) &&
+                 obj,
+                 "aead",
+                 json_object_new_boolean(subpkt.fields.features & PGP_KEY_FEATURE_AEAD)) &&
                obj_add_field_json(
-                 obj, "v5 keys", json_object_new_boolean(subpkt.fields.features.key_v5));
+                 obj,
+                 "v5 keys",
+                 json_object_new_boolean(subpkt.fields.features & PGP_KEY_FEATURE_V5));
     case PGP_SIG_SUBPKT_EMBEDDED_SIGNATURE: {
         json_object *sig = json_object_new_object();
         if (!sig || !obj_add_field_json(obj, "signature", sig)) {
             return false;
         }
         return !stream_dump_signature_pkt_json(ctx, subpkt.fields.sig, sig);
     }
     case PGP_SIG_SUBPKT_ISSUER_FPR:
@@ -1685,17 +1703,17 @@ stream_dump_signature_pkt_json(rnp_dump_
 {
     json_object *            material = NULL;
     pgp_signature_material_t sigmaterial = {};
     rnp_result_t             ret = RNP_ERROR_OUT_OF_MEMORY;
 
     if (!obj_add_field_json(pkt, "version", json_object_new_int(sig->version))) {
         goto done;
     }
-    if (!obj_add_intstr_json(pkt, "type", sig->type, sig_type_map)) {
+    if (!obj_add_intstr_json(pkt, "type", sig->type(), sig_type_map)) {
         goto done;
     }
 
     if (sig->version < PGP_V4) {
         if (!obj_add_field_json(
               pkt, "creation time", json_object_new_int(sig->creation_time))) {
             goto done;
         }
--- a/third_party/rnp/src/librepgp/stream-key.cpp
+++ b/third_party/rnp/src/librepgp/stream-key.cpp
@@ -296,68 +296,48 @@ transferable_userid_certify(const pgp_ke
     if (pgp_fingerprint(keyfp, &signer)) {
         RNP_LOG("failed to calculate keyfp");
         return NULL;
     }
 
     sig.version = PGP_V4;
     sig.halg = pgp_hash_adjust_alg_to_key(hash_alg, &signer);
     sig.palg = signer.alg;
-    sig.type = PGP_CERT_POSITIVE;
+    sig.set_type(PGP_CERT_POSITIVE);
 
-    if (!signature_set_keyfp(&sig, keyfp)) {
-        RNP_LOG("failed to set issuer fingerprint");
-        return NULL;
-    }
-    if (!signature_set_creation(&sig, time(NULL))) {
-        RNP_LOG("failed to set creation time");
-        return NULL;
-    }
-    if (cert.key_expiration && !signature_set_key_expiration(&sig, cert.key_expiration)) {
-        RNP_LOG("failed to set key expiration time");
-        return NULL;
-    }
-    if (cert.key_flags && !signature_set_key_flags(&sig, cert.key_flags)) {
-        RNP_LOG("failed to set key flags");
-        return NULL;
-    }
-    if (cert.primary && !signature_set_primary_uid(&sig, true)) {
-        RNP_LOG("failed to set primary userid");
-        return NULL;
-    }
-    const pgp_user_prefs_t *prefs = &cert.prefs;
-    if (prefs->symm_alg_count &&
-        !signature_set_preferred_symm_algs(
-          &sig, (uint8_t *) prefs->symm_algs, prefs->symm_alg_count)) {
-        RNP_LOG("failed to set symm alg prefs");
-        return NULL;
-    }
-    if (prefs->hash_alg_count &&
-        !signature_set_preferred_hash_algs(
-          &sig, (uint8_t *) prefs->hash_algs, prefs->hash_alg_count)) {
-        RNP_LOG("failed to set hash alg prefs");
-        return NULL;
-    }
-    if (prefs->z_alg_count &&
-        !signature_set_preferred_z_algs(&sig, (uint8_t *) prefs->z_algs, prefs->z_alg_count)) {
-        RNP_LOG("failed to set compress alg prefs");
-        return NULL;
-    }
-    if (prefs->ks_pref_count &&
-        !signature_set_key_server_prefs(&sig, (uint8_t) prefs->ks_prefs[0])) {
-        RNP_LOG("failed to set key server prefs");
-        return NULL;
-    }
-    if (prefs->key_server &&
-        !signature_set_preferred_key_server(&sig, (char *) prefs->key_server)) {
-        RNP_LOG("failed to set preferred key server");
-        return NULL;
-    }
-    if (!signature_set_keyid(&sig, keyid)) {
-        RNP_LOG("failed to set issuer key id");
+    try {
+        sig.set_keyfp(keyfp);
+        sig.set_creation(time(NULL));
+        if (cert.key_expiration) {
+            sig.set_key_expiration(cert.key_expiration);
+        }
+        if (cert.key_flags) {
+            sig.set_key_flags(cert.key_flags);
+        }
+        if (cert.primary) {
+            sig.set_primary_uid(true);
+        }
+        if (!cert.prefs.symm_algs.empty()) {
+            sig.set_preferred_symm_algs(cert.prefs.symm_algs);
+        }
+        if (!cert.prefs.hash_algs.empty()) {
+            sig.set_preferred_hash_algs(cert.prefs.hash_algs);
+        }
+        if (!cert.prefs.z_algs.empty()) {
+            sig.set_preferred_z_algs(cert.prefs.z_algs);
+        }
+        if (!cert.prefs.ks_prefs.empty()) {
+            sig.set_key_server_prefs(cert.prefs.ks_prefs[0]);
+        }
+        if (!cert.prefs.key_server.empty()) {
+            sig.set_key_server(cert.prefs.key_server);
+        }
+        sig.set_keyid(keyid);
+    } catch (const std::exception &e) {
+        RNP_LOG("failed to setup signature: %s", e.what());
         return NULL;
     }
 
     if (!signature_calculate_certification(&key, &userid.uid, &sig, &signer)) {
         RNP_LOG("failed to calculate signature");
         return NULL;
     }
     try {
@@ -378,28 +358,27 @@ signature_calculate_primary_binding(cons
 {
     pgp_key_id_t keyid = {};
     pgp_hash_t   hash = {};
     bool         res = false;
 
     sig->version = PGP_V4;
     sig->halg = pgp_hash_adjust_alg_to_key(halg, subkey);
     sig->palg = subkey->alg;
-    sig->type = PGP_SIG_PRIMARY;
+    sig->set_type(PGP_SIG_PRIMARY);
 
     if (pgp_keyid(keyid, subkey)) {
         RNP_LOG("failed to calculate keyid");
         return false;
     }
-    if (!signature_set_creation(sig, time(NULL))) {
-        RNP_LOG("failed to set embedded sig creation time");
-        return false;
-    }
-    if (!signature_set_keyid(sig, keyid)) {
-        RNP_LOG("failed to set issuer key id");
+    try {
+        sig->set_creation(time(NULL));
+        sig->set_keyid(keyid);
+    } catch (const std::exception &e) {
+        RNP_LOG("failed to setup embedded signature: %s", e.what());
         return false;
     }
     if (!signature_hash_binding(sig, key, subkey, &hash)) {
         RNP_LOG("failed to hash key and subkey");
         goto end;
     }
     if (!signature_fill_hashed_data(sig)) {
         RNP_LOG("failed to hash signature");
@@ -458,18 +437,20 @@ signature_calculate_binding(const pgp_ke
         }
         if (!signature_set_embedded_sig(sig, &embsig)) {
             RNP_LOG("failed to add primary key binding signature");
             goto end;
         }
     }
 
     /* add keyid since it should (probably) be after the primary key binding if any */
-    if (!signature_set_keyid(sig, keyid)) {
-        RNP_LOG("failed to set issuer key id");
+    try {
+        sig->set_keyid(keyid);
+    } catch (const std::exception &e) {
+        RNP_LOG("failed to set issuer key id: %s", e.what());
         goto end;
     }
     res = true;
 end:
     rng_destroy(&rng);
     return res;
 }
 
@@ -486,33 +467,29 @@ transferable_subkey_bind(const pgp_key_p
     }
 
     pgp_signature_t sig;
     pgp_key_flags_t realkf = (pgp_key_flags_t) 0;
 
     sig.version = PGP_V4;
     sig.halg = pgp_hash_adjust_alg_to_key(hash_alg, &key);
     sig.palg = key.alg;
-    sig.type = PGP_SIG_SUBKEY;
+    sig.set_type(PGP_SIG_SUBKEY);
 
-    if (!signature_set_keyfp(&sig, keyfp)) {
-        RNP_LOG("failed to set issuer fingerprint");
-        return NULL;
-    }
-    if (!signature_set_creation(&sig, time(NULL))) {
-        RNP_LOG("failed to set creation time");
-        return NULL;
-    }
-    if (binding.key_expiration &&
-        !signature_set_key_expiration(&sig, binding.key_expiration)) {
-        RNP_LOG("failed to set key expiration time");
-        return NULL;
-    }
-    if (binding.key_flags && !signature_set_key_flags(&sig, binding.key_flags)) {
-        RNP_LOG("failed to set key flags");
+    try {
+        sig.set_keyfp(keyfp);
+        sig.set_creation(time(NULL));
+        if (binding.key_expiration) {
+            sig.set_key_expiration(binding.key_expiration);
+        }
+        if (binding.key_flags) {
+            sig.set_key_flags(binding.key_flags);
+        }
+    } catch (const std::exception &e) {
+        RNP_LOG("failed to setup signature: %s", e.what());
         return NULL;
     }
 
     realkf = (pgp_key_flags_t) binding.key_flags;
     if (!realkf) {
         realkf = pgp_pk_alg_capabilities(subkey.subkey.alg);
     }
 
@@ -546,32 +523,25 @@ transferable_key_revoke(const pgp_key_pk
     if (pgp_fingerprint(keyfp, &signer)) {
         RNP_LOG("failed to calculate keyfp");
         return NULL;
     }
 
     sig.version = PGP_V4;
     sig.halg = pgp_hash_adjust_alg_to_key(hash_alg, &signer);
     sig.palg = signer.alg;
-    sig.type = is_primary_key_pkt(key.tag) ? PGP_SIG_REV_KEY : PGP_SIG_REV_SUBKEY;
+    sig.set_type(is_primary_key_pkt(key.tag) ? PGP_SIG_REV_KEY : PGP_SIG_REV_SUBKEY);
 
-    if (!signature_set_keyfp(&sig, keyfp)) {
-        RNP_LOG("failed to set issuer fingerprint");
-        return NULL;
-    }
-    if (!signature_set_creation(&sig, time(NULL))) {
-        RNP_LOG("failed to set creation time");
-        return NULL;
-    }
-    if (!signature_set_revocation_reason(&sig, revoke.code, revoke.reason.c_str())) {
-        RNP_LOG("failed to set revocation reason");
-        return NULL;
-    }
-    if (!signature_set_keyid(&sig, keyid)) {
-        RNP_LOG("failed to set issuer key id");
+    try {
+        sig.set_keyfp(keyfp);
+        sig.set_creation(time(NULL));
+        sig.set_revocation_reason(revoke.code, revoke.reason);
+        sig.set_keyid(keyid);
+    } catch (const std::exception &e) {
+        RNP_LOG("failed to setup signature: %s", e.what());
         return NULL;
     }
 
     if (is_primary_key_pkt(key.tag)) {
         res = signature_calculate_direct(&key, &sig, &signer);
     } else {
         res = signature_calculate_binding(&signer, &key, &sig, false);
     }
@@ -588,19 +558,22 @@ transferable_key_revoke(const pgp_key_pk
     }
 }
 
 static bool
 skip_pgp_packets(pgp_source_t *src, const std::set<pgp_pkt_type_t> &pkts)
 {
     do {
         int pkt = stream_pkt_type(src);
-        if (pkt <= 0) {
+        if (!pkt) {
             break;
         }
+        if (pkt < 0) {
+            return false;
+        }
         if (pkts.find((pgp_pkt_type_t) pkt) == pkts.end()) {
             return true;
         }
         uint64_t ppos = src->readb;
         if (stream_skip_packet(src)) {
             RNP_LOG("failed to skip packet at %" PRIu64, ppos);
             return false;
         }
@@ -676,71 +649,109 @@ process_pgp_subkey(pgp_source_t &src, pg
     }
 
     ret = process_pgp_key_signatures(&src, subkey.signatures, skiperrors);
 done:
     return ret;
 }
 
 rnp_result_t
+process_pgp_key_auto(pgp_source_t &          src,
+                     pgp_transferable_key_t &key,
+                     bool                    allowsub,
+                     bool                    skiperrors)
+{
+    key = {};
+    uint64_t srcpos = src.readb;
+    int      ptag = stream_pkt_type(&src);
+    if (is_subkey_pkt(ptag) && allowsub) {
+        pgp_transferable_subkey_t subkey;
+        rnp_result_t              ret = process_pgp_subkey(src, subkey, skiperrors);
+        if (subkey.subkey.tag != PGP_PKT_RESERVED) {
+            try {
+                key.subkeys.push_back(std::move(subkey));
+            } catch (const std::exception &e) {
+                RNP_LOG("%s", e.what());
+                ret = RNP_ERROR_OUT_OF_MEMORY;
+            }
+        }
+        /* change error code if we didn't process anything at all */
+        if (srcpos == src.readb) {
+            ret = RNP_ERROR_BAD_STATE;
+        }
+        return ret;
+    }
+
+    rnp_result_t ret = RNP_ERROR_BAD_FORMAT;
+    if (!is_primary_key_pkt(ptag)) {
+        RNP_LOG("wrong key tag: %d at pos %" PRIu64, ptag, src.readb);
+    } else {
+        ret = process_pgp_key(&src, key, skiperrors);
+    }
+    if (skiperrors && (ret == RNP_ERROR_BAD_FORMAT) &&
+        !skip_pgp_packets(&src,
+                          {PGP_PKT_TRUST,
+                           PGP_PKT_SIGNATURE,
+                           PGP_PKT_USER_ID,
+                           PGP_PKT_USER_ATTR,
+                           PGP_PKT_PUBLIC_SUBKEY,
+                           PGP_PKT_SECRET_SUBKEY})) {
+        ret = RNP_ERROR_READ;
+    }
+    /* change error code if we didn't process anything at all */
+    if (srcpos == src.readb) {
+        ret = RNP_ERROR_BAD_STATE;
+    }
+    return ret;
+}
+
+rnp_result_t
 process_pgp_keys(pgp_source_t *src, pgp_key_sequence_t &keys, bool skiperrors)
 {
     bool          armored = false;
     pgp_source_t  armorsrc = {0};
     pgp_source_t *origsrc = src;
     bool          has_secret = false;
     bool          has_public = false;
     rnp_result_t  ret = RNP_ERROR_GENERIC;
 
     keys.keys.clear();
     /* check whether keys are armored */
 armoredpass:
-    if (is_armored_source(src)) {
-        if ((ret = init_armored_src(&armorsrc, src))) {
+    if ((src->type != PGP_STREAM_ARMORED) && is_armored_source(src)) {
+        if (init_armored_src(&armorsrc, src)) {
             RNP_LOG("failed to parse armored data");
+            ret = RNP_ERROR_READ;
             goto finish;
         }
         armored = true;
         src = &armorsrc;
     }
 
     /* read sequence of transferable OpenPGP keys as described in RFC 4880, 11.1 - 11.2 */
     while (!src_eof(src) && !src_error(src)) {
         pgp_transferable_key_t curkey;
-        int                    ptag = stream_pkt_type(src);
-        if (!is_primary_key_pkt(ptag)) {
-            RNP_LOG("wrong key tag: %d at pos %" PRIu64, ptag, src->readb);
-            ret = RNP_ERROR_BAD_FORMAT;
+        ret = process_pgp_key_auto(*src, curkey, false, skiperrors);
+        if (ret && (!skiperrors || (ret != RNP_ERROR_BAD_FORMAT))) {
             goto finish;
         }
-
-        ret = process_pgp_key(src, curkey, skiperrors);
-        if ((ret == RNP_ERROR_BAD_FORMAT) && skiperrors &&
-            skip_pgp_packets(src,
-                             {PGP_PKT_TRUST,
-                              PGP_PKT_SIGNATURE,
-                              PGP_PKT_USER_ID,
-                              PGP_PKT_USER_ATTR,
-                              PGP_PKT_PUBLIC_SUBKEY,
-                              PGP_PKT_SECRET_SUBKEY})) {
+        /* check whether we actually read any key or just skipped erroneous packets */
+        if (curkey.key.tag == PGP_PKT_RESERVED) {
             continue;
         }
-        if (ret) {
-            goto finish;
-        }
+        has_secret |= (curkey.key.tag == PGP_PKT_SECRET_KEY);
+        has_public |= (curkey.key.tag == PGP_PKT_PUBLIC_KEY);
+
         try {
             keys.keys.emplace_back(std::move(curkey));
         } catch (const std::exception &e) {
             RNP_LOG("%s", e.what());
             ret = RNP_ERROR_OUT_OF_MEMORY;
             goto finish;
         }
-
-        has_secret |= (ptag == PGP_PKT_SECRET_KEY);
-        has_public |= (ptag == PGP_PKT_PUBLIC_KEY);
     }
 
     /* file may have multiple armored keys */
     if (armored && !src_eof(origsrc) && is_armored_source(origsrc)) {
         src_close(&armorsrc);
         armored = false;
         src = origsrc;
         goto armoredpass;
@@ -766,20 +777,20 @@ process_pgp_key(pgp_source_t *src, pgp_t
 {
     pgp_source_t armorsrc = {0};
     bool         armored = false;
     int          ptag;
     rnp_result_t ret = RNP_ERROR_GENERIC;
 
     key = pgp_transferable_key_t();
     /* check whether keys are armored */
-    if (is_armored_source(src)) {
-        if ((ret = init_armored_src(&armorsrc, src))) {
+    if ((src->type != PGP_STREAM_ARMORED) && is_armored_source(src)) {
+        if (init_armored_src(&armorsrc, src)) {
             RNP_LOG("failed to parse armored data");
-            return ret;
+            return RNP_ERROR_READ;
         }
         armored = true;
         src = &armorsrc;
     }
 
     /* main key packet */
     uint64_t keypos = src->readb;
     ptag = stream_pkt_type(src);
--- a/third_party/rnp/src/librepgp/stream-key.h
+++ b/third_party/rnp/src/librepgp/stream-key.h
@@ -92,16 +92,27 @@ pgp_signature_t *transferable_subkey_bin
                                           pgp_hash_alg_t                    hash_alg,
                                           const rnp_selfsig_binding_info_t &binding);
 
 pgp_signature_t *transferable_key_revoke(const pgp_key_pkt_t &key,
                                          const pgp_key_pkt_t &signer,
                                          pgp_hash_alg_t       hash_alg,
                                          const pgp_revoke_t & revoke);
 
+/* Process single primary key or subkey, skipping all key-related packets on error.
+   If key.key.tag is zero, then (on success) result is subkey and it is stored in
+   key.subkeys[0].
+   If returns RNP_ERROR_BAD_FORMAT then some packets failed parsing, but still key may contain
+   successfully read key or subkey.
+*/
+rnp_result_t process_pgp_key_auto(pgp_source_t &          src,
+                                  pgp_transferable_key_t &key,
+                                  bool                    allowsub,
+                                  bool                    skiperrors);
+
 rnp_result_t process_pgp_keys(pgp_source_t *src, pgp_key_sequence_t &keys, bool skiperrors);
 
 rnp_result_t process_pgp_key(pgp_source_t *src, pgp_transferable_key_t &key, bool skiperrors);
 
 rnp_result_t process_pgp_subkey(pgp_source_t &             src,
                                 pgp_transferable_subkey_t &subkey,
                                 bool                       skiperrors);
 
--- a/third_party/rnp/src/librepgp/stream-packet.cpp
+++ b/third_party/rnp/src/librepgp/stream-packet.cpp
@@ -401,17 +401,17 @@ add_packet_body_s2k(pgp_packet_body_t *b
         }
         return add_packet_body(body, s2k->salt, PGP_SALT_SIZE) &&
                add_packet_body_byte(body, iter);
     }
     case PGP_S2KS_EXPERIMENTAL: {
         if ((s2k->gpg_ext_num != PGP_S2K_GPG_NO_SECRET) &&
             (s2k->gpg_ext_num != PGP_S2K_GPG_SMARTCARD)) {
             RNP_LOG("Unknown experimental s2k.");
-            return false;
+            return add_packet_body(body, s2k->experimental.data(), s2k->experimental.size());
         }
         if (!add_packet_body(body, "GNU", 3) ||
             !add_packet_body_byte(body, s2k->gpg_ext_num)) {
             return false;
         }
         if (s2k->gpg_ext_num == PGP_S2K_GPG_SMARTCARD) {
             static_assert(sizeof(s2k->gpg_serial) == 16, "invalid gpg serial length");
             size_t slen = s2k->gpg_serial_len > 16 ? 16 : s2k->gpg_serial_len;
@@ -571,75 +571,84 @@ get_packet_body_key_curve(pgp_packet_bod
         return false;
     }
 
     *val = res;
     return true;
 }
 
 static bool
-get_packet_body_s2k(pgp_packet_body_t *body, pgp_s2k_t *s2k)
+get_packet_body_s2k(pgp_packet_body_t &body, pgp_s2k_t &s2k)
 {
     uint8_t spec = 0, halg = 0;
-    if (!get_packet_body_byte(body, &spec) || !get_packet_body_byte(body, &halg)) {
+    if (!get_packet_body_byte(&body, &spec) || !get_packet_body_byte(&body, &halg)) {
         return false;
     }
-    s2k->specifier = (pgp_s2k_specifier_t) spec;
-    s2k->hash_alg = (pgp_hash_alg_t) halg;
+    s2k.specifier = (pgp_s2k_specifier_t) spec;
+    s2k.hash_alg = (pgp_hash_alg_t) halg;
 
-    switch (s2k->specifier) {
+    switch (s2k.specifier) {
     case PGP_S2KS_SIMPLE:
         return true;
     case PGP_S2KS_SALTED:
-        return get_packet_body_buf(body, s2k->salt, PGP_SALT_SIZE);
+        return get_packet_body_buf(&body, s2k.salt, PGP_SALT_SIZE);
     case PGP_S2KS_ITERATED_AND_SALTED: {
         uint8_t iter;
-        if (!get_packet_body_buf(body, s2k->salt, PGP_SALT_SIZE) ||
-            !get_packet_body_byte(body, &iter)) {
+        if (!get_packet_body_buf(&body, s2k.salt, PGP_SALT_SIZE) ||
+            !get_packet_body_byte(&body, &iter)) {
             return false;
         }
-        s2k->iterations = iter;
+        s2k.iterations = iter;
         return true;
     }
     case PGP_S2KS_EXPERIMENTAL: {
+        try {
+            s2k.experimental = {body.data + body.pos, body.data + body.len};
+        } catch (const std::exception &e) {
+            RNP_LOG("%s", e.what());
+            return false;
+        }
         uint8_t gnu[3] = {0};
-        if (!get_packet_body_buf(body, gnu, 3) || memcmp(gnu, "GNU", 3)) {
+        if (!get_packet_body_buf(&body, gnu, 3) || memcmp(gnu, "GNU", 3)) {
             RNP_LOG("Unknown experimental s2k. Skipping.");
-            body->pos = body->len;
-            s2k->gpg_ext_num = PGP_S2K_GPG_NONE;
+            body.pos = body.len;
+            s2k.gpg_ext_num = PGP_S2K_GPG_NONE;
             return true;
         }
         uint8_t ext_num = 0;
-        if (!get_packet_body_byte(body, &ext_num)) {
+        if (!get_packet_body_byte(&body, &ext_num)) {
             return false;
         }
         if ((ext_num != PGP_S2K_GPG_NO_SECRET) && (ext_num != PGP_S2K_GPG_SMARTCARD)) {
-            RNP_LOG("Unsupported gpg extension num: %" PRIu8, ext_num);
-        }
-        s2k->gpg_ext_num = (pgp_s2k_gpg_extension_t) ext_num;
-        if (s2k->gpg_ext_num == PGP_S2K_GPG_NO_SECRET) {
+            RNP_LOG("Unsupported gpg extension num: %" PRIu8 ", skipping", ext_num);
+            body.pos = body.len;
+            s2k.gpg_ext_num = PGP_S2K_GPG_NONE;
             return true;
         }
-        if (!get_packet_body_byte(body, &s2k->gpg_serial_len)) {
+        s2k.gpg_ext_num = (pgp_s2k_gpg_extension_t) ext_num;
+        if (s2k.gpg_ext_num == PGP_S2K_GPG_NO_SECRET) {
+            return true;
+        }
+        if (!get_packet_body_byte(&body, &s2k.gpg_serial_len)) {
             RNP_LOG("Failed to get GPG serial len");
             return false;
         }
-        size_t len = s2k->gpg_serial_len;
-        if (s2k->gpg_serial_len > 16) {
+        size_t len = s2k.gpg_serial_len;
+        if (s2k.gpg_serial_len > 16) {
             RNP_LOG("Warning: gpg_serial_len is %d", (int) len);
             len = 16;
         }
-        if (!get_packet_body_buf(body, s2k->gpg_serial, len)) {
+        if (!get_packet_body_buf(&body, s2k.gpg_serial, len)) {
             RNP_LOG("Failed to get GPG serial");
             return false;
         }
         return true;
     }
     default:
-        RNP_LOG("unknown s2k specifier: %d", (int) s2k->specifier);
+        RNP_LOG("unknown s2k specifier: %d", (int) s2k.specifier);
         return false;
     }
 }
 
 void
 free_packet_body(pgp_packet_body_t *body)
 {
     free(body->data);
@@ -1106,17 +1115,17 @@ stream_parse_sk_sesskey(pgp_source_t *sr
         RNP_LOG("wrong sk ptag: %d", ptag);
         return RNP_ERROR_BAD_FORMAT;
     }
 
     if ((res = stream_read_packet_body(src, &pkt))) {
         return res;
     }
 
-    memset(skey, 0, sizeof(*skey));
+    *skey = {};
     res = RNP_ERROR_BAD_FORMAT;
 
     /* version */
     if (!get_packet_body_byte(&pkt, &bt) || ((bt != PGP_SKSK_V4) && (bt != PGP_SKSK_V5))) {
         RNP_LOG("wrong packet version");
         goto finish;
     }
     skey->version = bt;
@@ -1138,17 +1147,17 @@ stream_parse_sk_sesskey(pgp_source_t *sr
         if ((skey->aalg != PGP_AEAD_EAX) && (skey->aalg != PGP_AEAD_OCB)) {
             RNP_LOG("unsupported AEAD algorithm : %d", (int) skey->aalg);
             res = RNP_ERROR_BAD_PARAMETERS;
             goto finish;
         }
     }
 
     /* s2k */
-    if (!get_packet_body_s2k(&pkt, &skey->s2k)) {
+    if (!get_packet_body_s2k(pkt, skey->s2k)) {
         RNP_LOG("failed to parse s2k");
         goto finish;
     }
 
     if (skey->version == PGP_SKSK_V5) {
         /* v5: iv + esk + tag. For both EAX and OCB ivlen and taglen are 16 octets */
         size_t ivlen = pgp_cipher_aead_nonce_len(skey->aalg);
         size_t taglen = pgp_cipher_aead_tag_len(skey->aalg);
@@ -1211,17 +1220,17 @@ stream_parse_pk_sesskey(pgp_source_t *sr
         RNP_LOG("wrong pk ptag: %d", ptag);
         return RNP_ERROR_BAD_FORMAT;
     }
 
     if ((res = stream_read_packet_body(src, &pkt))) {
         return res;
     }
 
-    memset(pkey, 0, sizeof(*pkey));
+    *pkey = {};
     res = RNP_ERROR_BAD_FORMAT;
 
     /* version */
     if (!get_packet_body_byte(&pkt, &bt) || (bt != PGP_PKSK_V3)) {
         RNP_LOG("wrong packet version");
         goto finish;
     }
     pkey->version = bt;
@@ -1366,17 +1375,17 @@ signature_read_v3(pgp_packet_body_t *pkt
     if ((sig->hashed_data = (uint8_t *) malloc(5)) == NULL) {
         RNP_LOG("allocation failed");
         return RNP_ERROR_OUT_OF_MEMORY;
     }
     memcpy(sig->hashed_data, &buf[1], 5);
     sig->hashed_len = 5;
 
     /* signature type */
-    sig->type = (pgp_sig_type_t) buf[1];
+    sig->set_type((pgp_sig_type_t) buf[1]);
 
     /* creation time */
     sig->creation_time = read_uint32(&buf[2]);
 
     /* signer's key id */
     static_assert(std::tuple_size<decltype(sig->signer)>::value == PGP_KEY_ID_SIZE,
                   "v3 signer field size mismatch");
     memcpy(sig->signer.data(), &buf[6], PGP_KEY_ID_SIZE);
@@ -1498,19 +1507,17 @@ signature_parse_subpacket(pgp_sig_subpkt
         if ((oklen = subpkt.len >= 1)) {
             subpkt.fields.revocation_reason.code = (pgp_revocation_type_t) subpkt.data[0];
             subpkt.fields.revocation_reason.str = (const char *) &subpkt.data[1];
             subpkt.fields.revocation_reason.len = subpkt.len - 1;
         }
         break;
     case PGP_SIG_SUBPKT_FEATURES:
         if ((oklen = subpkt.len >= 1)) {
-            subpkt.fields.features.mdc = subpkt.data[0] & 0x01;
-            subpkt.fields.features.aead = subpkt.data[0] & 0x02;
-            subpkt.fields.features.key_v5 = subpkt.data[0] & 0x04;
+            subpkt.fields.features = subpkt.data[0];
         }
         break;
     case PGP_SIG_SUBPKT_SIGNATURE_TARGET:
         if ((oklen = subpkt.len >= 18)) {
             subpkt.fields.sig_target.pkalg = (pgp_pubkey_alg_t) subpkt.data[0];
             subpkt.fields.sig_target.halg = (pgp_hash_alg_t) subpkt.data[1];
             subpkt.fields.sig_target.hash = &subpkt.data[2];
             subpkt.fields.sig_target.hlen = subpkt.len - 2;
@@ -1661,17 +1668,17 @@ signature_read_v4(pgp_packet_body_t *pkt
     rnp_result_t res = RNP_ERROR_BAD_FORMAT;
 
     if (!get_packet_body_buf(pkt, buf, 5)) {
         RNP_LOG("cannot get first 5 bytes");
         return RNP_ERROR_BAD_FORMAT;
     }
 
     /* signature type */
-    sig->type = (pgp_sig_type_t) buf[0];
+    sig->set_type((pgp_sig_type_t) buf[0]);
 
     /* public key algorithm */
     sig->palg = (pgp_pubkey_alg_t) buf[1];
 
     /* hash algorithm */
     sig->halg = (pgp_hash_alg_t) buf[2];
 
     /* hashed subpackets length */
@@ -2094,17 +2101,17 @@ stream_parse_key(pgp_source_t *src, pgp_
     }
 
     /* Read the packet into memory */
     if ((res = stream_read_packet_body(src, &pkt))) {
         return res;
     }
 
     res = RNP_ERROR_BAD_FORMAT;
-    memset(key, 0, sizeof(*key));
+    *key = {};
 
     /* key type, i.e. tag */
     key->tag = pkt.tag;
 
     /* version */
     if (!get_packet_body_byte(&pkt, &ver) || (ver < PGP_V2) || (ver > PGP_V4)) {
         RNP_LOG("wrong key packet version");
         goto finish;
@@ -2216,17 +2223,17 @@ stream_parse_key(pgp_source_t *src, pgp_
         switch (key->sec_protection.s2k.usage) {
         case PGP_S2KU_NONE:
             break;
         case PGP_S2KU_ENCRYPTED:
         case PGP_S2KU_ENCRYPTED_AND_HASHED: {
             /* we have s2k */
             uint8_t salg = 0;
             if (!get_packet_body_byte(&pkt, &salg) ||
-                !get_packet_body_s2k(&pkt, &key->sec_protection.s2k)) {
+                !get_packet_body_s2k(pkt, key->sec_protection.s2k)) {
                 RNP_LOG("failed to read key protection");
                 goto finish;
             }
             key->sec_protection.symm_alg = (pgp_symm_alg_t) salg;
             break;
         }
         default:
             /* old-style: usage is symmetric algorithm identifier */
--- a/third_party/rnp/src/librepgp/stream-parse.cpp
+++ b/third_party/rnp/src/librepgp/stream-parse.cpp
@@ -751,17 +751,17 @@ add_hash_for_sig(pgp_source_signed_param
     }
     return pgp_hash_list_add(param->hashes, halg);
 }
 
 static const pgp_hash_t *
 get_hash_for_sig(pgp_source_signed_param_t *param, pgp_signature_info_t *sinfo)
 {
     /* Cleartext always uses param->hashes instead of param->txt_hashes */
-    if (!param->cleartext && (sinfo->sig->type == PGP_SIG_TEXT)) {
+    if (!param->cleartext && (sinfo->sig->type() == PGP_SIG_TEXT)) {
         return pgp_hash_list_get(param->txt_hashes, sinfo->sig->halg);
     }
     return pgp_hash_list_get(param->hashes, sinfo->sig->halg);
 }
 
 static void
 signed_validate_signature(pgp_source_signed_param_t *param, pgp_signature_info_t *sinfo)
 {
@@ -938,17 +938,17 @@ signed_read_signatures(pgp_source_t *src
     pgp_source_signed_param_t *param = (pgp_source_signed_param_t *) src->param;
 
     /* reading signatures */
     for (auto op = param->onepasses.rbegin(); op != param->onepasses.rend(); op++) {
         if ((ret = signed_read_single_signature(param, src, &sig)) != RNP_SUCCESS) {
             return ret;
         }
 
-        if (!signature_matches_onepass(sig, &*op)) {
+        if (!sig || !sig->matches_onepass(*op)) {
             RNP_LOG("signature doesn't match one-pass");
             return RNP_ERROR_BAD_FORMAT;
         }
     }
 
     return RNP_SUCCESS;
 }
 
@@ -982,21 +982,22 @@ signed_src_finish(pgp_source_t *src)
         if (!sinfo.sig) {
             continue;
         }
 
         /* we need public key, however may fallback to secret later on */
         keyctx.secret = false;
 
         /* Get the key id */
-        if (!signature_get_keyid(sinfo.sig, keyctx.search.by.keyid)) {
+        if (!sinfo.sig->has_keyid()) {
             RNP_LOG("cannot get signer's key id from signature");
             sinfo.unknown = true;
             continue;
         }
+        keyctx.search.by.keyid = sinfo.sig->keyid();
 
         /* Get the public key */
         if (!(key = pgp_request_key(param->handler->key_provider, &keyctx))) {
             // fallback to secret key
             keyctx.secret = true;
             if (!(key = pgp_request_key(param->handler->key_provider, &keyctx))) {
                 RNP_LOG("signer's key not found");
                 sinfo.no_signer = true;
@@ -1818,18 +1819,18 @@ get_aead_src_hdr(pgp_source_t *src, pgp_
 static rnp_result_t
 encrypted_read_packet_data(pgp_source_encrypted_param_t *param)
 {
     rnp_result_t     errcode = RNP_ERROR_GENERIC;
     uint8_t          ptag;
     uint8_t          mdcver;
     uint8_t          hdr[4];
     int              ptype;
-    pgp_sk_sesskey_t skey = {0};
-    pgp_pk_sesskey_t pkey = {0};
+    pgp_sk_sesskey_t skey;
+    pgp_pk_sesskey_t pkey;
 
     /* Reading pk/sk encrypted session key(s) */
     while (true) {
         if (!src_peek_eq(param->pkt.readsrc, &ptag, 1)) {
             RNP_LOG("failed to read packet header");
             return RNP_ERROR_READ;
         }
         ptype = get_packet_type(ptag);
@@ -2193,19 +2194,19 @@ init_signed_src(pgp_parse_handler_t *han
             if (onepass.nested) {
                 /* despite the name non-zero value means that it is the last one-pass */
                 break;
             }
         } else if (ptype == PGP_PKT_SIGNATURE) {
             /* no need to check the error here - we already know tag */
             signed_read_single_signature(param, readsrc, &sig);
             /* adding hash context */
-            if (sig && !add_hash_for_sig(param, sig->type, sig->halg)) {
+            if (sig && !add_hash_for_sig(param, sig->type(), sig->halg)) {
                 RNP_LOG(
-                  "Failed to create hash %d for sig %d.", (int) sig->halg, (int) sig->type);
+                  "Failed to create hash %d for sig %d.", (int) sig->halg, (int) sig->type());
                 errcode = RNP_ERROR_BAD_PARAMETERS;
                 goto finish;
             }
         } else {
             break;
         }
 
         /* check if we are not it endless loop */
--- a/third_party/rnp/src/librepgp/stream-sig.cpp
+++ b/third_party/rnp/src/librepgp/stream-sig.cpp
@@ -29,645 +29,28 @@
 #include <stdio.h>
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #else
 #include "uniwin.h"
 #endif
 #include <string.h>
 #include <type_traits>
+#include <stdexcept>
 #include <rnp/rnp_def.h>
 #include "types.h"
 #include "stream-sig.h"
 #include "stream-packet.h"
 #include "stream-armor.h"
 #include "pgp-key.h"
 #include "crypto/signatures.h"
 
 #include <time.h>
 
 bool
-signature_matches_onepass(pgp_signature_t *sig, pgp_one_pass_sig_t *onepass)
-{
-    if (!sig || !onepass) {
-        return false;
-    }
-
-    pgp_key_id_t keyid = {};
-    if (!signature_get_keyid(sig, keyid)) {
-        return false;
-    }
-
-    return (keyid == onepass->keyid) && (sig->halg == onepass->halg) &&
-           (sig->palg == onepass->palg) && (sig->type == onepass->type);
-}
-
-pgp_sig_subpkt_t *
-signature_get_subpkt(pgp_signature_t *sig, pgp_sig_subpacket_type_t type)
-{
-    if (!sig || (sig->version < PGP_V4)) {
-        return NULL;
-    }
-    for (auto &subpkt : sig->subpkts) {
-        if (subpkt.type == type) {
-            return &subpkt;
-        }
-    }
-    return NULL;
-}
-
-const pgp_sig_subpkt_t *
-signature_get_subpkt(const pgp_signature_t *sig, pgp_sig_subpacket_type_t type)
-{
-    if (!sig || (sig->version < PGP_V4)) {
-        return NULL;
-    }
-    for (auto &subpkt : sig->subpkts) {
-        if (subpkt.type == type) {
-            return &subpkt;
-        }
-    }
-    return NULL;
-}
-
-pgp_sig_subpkt_t *
-signature_add_subpkt(pgp_signature_t *        sig,
-                     pgp_sig_subpacket_type_t type,
-                     size_t                   datalen,
-                     bool                     reuse)
-{
-    pgp_sig_subpkt_t *subpkt = NULL;
-    if (!sig) {
-        return NULL;
-    }
-    if (sig->version < PGP_V4) {
-        RNP_LOG("wrong signature version");
-        return NULL;
-    }
-
-    uint8_t *newdata = (uint8_t *) calloc(1, datalen);
-    if (!newdata) {
-        RNP_LOG("Allocation failed");
-        return NULL;
-    }
-
-    if (reuse && (subpkt = signature_get_subpkt(sig, type))) {
-        *subpkt = {};
-    } else {
-        try {
-            sig->subpkts.push_back({});
-            subpkt = &sig->subpkts.back();
-        } catch (const std::exception &e) {
-            RNP_LOG("%s", e.what());
-            free(newdata);
-            return NULL;
-        }
-    }
-
-    subpkt->data = newdata;
-    subpkt->type = type;
-    subpkt->len = datalen;
-    return subpkt;
-}
-
-void
-signature_remove_subpkt(pgp_signature_t *sig, pgp_sig_subpkt_t *subpkt)
-{
-    for (auto it = sig->subpkts.begin(); it < sig->subpkts.end(); it++) {
-        if (&*it == subpkt) {
-            sig->subpkts.erase(it);
-            return;
-        }
-    }
-}
-
-pgp_sig_type_t
-signature_get_type(const pgp_signature_t *sig)
-{
-    return sig->type;
-}
-
-bool
-signature_has_keyfp(const pgp_signature_t *sig)
-{
-    return signature_get_subpkt(sig, PGP_SIG_SUBPKT_ISSUER_FPR);
-}
-
-bool
-signature_get_keyfp(const pgp_signature_t *sig, pgp_fingerprint_t &fp)
-{
-    if (!sig || (sig->version < PGP_V4)) {
-        return false;
-    }
-
-    const pgp_sig_subpkt_t *subpkt = signature_get_subpkt(sig, PGP_SIG_SUBPKT_ISSUER_FPR);
-    if (!subpkt) {
-        return false;
-    }
-    fp.length = subpkt->fields.issuer_fp.len;
-    if (subpkt->fields.issuer_fp.len <= sizeof(fp.fingerprint)) {
-        memcpy(fp.fingerprint, subpkt->fields.issuer_fp.fp, subpkt->fields.issuer_fp.len);
-        return true;
-    }
-    return false;
-}
-
-bool
-signature_set_keyfp(pgp_signature_t *sig, const pgp_fingerprint_t &fp)
-{
-    pgp_sig_subpkt_t *subpkt = NULL;
-
-    if (!sig) {
-        return false;
-    }
-
-    subpkt = signature_add_subpkt(sig, PGP_SIG_SUBPKT_ISSUER_FPR, 1 + fp.length, true);
-    if (!subpkt) {
-        return false;
-    }
-
-    subpkt->parsed = 1;
-    subpkt->hashed = 1;
-    subpkt->data[0] = 4;
-    memcpy(subpkt->data + 1, fp.fingerprint, fp.length);
-    subpkt->fields.issuer_fp.len = fp.length;
-    subpkt->fields.issuer_fp.version = subpkt->data[0];
-    subpkt->fields.issuer_fp.fp = subpkt->data + 1;
-    return true;
-}
-
-bool
-signature_has_keyid(const pgp_signature_t *sig)
-{
-    if (!sig) {
-        return false;
-    }
-
-    return (sig->version < PGP_V4) ||
-           signature_get_subpkt(sig, PGP_SIG_SUBPKT_ISSUER_KEY_ID) ||
-           signature_get_subpkt(sig, PGP_SIG_SUBPKT_ISSUER_FPR);
-}
-
-bool
-signature_get_keyid(const pgp_signature_t *sig, pgp_key_id_t &id)
-{
-    if (!sig) {
-        return false;
-    }
-
-    /* version 3 uses signature field */
-    if (sig->version < PGP_V4) {
-        id = sig->signer;
-        return true;
-    }
-
-    /* version 4 and up use subpackets */
-    const pgp_sig_subpkt_t *subpkt;
-    static_assert(std::tuple_size<std::remove_reference<decltype(id)>::type>::value ==
-                    PGP_KEY_ID_SIZE,
-                  "pgp_key_id_t size mismatch");
-    if ((subpkt = signature_get_subpkt(sig, PGP_SIG_SUBPKT_ISSUER_KEY_ID))) {
-        memcpy(id.data(), subpkt->fields.issuer, PGP_KEY_ID_SIZE);
-        return true;
-    }
-    if ((subpkt = signature_get_subpkt(sig, PGP_SIG_SUBPKT_ISSUER_FPR))) {
-        memcpy(id.data(),
-               subpkt->fields.issuer_fp.fp + subpkt->fields.issuer_fp.len - PGP_KEY_ID_SIZE,
-               PGP_KEY_ID_SIZE);
-        return true;
-    }
-    return false;
-}
-
-bool
-signature_set_keyid(pgp_signature_t *sig, const pgp_key_id_t &id)
-{
-    if (!sig) {
-        return false;
-    }
-
-    if (sig->version < PGP_V4) {
-        sig->signer = id;
-        return true;
-    }
-
-    static_assert(std::tuple_size<std::remove_reference<decltype(id)>::type>::value ==
-                    PGP_KEY_ID_SIZE,
-                  "pgp_key_id_t size mismatch");
-    pgp_sig_subpkt_t *subpkt =
-      signature_add_subpkt(sig, PGP_SIG_SUBPKT_ISSUER_KEY_ID, PGP_KEY_ID_SIZE, true);
-    if (!subpkt) {
-        return false;
-    }
-
-    subpkt->parsed = 1;
-    subpkt->hashed = 0;
-    memcpy(subpkt->data, id.data(), PGP_KEY_ID_SIZE);
-    subpkt->fields.issuer = subpkt->data;
-    return true;
-}
-
-uint32_t
-signature_get_creation(const pgp_signature_t *sig)
-{
-    if (!sig) {
-        return 0;
-    }
-    if (sig->version < PGP_V4) {
-        return sig->creation_time;
-    }
-    const pgp_sig_subpkt_t *subpkt;
-    if ((subpkt = signature_get_subpkt(sig, PGP_SIG_SUBPKT_CREATION_TIME))) {
-        return subpkt->fields.create;
-    }
-
-    return 0;
-}
-
-bool
-signature_set_creation(pgp_signature_t *sig, uint32_t ctime)
-{
-    pgp_sig_subpkt_t *subpkt;
-
-    if (!sig) {
-        return false;
-    }
-    if (sig->version < PGP_V4) {
-        sig->creation_time = ctime;
-        return true;
-    }
-
-    subpkt = signature_add_subpkt(sig, PGP_SIG_SUBPKT_CREATION_TIME, 4, true);
-    if (!subpkt) {
-        return false;
-    }
-
-    subpkt->parsed = 1;
-    subpkt->hashed = 1;
-    STORE32BE(subpkt->data, ctime);
-    subpkt->fields.create = ctime;
-    return true;
-}
-
-uint32_t
-signature_get_expiration(const pgp_signature_t *sig)
-{
-    const pgp_sig_subpkt_t *subpkt = signature_get_subpkt(sig, PGP_SIG_SUBPKT_EXPIRATION_TIME);
-    return subpkt ? subpkt->fields.expiry : 0;
-}
-
-bool
-signature_set_expiration(pgp_signature_t *sig, uint32_t etime)
-{
-    pgp_sig_subpkt_t *subpkt;
-
-    if (!sig || (sig->version < PGP_V4)) {
-        return false;
-    }
-
-    subpkt = signature_add_subpkt(sig, PGP_SIG_SUBPKT_EXPIRATION_TIME, 4, true);
-    if (!subpkt) {
-        return false;
-    }
-
-    subpkt->parsed = 1;
-    subpkt->hashed = 1;
-    STORE32BE(subpkt->data, etime);
-    subpkt->fields.expiry = etime;
-    return true;
-}
-
-bool
-signature_has_key_expiration(const pgp_signature_t *sig)
-{
-    return signature_get_subpkt(sig, PGP_SIG_SUBPKT_KEY_EXPIRY);
-}
-
-uint32_t
-signature_get_key_expiration(const pgp_signature_t *sig)
-{
-    const pgp_sig_subpkt_t *subpkt = signature_get_subpkt(sig, PGP_SIG_SUBPKT_KEY_EXPIRY);
-    return subpkt ? subpkt->fields.expiry : 0;
-}
-
-bool
-signature_set_key_expiration(pgp_signature_t *sig, uint32_t etime)
-{
-    pgp_sig_subpkt_t *subpkt;
-
-    subpkt = signature_add_subpkt(sig, PGP_SIG_SUBPKT_KEY_EXPIRY, 4, true);
-    if (!subpkt) {
-        return false;
-    }
-
-    subpkt->parsed = 1;
-    subpkt->hashed = 1;
-    STORE32BE(subpkt->data, etime);
-    subpkt->fields.expiry = etime;
-    return true;
-}
-
-bool
-signature_has_key_flags(const pgp_signature_t *sig)
-{
-    return signature_get_subpkt(sig, PGP_SIG_SUBPKT_KEY_FLAGS);
-}
-
-uint8_t
-signature_get_key_flags(const pgp_signature_t *sig)
-{
-    const pgp_sig_subpkt_t *subpkt = signature_get_subpkt(sig, PGP_SIG_SUBPKT_KEY_FLAGS);
-    return subpkt ? subpkt->fields.key_flags : 0;
-}
-
-bool
-signature_set_key_flags(pgp_signature_t *sig, uint8_t flags)
-{
-    pgp_sig_subpkt_t *subpkt;
-
-    subpkt = signature_add_subpkt(sig, PGP_SIG_SUBPKT_KEY_FLAGS, 1, true);
-    if (!subpkt) {
-        return false;
-    }
-
-    subpkt->parsed = 1;
-    subpkt->hashed = 1;
-    subpkt->data[0] = flags;
-    subpkt->fields.key_flags = flags;
-    return true;
-}
-
-bool
-signature_get_primary_uid(pgp_signature_t *sig)
-{
-    pgp_sig_subpkt_t *subpkt;
-
-    if ((subpkt = signature_get_subpkt(sig, PGP_SIG_SUBPKT_PRIMARY_USER_ID))) {
-        return subpkt->fields.primary_uid;
-    }
-
-    return false;
-}
-
-bool
-signature_set_primary_uid(pgp_signature_t *sig, bool primary)
-{
-    pgp_sig_subpkt_t *subpkt;
-
-    subpkt = signature_add_subpkt(sig, PGP_SIG_SUBPKT_PRIMARY_USER_ID, 1, true);
-    if (!subpkt) {
-        return false;
-    }
-
-    subpkt->parsed = 1;
-    subpkt->hashed = 1;
-    subpkt->data[0] = primary;
-    subpkt->fields.primary_uid = primary;
-    return true;
-}
-
-static bool
-signature_set_preferred_algs(pgp_signature_t *        sig,
-                             uint8_t                  algs[],
-                             size_t                   len,
-                             pgp_sig_subpacket_type_t type)
-{
-    pgp_sig_subpkt_t *subpkt;
-
-    subpkt = signature_add_subpkt(sig, type, len, true);
-    if (!subpkt) {
-        return false;
-    }
-
-    subpkt->parsed = 1;
-    subpkt->hashed = 1;
-    memcpy(subpkt->data, algs, len);
-    subpkt->fields.preferred.arr = subpkt->data;
-    subpkt->fields.preferred.len = len;
-    return true;
-}
-
-static bool
-signature_get_preferred_algs(const pgp_signature_t *  sig,
-                             uint8_t **               algs,
-                             size_t *                 len,
-                             pgp_sig_subpacket_type_t type)
-{
-    if (!algs || !len) {
-        return false;
-    }
-
-    const pgp_sig_subpkt_t *subpkt = signature_get_subpkt(sig, type);
-    if (subpkt) {
-        *algs = subpkt->fields.preferred.arr;
-        *len = subpkt->fields.preferred.len;
-        return true;
-    }
-    return false;
-}
-
-bool
-signature_has_preferred_symm_algs(const pgp_signature_t *sig)
-{
-    return signature_get_subpkt(sig, PGP_SIG_SUBPKT_PREFERRED_SKA);
-}
-
-bool
-signature_get_preferred_symm_algs(const pgp_signature_t *sig, uint8_t **algs, size_t *count)
-{
-    return signature_get_preferred_algs(sig, algs, count, PGP_SIG_SUBPKT_PREFERRED_SKA);
-}
-
-bool
-signature_set_preferred_symm_algs(pgp_signature_t *sig, uint8_t algs[], size_t len)
-{
-    return signature_set_preferred_algs(sig, algs, len, PGP_SIG_SUBPKT_PREFERRED_SKA);
-}
-
-bool
-signature_has_preferred_hash_algs(const pgp_signature_t *sig)
-{
-    return signature_get_subpkt(sig, PGP_SIG_SUBPKT_PREFERRED_HASH);
-}
-
-bool
-signature_get_preferred_hash_algs(const pgp_signature_t *sig, uint8_t **algs, size_t *count)
-{
-    return signature_get_preferred_algs(sig, algs, count, PGP_SIG_SUBPKT_PREFERRED_HASH);
-}
-
-bool
-signature_set_preferred_hash_algs(pgp_signature_t *sig, uint8_t algs[], size_t len)
-{
-    return signature_set_preferred_algs(sig, algs, len, PGP_SIG_SUBPKT_PREFERRED_HASH);
-}
-
-bool
-signature_has_preferred_z_algs(const pgp_signature_t *sig)
-{
-    return signature_get_subpkt(sig, PGP_SIG_SUBPKT_PREF_COMPRESS);
-}
-
-bool
-signature_get_preferred_z_algs(const pgp_signature_t *sig, uint8_t **algs, size_t *count)
-{
-    return signature_get_preferred_algs(sig, algs, count, PGP_SIG_SUBPKT_PREF_COMPRESS);
-}
-
-bool
-signature_set_preferred_z_algs(pgp_signature_t *sig, uint8_t algs[], size_t len)
-{
-    return signature_set_preferred_algs(sig, algs, len, PGP_SIG_SUBPKT_PREF_COMPRESS);
-}
-
-bool
-signature_has_key_server_prefs(const pgp_signature_t *sig)
-{
-    return signature_get_subpkt(sig, PGP_SIG_SUBPKT_KEYSERV_PREFS);
-}
-
-uint8_t
-signature_get_key_server_prefs(const pgp_signature_t *sig)
-{
-    const pgp_sig_subpkt_t *subpkt = signature_get_subpkt(sig, PGP_SIG_SUBPKT_KEYSERV_PREFS);
-    return subpkt ? subpkt->data[0] : 0;
-}
-
-bool
-signature_set_key_server_prefs(pgp_signature_t *sig, uint8_t prefs)
-{
-    pgp_sig_subpkt_t *subpkt;
-
-    subpkt = signature_add_subpkt(sig, PGP_SIG_SUBPKT_KEYSERV_PREFS, 1, true);
-    if (!subpkt) {
-        return false;
-    }
-
-    subpkt->parsed = 1;
-    subpkt->hashed = 1;
-    subpkt->data[0] = prefs;
-    subpkt->fields.ks_prefs.no_modify = prefs & 0x80;
-    return true;
-}
-
-bool
-signature_set_preferred_key_server(pgp_signature_t *sig, const char *uri)
-{
-    pgp_sig_subpkt_t *subpkt;
-    size_t            len = strlen(uri);
-
-    subpkt = signature_add_subpkt(sig, PGP_SIG_SUBPKT_PREF_KEYSERV, len, true);
-    if (!subpkt) {
-        return false;
-    }
-
-    subpkt->parsed = 1;
-    subpkt->hashed = 1;
-    memcpy(subpkt->data, uri, len);
-    subpkt->fields.preferred_ks.uri = (char *) subpkt->data;
-    subpkt->fields.preferred_ks.len = len;
-    return true;
-}
-
-bool
-signature_has_trust(const pgp_signature_t *sig)
-{
-    return signature_get_subpkt(sig, PGP_SIG_SUBPKT_TRUST);
-}
-
-bool
-signature_get_trust(const pgp_signature_t *sig, uint8_t *level, uint8_t *amount)
-{
-    const pgp_sig_subpkt_t *subpkt = signature_get_subpkt(sig, PGP_SIG_SUBPKT_TRUST);
-    if (subpkt) {
-        if (level) {
-            *level = subpkt->fields.trust.level;
-        }
-        if (amount) {
-            *amount = subpkt->fields.trust.amount;
-        }
-        return true;
-    }
-    return false;
-}
-
-bool
-signature_set_trust(pgp_signature_t *sig, uint8_t level, uint8_t amount)
-{
-    pgp_sig_subpkt_t *subpkt;
-
-    subpkt = signature_add_subpkt(sig, PGP_SIG_SUBPKT_TRUST, 2, true);
-    if (!subpkt) {
-        return false;
-    }
-
-    subpkt->parsed = 1;
-    subpkt->hashed = 1;
-    subpkt->data[0] = level;
-    subpkt->data[1] = amount;
-    subpkt->fields.trust.level = level;
-    subpkt->fields.trust.amount = amount;
-    return true;
-}
-
-bool
-signature_get_revocable(const pgp_signature_t *sig)
-{
-    const pgp_sig_subpkt_t *subpkt = signature_get_subpkt(sig, PGP_SIG_SUBPKT_REVOCABLE);
-    return subpkt ? subpkt->fields.revocable : true;
-}
-
-bool
-signature_set_revocable(pgp_signature_t *sig, bool revocable)
-{
-    pgp_sig_subpkt_t *subpkt;
-
-    subpkt = signature_add_subpkt(sig, PGP_SIG_SUBPKT_REVOCABLE, 1, true);
-    if (!subpkt) {
-        return false;
-    }
-
-    subpkt->parsed = 1;
-    subpkt->hashed = 1;
-    subpkt->data[0] = revocable;
-    subpkt->fields.revocable = revocable;
-    return true;
-}
-
-bool
-signature_set_features(pgp_signature_t *sig, uint8_t features)
-{
-    pgp_sig_subpkt_t *subpkt;
-
-    subpkt = signature_add_subpkt(sig, PGP_SIG_SUBPKT_FEATURES, 1, true);
-    if (!subpkt) {
-        return false;
-    }
-
-    subpkt->hashed = 1;
-    subpkt->data[0] = features;
-    return signature_parse_subpacket(*subpkt);
-}
-
-bool
-signature_set_signer_uid(pgp_signature_t *sig, uint8_t *uid, size_t len)
-{
-    pgp_sig_subpkt_t *subpkt;
-
-    subpkt = signature_add_subpkt(sig, PGP_SIG_SUBPKT_SIGNERS_USER_ID, len, true);
-    if (!subpkt) {
-        return false;
-    }
-
-    subpkt->hashed = 1;
-    memcpy(subpkt->data, uid, len);
-    return signature_parse_subpacket(*subpkt);
-}
-
-bool
 signature_set_embedded_sig(pgp_signature_t *sig, pgp_signature_t *esig)
 {
     pgp_sig_subpkt_t *subpkt = NULL;
     pgp_dest_t        memdst = {};
     pgp_source_t      memsrc = {};
     size_t            len = 0;
     bool              res = false;
 
@@ -683,149 +66,69 @@ signature_set_embedded_sig(pgp_signature
         RNP_LOG("failed to init mem src");
         goto finish;
     }
     if (!stream_read_pkt_len(&memsrc, &len)) {
         RNP_LOG("wrong pkt len");
         goto finish;
     }
 
-    subpkt = signature_add_subpkt(sig, PGP_SIG_SUBPKT_EMBEDDED_SIGNATURE, len, true);
-    if (!subpkt) {
-        RNP_LOG("failed to add subpkt");
-        goto finish;
-    }
-
-    subpkt->hashed = 0;
-    if (!src_read_eq(&memsrc, subpkt->data, len)) {
-        RNP_LOG("failed to read back signature");
-        goto finish;
-    }
     try {
+        subpkt = &sig->add_subpkt(PGP_SIG_SUBPKT_EMBEDDED_SIGNATURE, len, true);
+        subpkt->hashed = false;
+        if (!src_read_eq(&memsrc, subpkt->data, len)) {
+            RNP_LOG("failed to read back signature");
+            goto finish;
+        }
         subpkt->fields.sig = new pgp_signature_t(*esig);
+        subpkt->parsed = true;
+        res = true;
     } catch (const std::exception &e) {
         RNP_LOG("%s", e.what());
-        goto finish;
     }
-    subpkt->parsed = 1;
-    res = true;
 finish:
     if (!res && subpkt) {
-        signature_remove_subpkt(sig, subpkt);
+        sig->remove_subpkt(subpkt);
     }
     src_close(&memsrc);
     dst_close(&memdst, true);
     return res;
 }
 
 bool
 signature_add_notation_data(pgp_signature_t *sig,
                             bool             readable,
                             const char *     name,
                             const char *     value)
 {
-    pgp_sig_subpkt_t *subpkt;
-    size_t            nlen, vlen;
-
-    nlen = strlen(name);
-    vlen = strlen(value);
+    size_t nlen = strlen(name);
+    size_t vlen = strlen(value);
 
     if ((nlen > 0xffff) || (vlen > 0xffff)) {
         RNP_LOG("wrong length");
         return false;
     }
 
-    subpkt = signature_add_subpkt(sig, PGP_SIG_SUBPKT_NOTATION_DATA, 8 + nlen + vlen, false);
-    if (!subpkt) {
+    try {
+        pgp_sig_subpkt_t &subpkt =
+          sig->add_subpkt(PGP_SIG_SUBPKT_NOTATION_DATA, 8 + nlen + vlen, false);
+        subpkt.hashed = true;
+        if (readable) {
+            subpkt.data[0] = 0x80;
+            subpkt.fields.notation.flags[0] = 0x80;
+        }
+        write_uint16(subpkt.data + 4, nlen);
+        memcpy(subpkt.data + 6, name, nlen);
+        write_uint16(subpkt.data + 6 + nlen, vlen);
+        memcpy(subpkt.data + 8 + nlen, value, vlen);
+        return signature_parse_subpacket(subpkt);
+    } catch (const std::exception &e) {
+        RNP_LOG("%s", e.what());
         return false;
     }
-
-    subpkt->hashed = 1;
-    if (readable) {
-        subpkt->data[0] = 0x80;
-        subpkt->fields.notation.flags[0] = 0x80;
-    }
-    write_uint16(subpkt->data + 4, nlen);
-    memcpy(subpkt->data + 6, name, nlen);
-    write_uint16(subpkt->data + 6 + nlen, vlen);
-    memcpy(subpkt->data + 8 + nlen, value, vlen);
-    return signature_parse_subpacket(*subpkt);
-}
-
-bool
-signature_has_key_server(const pgp_signature_t *sig)
-{
-    return signature_get_subpkt(sig, PGP_SIG_SUBPKT_PREF_KEYSERV);
-}
-
-char *
-signature_get_key_server(const pgp_signature_t *sig)
-{
-    const pgp_sig_subpkt_t *subpkt = signature_get_subpkt(sig, PGP_SIG_SUBPKT_PREF_KEYSERV);
-    if (subpkt) {
-        char *res = (char *) malloc(subpkt->len + 1);
-        if (res) {
-            memcpy(res, subpkt->data, subpkt->len);
-            res[subpkt->len] = '\0';
-        }
-        return res;
-    }
-    return NULL;
-}
-
-bool
-signature_has_revocation_reason(const pgp_signature_t *sig)
-{
-    return signature_get_subpkt(sig, PGP_SIG_SUBPKT_REVOCATION_REASON);
-}
-
-bool
-signature_get_revocation_reason(const pgp_signature_t *sig,
-                                pgp_revocation_type_t *code,
-                                char **                reason)
-{
-    const pgp_sig_subpkt_t *subpkt =
-      signature_get_subpkt(sig, PGP_SIG_SUBPKT_REVOCATION_REASON);
-    if (subpkt) {
-        if (code) {
-            *code = subpkt->fields.revocation_reason.code;
-        }
-        if (reason) {
-            size_t len = subpkt->fields.revocation_reason.len;
-            *reason = (char *) malloc(len + 1);
-            if (!*reason) {
-                RNP_LOG("alloc failed");
-                return false;
-            }
-            memcpy(*reason, subpkt->fields.revocation_reason.str, len);
-            (*reason)[len] = '\0';
-        }
-        return true;
-    }
-    return false;
-}
-
-bool
-signature_set_revocation_reason(pgp_signature_t *     sig,
-                                pgp_revocation_type_t code,
-                                const char *          reason)
-{
-    size_t            datalen = 1 + (reason ? strlen(reason) : 0);
-    pgp_sig_subpkt_t *subpkt =
-      signature_add_subpkt(sig, PGP_SIG_SUBPKT_REVOCATION_REASON, datalen, true);
-    if (!subpkt) {
-        return false;
-    }
-
-    subpkt->hashed = 1;
-    subpkt->data[0] = code;
-    if (reason) {
-        memcpy(subpkt->data + 1, reason, strlen(reason));
-    }
-    return signature_parse_subpacket(*subpkt);
 }
 
 bool
 signature_fill_hashed_data(pgp_signature_t *sig)
 {
     pgp_packet_body_t hbody;
     bool              res;
 
@@ -840,21 +143,21 @@ signature_fill_hashed_data(pgp_signature
     }
 
     if (!init_packet_body(&hbody, PGP_PKT_RESERVED)) {
         RNP_LOG("allocation failed");
         return false;
     }
 
     if (sig->version < PGP_V4) {
-        res = add_packet_body_byte(&hbody, sig->type) &&
+        res = add_packet_body_byte(&hbody, sig->type()) &&
               add_packet_body_uint32(&hbody, sig->creation_time);
     } else {
         res = add_packet_body_byte(&hbody, sig->version) &&
-              add_packet_body_byte(&hbody, sig->type) &&
+              add_packet_body_byte(&hbody, sig->type()) &&
               add_packet_body_byte(&hbody, sig->palg) &&
               add_packet_body_byte(&hbody, sig->halg) &&
               add_packet_body_subpackets(&hbody, sig, true);
     }
 
     if (res) {
         free(sig->hashed_data);
         /* get ownership on body data */
@@ -1000,20 +303,19 @@ signature_hash_direct(const pgp_signatur
     }
 
     return res;
 }
 
 rnp_result_t
 signature_check(pgp_signature_info_t *sinfo, pgp_hash_t *hash)
 {
-    time_t            now;
-    uint32_t          create, expiry, kcreate;
-    pgp_fingerprint_t fp = {};
-    rnp_result_t      ret = RNP_ERROR_SIGNATURE_INVALID;
+    time_t       now;
+    uint32_t     create, expiry, kcreate;
+    rnp_result_t ret = RNP_ERROR_SIGNATURE_INVALID;
 
     sinfo->no_signer = !sinfo->signer;
     sinfo->valid = false;
     sinfo->expired = false;
 
     if (!sinfo->sig) {
         ret = RNP_ERROR_NULL_POINTER;
         goto finish;
@@ -1030,18 +332,18 @@ signature_check(pgp_signature_info_t *si
           !signature_validate(sinfo->sig, pgp_key_get_material(sinfo->signer), hash);
     } else {
         sinfo->valid = false;
         RNP_LOG("invalid or untrusted key");
     }
 
     /* Check signature's expiration time */
     now = time(NULL);
-    create = signature_get_creation(sinfo->sig);
-    expiry = signature_get_expiration(sinfo->sig);
+    create = sinfo->sig->creation();
+    expiry = sinfo->sig->expiration();
     if (create > now) {
         /* signature created later then now */
         RNP_LOG("signature created %d seconds in future", (int) (create - now));
         sinfo->expired = true;
     }
     if (create && expiry && (create + expiry < now)) {
         /* signature expired */
         RNP_LOG("signature expired");
@@ -1058,17 +360,17 @@ signature_check(pgp_signature_info_t *si
     /* check whether key was not expired when sig created */
     if (!sinfo->ignore_expiry && pgp_key_get_expiration(sinfo->signer) &&
         (kcreate + pgp_key_get_expiration(sinfo->signer) < create)) {
         RNP_LOG("signature made after key expiration");
         sinfo->valid = false;
     }
 
     /* Check signer's fingerprint */
-    if (signature_get_keyfp(sinfo->sig, fp) && (fp != pgp_key_get_fp(sinfo->signer))) {
+    if (sinfo->sig->has_keyfp() && (sinfo->sig->keyfp() != pgp_key_get_fp(sinfo->signer))) {
         RNP_LOG("issuer fingerprint doesn't match signer's one");
         sinfo->valid = false;
     }
 
     if (sinfo->expired && sinfo->valid) {
         ret = RNP_ERROR_SIGNATURE_EXPIRED;
     } else {
         ret = sinfo->valid ? RNP_SUCCESS : RNP_ERROR_SIGNATURE_INVALID;
@@ -1100,34 +402,34 @@ signature_check_binding(pgp_signature_in
     pgp_hash_t   hash = {};
     rnp_result_t res = RNP_ERROR_SIGNATURE_INVALID;
 
     if (!signature_hash_binding(sinfo->sig, key, pgp_key_get_pkt(subkey), &hash)) {
         return RNP_ERROR_BAD_FORMAT;
     }
 
     res = signature_check(sinfo, &hash);
-    if (res || !(signature_get_key_flags(sinfo->sig) & PGP_KF_SIGN)) {
+    if (res || !(sinfo->sig->key_flags() & PGP_KF_SIGN)) {
         return res;
     }
 
     /* check primary key binding signature if any */
     res = RNP_ERROR_SIGNATURE_INVALID;
     sinfo->valid = false;
     pgp_sig_subpkt_t *subpkt =
-      signature_get_subpkt(sinfo->sig, PGP_SIG_SUBPKT_EMBEDDED_SIGNATURE);
+      sinfo->sig->get_subpkt(PGP_SIG_SUBPKT_EMBEDDED_SIGNATURE, false);
     if (!subpkt) {
         RNP_LOG("error! no primary key binding signature");
         return res;
     }
     if (!subpkt->parsed) {
         RNP_LOG("invalid embedded signature subpacket");
         return res;
     }
-    if (subpkt->fields.sig->type != PGP_SIG_PRIMARY) {
+    if (subpkt->fields.sig->type() != PGP_SIG_PRIMARY) {
         RNP_LOG("invalid primary key binding signature");
         return res;
     }
     if (subpkt->fields.sig->version < PGP_V4) {
         RNP_LOG("invalid primary key binding signature version");
         return res;
     }
 
@@ -1314,17 +616,17 @@ pgp_sig_subpkt_t::~pgp_sig_subpkt_t()
         delete fields.sig;
     }
     free(data);
 }
 
 pgp_signature_t::pgp_signature_t(const pgp_signature_t &src)
 {
     version = src.version;
-    type = src.type;
+    type_ = src.type_;
     palg = src.palg;
     halg = src.halg;
     memcpy(lbits, src.lbits, sizeof(src.lbits));
     creation_time = src.creation_time;
     signer = src.signer;
 
     hashed_len = src.hashed_len;
     hashed_data = NULL;
@@ -1343,17 +645,17 @@ pgp_signature_t::pgp_signature_t(const p
         memcpy(material_buf, src.material_buf, material_len);
     }
     subpkts = src.subpkts;
 }
 
 pgp_signature_t::pgp_signature_t(pgp_signature_t &&src)
 {
     version = src.version;
-    type = src.type;
+    type_ = src.type_;
     palg = src.palg;
     halg = src.halg;
     memcpy(lbits, src.lbits, sizeof(src.lbits));
     creation_time = src.creation_time;
     signer = src.signer;
     hashed_len = src.hashed_len;
     hashed_data = src.hashed_data;
     src.hashed_data = NULL;
@@ -1366,17 +668,17 @@ pgp_signature_t::pgp_signature_t(pgp_sig
 pgp_signature_t &
 pgp_signature_t::operator=(pgp_signature_t &&src)
 {
     if (this == &src) {
         return *this;
     }
 
     version = src.version;
-    type = src.type;
+    type_ = src.type_;
     palg = src.palg;
     halg = src.halg;
     memcpy(lbits, src.lbits, sizeof(src.lbits));
     creation_time = src.creation_time;
     signer = src.signer;
     hashed_len = src.hashed_len;
     free(hashed_data);
     hashed_data = src.hashed_data;
@@ -1393,17 +695,17 @@ pgp_signature_t::operator=(pgp_signature
 pgp_signature_t &
 pgp_signature_t::operator=(const pgp_signature_t &src)
 {
     if (this == &src) {
         return *this;
     }
 
     version = src.version;
-    type = src.type;
+    type_ = src.type_;
     palg = src.palg;
     halg = src.halg;
     memcpy(lbits, src.lbits, sizeof(src.lbits));
     creation_time = src.creation_time;
     signer = src.signer;
 
     hashed_len = src.hashed_len;
     free(hashed_data);
@@ -1447,8 +749,525 @@ pgp_signature_t::operator!=(const pgp_si
     return !(*this == src);
 }
 
 pgp_signature_t::~pgp_signature_t()
 {
     free(hashed_data);
     free(material_buf);
 }
+
+pgp_sig_subpkt_t *
+pgp_signature_t::get_subpkt(pgp_sig_subpacket_type_t stype, bool hashed)
+{
+    if (version < PGP_V4) {
+        return NULL;
+    }
+    for (auto &subpkt : subpkts) {
+        /* if hashed is false then accept any hashed/not hashed subpacket */
+        if ((subpkt.type == stype) && (!hashed || subpkt.hashed)) {
+            return &subpkt;
+        }
+    }
+    return NULL;
+}
+
+const pgp_sig_subpkt_t *
+pgp_signature_t::get_subpkt(pgp_sig_subpacket_type_t stype, bool hashed) const
+{
+    if (version < PGP_V4) {
+        return NULL;
+    }
+    for (auto &subpkt : subpkts) {
+        /* if hashed is false then accept any hashed/not hashed subpacket */
+        if ((subpkt.type == stype) && (!hashed || subpkt.hashed)) {
+            return &subpkt;
+        }
+    }
+    return NULL;
+}
+
+bool
+pgp_signature_t::has_subpkt(pgp_sig_subpacket_type_t stype, bool hashed) const
+{
+    if (version < PGP_V4) {
+        return false;
+    }
+    for (auto &subpkt : subpkts) {
+        /* if hashed is false then accept any hashed/not hashed subpacket */
+        if ((subpkt.type == stype) && (!hashed || subpkt.hashed)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool
+pgp_signature_t::has_keyid() const
+{
+    return (version < PGP_V4) || has_subpkt(PGP_SIG_SUBPKT_ISSUER_KEY_ID, false) ||
+           has_keyfp();
+}
+
+pgp_key_id_t
+pgp_signature_t::keyid() const
+{
+    /* version 3 uses signature field */
+    if (version < PGP_V4) {
+        return signer;
+    }
+
+    /* version 4 and up use subpackets */
+    pgp_key_id_t res;
+    static_assert(std::tuple_size<decltype(res)>::value == PGP_KEY_ID_SIZE,
+                  "pgp_key_id_t size mismatch");
+
+    const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_ISSUER_KEY_ID, false);
+    if (subpkt) {
+        memcpy(res.data(), subpkt->fields.issuer, PGP_KEY_ID_SIZE);
+        return res;
+    }
+    if ((subpkt = get_subpkt(PGP_SIG_SUBPKT_ISSUER_FPR))) {
+        memcpy(res.data(),
+               subpkt->fields.issuer_fp.fp + subpkt->fields.issuer_fp.len - PGP_KEY_ID_SIZE,
+               PGP_KEY_ID_SIZE);
+        return res;
+    }
+    throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS);
+}
+
+void
+pgp_signature_t::set_keyid(const pgp_key_id_t &id)
+{
+    if (version < PGP_V4) {
+        signer = id;
+        return;
+    }
+
+    static_assert(std::tuple_size<std::remove_reference<decltype(id)>::type>::value ==
+                    PGP_KEY_ID_SIZE,
+                  "pgp_key_id_t size mismatch");
+    pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_ISSUER_KEY_ID, PGP_KEY_ID_SIZE, true);
+    subpkt.parsed = true;
+    subpkt.hashed = false;
+    memcpy(subpkt.data, id.data(), PGP_KEY_ID_SIZE);
+    subpkt.fields.issuer = subpkt.data;
+}
+
+bool
+pgp_signature_t::has_keyfp() const
+{
+    if (version < PGP_V4) {
+        return false;
+    }
+    const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_ISSUER_FPR);
+    return subpkt && (subpkt->fields.issuer_fp.len <= PGP_FINGERPRINT_SIZE);
+}
+
+pgp_fingerprint_t
+pgp_signature_t::keyfp() const
+{
+    if (version < PGP_V4) {
+        throw rnp::rnp_exception(RNP_ERROR_BAD_STATE);
+    }
+    const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_ISSUER_FPR);
+    pgp_fingerprint_t       res;
+    if (!subpkt || (subpkt->fields.issuer_fp.len > sizeof(res.fingerprint))) {
+        throw rnp::rnp_exception(RNP_ERROR_BAD_STATE);
+    }
+    res.length = subpkt->fields.issuer_fp.len;
+    memcpy(res.fingerprint, subpkt->fields.issuer_fp.fp, subpkt->fields.issuer_fp.len);
+    return res;
+}
+
+void
+pgp_signature_t::set_keyfp(const pgp_fingerprint_t &fp)
+{
+    if (version < PGP_V4) {
+        throw rnp::rnp_exception(RNP_ERROR_BAD_STATE);
+    }
+    pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_ISSUER_FPR, 1 + fp.length, true);
+    subpkt.parsed = true;
+    subpkt.hashed = true;
+    subpkt.data[0] = 4;
+    memcpy(subpkt.data + 1, fp.fingerprint, fp.length);
+    subpkt.fields.issuer_fp.len = fp.length;
+    subpkt.fields.issuer_fp.version = subpkt.data[0];
+    subpkt.fields.issuer_fp.fp = subpkt.data + 1;
+}
+
+uint32_t
+pgp_signature_t::creation() const
+{
+    if (version < PGP_V4) {
+        return creation_time;
+    }
+    const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_CREATION_TIME);
+    return subpkt ? subpkt->fields.create : 0;
+}
+
+void
+pgp_signature_t::set_creation(uint32_t ctime)
+{
+    if (version < PGP_V4) {
+        creation_time = ctime;
+        return;
+    }
+
+    pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_CREATION_TIME, 4, true);
+    subpkt.parsed = true;
+    subpkt.hashed = true;
+    STORE32BE(subpkt.data, ctime);
+    subpkt.fields.create = ctime;
+}
+
+uint32_t
+pgp_signature_t::expiration() const
+{
+    const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_EXPIRATION_TIME);
+    return subpkt ? subpkt->fields.expiry : 0;
+}
+
+void
+pgp_signature_t::set_expiration(uint32_t etime)
+{
+    if (version < PGP_V4) {
+        throw rnp::rnp_exception(RNP_ERROR_BAD_STATE);
+    }
+
+    pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_EXPIRATION_TIME, 4, true);
+    subpkt.parsed = true;
+    subpkt.hashed = true;
+    STORE32BE(subpkt.data, etime);
+    subpkt.fields.expiry = etime;
+}
+
+uint32_t
+pgp_signature_t::key_expiration() const
+{
+    const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_KEY_EXPIRY);
+    return subpkt ? subpkt->fields.expiry : 0;
+}
+
+void
+pgp_signature_t::set_key_expiration(uint32_t etime)
+{
+    if (version < PGP_V4) {
+        throw rnp::rnp_exception(RNP_ERROR_BAD_STATE);
+    }
+
+    pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_KEY_EXPIRY, 4, true);
+    subpkt.parsed = true;
+    subpkt.hashed = true;
+    STORE32BE(subpkt.data, etime);
+    subpkt.fields.expiry = etime;
+}
+
+uint8_t
+pgp_signature_t::key_flags() const
+{
+    const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_KEY_FLAGS);
+    return subpkt ? subpkt->fields.key_flags : 0;
+}
+
+void
+pgp_signature_t::set_key_flags(uint8_t flags)
+{
+    pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_KEY_FLAGS, 1, true);
+    subpkt.parsed = true;
+    subpkt.hashed = true;
+    subpkt.data[0] = flags;
+    subpkt.fields.key_flags = flags;
+}
+
+bool
+pgp_signature_t::primary_uid() const
+{
+    const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_PRIMARY_USER_ID);
+    return subpkt ? subpkt->fields.primary_uid : false;
+}
+
+void
+pgp_signature_t::set_primary_uid(bool primary)
+{
+    pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_PRIMARY_USER_ID, 1, true);
+    subpkt.parsed = true;
+    subpkt.hashed = true;
+    subpkt.data[0] = primary;
+    subpkt.fields.primary_uid = primary;
+}
+
+std::vector<uint8_t>
+pgp_signature_t::preferred(pgp_sig_subpacket_type_t type) const
+{
+    const pgp_sig_subpkt_t *subpkt = get_subpkt(type);
+    return subpkt ? std::vector<uint8_t>(subpkt->fields.preferred.arr,
+                                         subpkt->fields.preferred.arr +
+                                           subpkt->fields.preferred.len) :
+                    std::vector<uint8_t>();
+}
+
+void
+pgp_signature_t::set_preferred(const std::vector<uint8_t> &data, pgp_sig_subpacket_type_t type)
+{
+    if (version < PGP_V4) {
+        throw rnp::rnp_exception(RNP_ERROR_BAD_STATE);
+    }
+
+    if (data.empty()) {
+        pgp_sig_subpkt_t *subpkt = get_subpkt(type);
+        if (subpkt) {
+            remove_subpkt(subpkt);
+        }
+        return;
+    }
+
+    pgp_sig_subpkt_t &subpkt = add_subpkt(type, data.size(), true);
+    subpkt.parsed = true;
+    subpkt.hashed = true;
+    memcpy(subpkt.data, data.data(), data.size());
+    subpkt.fields.preferred.arr = subpkt.data;
+    subpkt.fields.preferred.len = data.size();
+}
+
+std::vector<uint8_t>
+pgp_signature_t::preferred_symm_algs() const
+{
+    return preferred(PGP_SIG_SUBPKT_PREFERRED_SKA);
+}
+
+void
+pgp_signature_t::set_preferred_symm_algs(const std::vector<uint8_t> &algs)
+{
+    set_preferred(algs, PGP_SIG_SUBPKT_PREFERRED_SKA);
+}
+
+std::vector<uint8_t>
+pgp_signature_t::preferred_hash_algs() const
+{
+    return preferred(PGP_SIG_SUBPKT_PREFERRED_HASH);
+}
+
+void
+pgp_signature_t::set_preferred_hash_algs(const std::vector<uint8_t> &algs)
+{
+    set_preferred(algs, PGP_SIG_SUBPKT_PREFERRED_HASH);
+}
+
+std::vector<uint8_t>
+pgp_signature_t::preferred_z_algs() const
+{
+    return preferred(PGP_SIG_SUBPKT_PREF_COMPRESS);
+}
+
+void
+pgp_signature_t::set_preferred_z_algs(const std::vector<uint8_t> &algs)
+{
+    set_preferred(algs, PGP_SIG_SUBPKT_PREF_COMPRESS);
+}
+
+uint8_t
+pgp_signature_t::key_server_prefs() const
+{
+    const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_KEYSERV_PREFS);
+    return subpkt ? subpkt->data[0] : 0;
+}
+
+void
+pgp_signature_t::set_key_server_prefs(uint8_t prefs)
+{
+    if (version < PGP_V4) {
+        throw rnp::rnp_exception(RNP_ERROR_BAD_STATE);
+    }
+
+    pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_KEYSERV_PREFS, 1, true);
+    subpkt.parsed = true;
+    subpkt.hashed = true;
+    subpkt.data[0] = prefs;
+    subpkt.fields.ks_prefs.no_modify = prefs & 0x80;
+}
+
+std::string
+pgp_signature_t::key_server() const
+{
+    const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_PREF_KEYSERV);
+    return subpkt ? std::string((char *) subpkt->data, subpkt->len) : "";
+}
+
+void
+pgp_signature_t::set_key_server(const std::string &uri)
+{
+    if (version < PGP_V4) {
+        throw rnp::rnp_exception(RNP_ERROR_BAD_STATE);
+    }
+
+    if (uri.empty()) {
+        pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_PREF_KEYSERV);
+        if (subpkt) {
+            remove_subpkt(subpkt);
+        }
+        return;
+    }
+
+    pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_PREF_KEYSERV, uri.size(), true);
+    subpkt.parsed = true;
+    subpkt.hashed = true;
+    memcpy(subpkt.data, uri.data(), uri.size());
+    subpkt.fields.preferred_ks.uri = (char *) subpkt.data;
+    subpkt.fields.preferred_ks.len = uri.size();
+}
+
+uint8_t
+pgp_signature_t::trust_level() const
+{
+    const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_TRUST);
+    return subpkt ? subpkt->fields.trust.level : 0;
+}
+
+uint8_t
+pgp_signature_t::trust_amount() const
+{
+    const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_TRUST);
+    return subpkt ? subpkt->fields.trust.amount : 0;
+}
+
+void
+pgp_signature_t::set_trust(uint8_t level, uint8_t amount)
+{
+    pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_TRUST, 2, true);
+    subpkt.parsed = true;
+    subpkt.hashed = true;
+    subpkt.data[0] = level;
+    subpkt.data[1] = amount;
+    subpkt.fields.trust.level = level;
+    subpkt.fields.trust.amount = amount;
+}
+
+bool
+pgp_signature_t::revocable() const
+{
+    const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_REVOCABLE);
+    return subpkt ? subpkt->fields.revocable : true;
+}
+
+void
+pgp_signature_t::set_revocable(bool status)
+{
+    pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_REVOCABLE, 1, true);
+    subpkt.parsed = true;
+    subpkt.hashed = true;
+    subpkt.data[0] = status;
+    subpkt.fields.revocable = status;
+}
+
+std::string
+pgp_signature_t::revocation_reason() const
+{
+    const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_REVOCATION_REASON);
+    return subpkt ? std::string(subpkt->fields.revocation_reason.str,
+                                subpkt->fields.revocation_reason.len) :
+                    "";
+}
+
+pgp_revocation_type_t
+pgp_signature_t::revocation_code() const
+{
+    const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_REVOCATION_REASON);
+    return subpkt ? subpkt->fields.revocation_reason.code : PGP_REVOCATION_NO_REASON;
+}
+
+void
+pgp_signature_t::set_revocation_reason(pgp_revocation_type_t code, const std::string &reason)
+{
+    size_t            datalen = 1 + reason.size();
+    pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_REVOCATION_REASON, datalen, true);
+    subpkt.hashed = true;
+    subpkt.data[0] = code;
+    memcpy(subpkt.data + 1, reason.data(), reason.size());
+
+    if (!signature_parse_subpacket(subpkt)) {
+        throw rnp::rnp_exception(RNP_ERROR_BAD_STATE);
+    }
+}
+
+bool
+pgp_signature_t::key_has_features(pgp_key_feature_t flags) const
+{
+    const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_FEATURES);
+    return subpkt ? subpkt->data[0] & flags : false;
+}
+
+void
+pgp_signature_t::set_key_features(pgp_key_feature_t flags)
+{
+    pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_FEATURES, 1, true);
+    subpkt.hashed = true;
+    subpkt.data[0] = flags;
+    subpkt.fields.features = flags;
+    subpkt.parsed = true;
+}
+
+std::string
+pgp_signature_t::signer_uid() const
+{
+    const pgp_sig_subpkt_t *subpkt = get_subpkt(PGP_SIG_SUBPKT_SIGNERS_USER_ID);
+    return subpkt ? std::string(subpkt->fields.signer.uid, subpkt->fields.signer.len) : "";
+}
+
+void
+pgp_signature_t::set_signer_uid(const std::string &uid)
+{
+    pgp_sig_subpkt_t &subpkt = add_subpkt(PGP_SIG_SUBPKT_SIGNERS_USER_ID, uid.size(), true);
+    subpkt.hashed = true;
+    memcpy(subpkt.data, uid.data(), uid.size());
+    subpkt.fields.signer.uid = (const char *) subpkt.data;
+    subpkt.fields.signer.len = subpkt.len;
+    subpkt.parsed = true;
+}
+
+pgp_sig_subpkt_t &
+pgp_signature_t::add_subpkt(pgp_sig_subpacket_type_t type, size_t datalen, bool reuse)
+{
+    if (version < PGP_V4) {
+        RNP_LOG("wrong signature version");
+        throw std::invalid_argument("version");
+    }
+
+    uint8_t *newdata = (uint8_t *) calloc(1, datalen);
+    if (!newdata) {
+        RNP_LOG("Allocation failed");
+        throw std::bad_alloc();
+    }
+
+    pgp_sig_subpkt_t *subpkt = NULL;
+    if (reuse && (subpkt = get_subpkt(type))) {
+        *subpkt = {};
+    } else {
+        subpkts.push_back({});
+        subpkt = &subpkts.back();
+    }
+
+    subpkt->data = newdata;
+    subpkt->type = type;
+    subpkt->len = datalen;
+    return *subpkt;
+}
+
+void
+pgp_signature_t::remove_subpkt(pgp_sig_subpkt_t *subpkt)
+{
+    for (auto it = subpkts.begin(); it < subpkts.end(); it++) {
+        if (&*it == subpkt) {
+            subpkts.erase(it);
+            return;
+        }
+    }
+}
+
+bool
+pgp_signature_t::matches_onepass(const pgp_one_pass_sig_t &onepass) const
+{
+    if (!has_keyid()) {
+        return false;
+    }
+    return (halg == onepass.halg) && (palg == onepass.palg) && (type_ == onepass.type) &&
+           (onepass.keyid == keyid());
+}
--- a/third_party/rnp/src/librepgp/stream-sig.h
+++ b/third_party/rnp/src/librepgp/stream-sig.h
@@ -42,262 +42,23 @@ typedef struct pgp_signature_info_t {
     bool             no_signer; /* no signer's public key available */
     bool             expired;   /* signature is expired */
     bool             signer_valid;  /* assume that signing key is valid */
     bool             ignore_expiry; /* ignore signer's key expiration time */
 } pgp_signature_info_t;
 
 typedef std::vector<pgp_signature_t> pgp_signature_list_t;
 
-/**
- * @brief Check whether signature packet matches one-pass signature packet.
- * @param sig pointer to the read signature packet
- * @param onepass pointer to the read one-pass signature packet
- * @return true if sig corresponds to onepass or false otherwise
- */
-bool signature_matches_onepass(pgp_signature_t *sig, pgp_one_pass_sig_t *onepass);
-
-/**
- * @brief Get v4 signature's subpacket of the specified type
- * @param sig loaded or populated signature, could not be NULL
- * @param type type of the subpacket to lookup for
- * @return pointer to the subpacket structure or NULL if it was not found or error occurred
- */
-pgp_sig_subpkt_t *signature_get_subpkt(pgp_signature_t *sig, pgp_sig_subpacket_type_t type);
-const pgp_sig_subpkt_t *signature_get_subpkt(const pgp_signature_t *  sig,
-                                             pgp_sig_subpacket_type_t type);
-
-/**
- * @brief Add subpacket of the specified type to v4 signature
- * @param sig loaded or populated signature, could not be NULL
- * @param type type of the subpacket
- * @param datalen length of the subpacket body
- * @param reuse replace already existing subpacket of the specified type if any
- * @return pointer to the subpacket structure or NULL if error occurred
- */
-pgp_sig_subpkt_t *signature_add_subpkt(pgp_signature_t *        sig,
-                                       pgp_sig_subpacket_type_t type,
-                                       size_t                   datalen,
-                                       bool                     reuse);
-
-/**
- * @brief Remove signature's subpacket
- * @param sig loaded or populated signature, could not be NULL
- * @param subpkt subpacket to remove. If not in the subpackets list then no action is taken.
- */
-void signature_remove_subpkt(pgp_signature_t *sig, pgp_sig_subpkt_t *subpkt);
-
-/**
- * @brief Get type of the signature.
- * @param sig loaded or populated signature, could not be NULL
- * @return type of the signature
- */
-pgp_sig_type_t signature_get_type(const pgp_signature_t *sig);
-
-/**
- * @brief Check whether signature has signing key fingerprint
- * @param sig loaded or populated v4 signature, could not be NULL
- * @return true if fingerprint is available or false otherwise
- */
-bool signature_has_keyfp(const pgp_signature_t *sig);
-
-/**
- * @brief Get signing key's fingerprint if it is available
- * @param sig loaded or populated v4 signature, could not be NULL
- * @param fp reference to the fingerprint structure
- * @return true if fingerprint is available and returned or false otherwise
- */
-bool signature_get_keyfp(const pgp_signature_t *sig, pgp_fingerprint_t &fp);
-
-/**
- * @brief Set signing key fingerprint
- * @param sig v4 signature being populated
- * @param fp fingerprint structure
- * @return true on success or false otherwise;
- */
-bool signature_set_keyfp(pgp_signature_t *sig, const pgp_fingerprint_t &fp);
-
-/**
- * @brief Check whether signature has signing key id
- * @param sig populated or loaded signature
- * @return true if key id available (via v3 field, or v4 key id/key fp subpacket)
- */
-bool signature_has_keyid(const pgp_signature_t *sig);
-
-/**
- * @brief Get signature's signing key id
- * @param sig populated or loaded signature
- * @param id reference to return key identifier
- * @return true on success or false otherwise
- */
-bool signature_get_keyid(const pgp_signature_t *sig, pgp_key_id_t &id);
-
-/**
- * @brief Set the signature's key id
- * @param sig signature being populated. Version should be set prior of setting key id.
- * @param id reference to key identifier
- * @return true on success or false otherwise
- */
-bool signature_set_keyid(pgp_signature_t *sig, const pgp_key_id_t &id);
-
-/**
- * @brief Get signature's creation time
- * @param sig pointer to the loaded or populated signature.
- * @return time in seconds since the Jan 1, 1970 UTC. 0 is the default value and returned even
- *         if creation time is not available
- */
-uint32_t signature_get_creation(const pgp_signature_t *sig);
-
-/**
- * @brief Set signature's creation time
- * @param sig signature being populated
- * @param ctime creation time in seconds since the Jan 1, 1970 UTC.
- * @return true on success or false otherwise
- */
-bool signature_set_creation(pgp_signature_t *sig, uint32_t ctime);
-
-/**
- * @brief Get the signature's expiration time
- * @param sig populated or loaded signature
- * @return expiration time in seconds since the creation time. 0 if signature never expires.
- */
-uint32_t signature_get_expiration(const pgp_signature_t *sig);
-
-/**
- * @brief Set the signature's expiration time
- * @param sig signature being populated
- * @param etime expiration time
- * @return true on success or false otherwise
- */
-bool signature_set_expiration(pgp_signature_t *sig, uint32_t etime);
-
-/**
- * @brief Check whether signature has key expiration
- * @param sig populated or loaded signature
- * @return true if signature has key expiration time or false otherwise
- */
-bool signature_has_key_expiration(const pgp_signature_t *sig);
-
-/**
- * @brief Get the key expiration time
- * @param sig populated or loaded signature
- * @return expiration time in seconds since the creation time. 0 if key never expires.
- */
-uint32_t signature_get_key_expiration(const pgp_signature_t *sig);
-
-/**
- * @brief Set the key expiration time
- * @param sig signature being populated
- * @param etime expiration time
- * @return true on success or false otherwise
- */
-bool signature_set_key_expiration(pgp_signature_t *sig, uint32_t etime);
-
-/**
- * @brief Check whether signature has key flags
- * @param sig populated or loaded signature
- * @return true if key flags are available or false otherwise
- */
-bool signature_has_key_flags(const pgp_signature_t *sig);
-
-/**
- * @brief Get the key flags
- * @param sig populated or loaded signature
- * @return byte of key flags. If there is no corresponding subpackets then 0 is returned.
- */
-uint8_t signature_get_key_flags(const pgp_signature_t *sig);
-
-/**
- * @brief Set the key flags
- * @param sig signature being populated
- * @param flags byte of key flags
- * @return true on success or false otherwise
- */
-bool signature_set_key_flags(pgp_signature_t *sig, uint8_t flags);
-
-/**
- * @brief Get the primary user id flag
- * @param sig populated or loaded signature
- * @return true if user id is marked as primary or false otherwise
- */
-bool signature_get_primary_uid(pgp_signature_t *sig);
-
-/**
- * @brief Set the primary user id flag
- * @param sig signature being populated
- * @param primary true if user id should be marked as primary
- * @return true on success or false otherwise
- */
-bool signature_set_primary_uid(pgp_signature_t *sig, bool primary);
-
-bool signature_has_preferred_symm_algs(const pgp_signature_t *sig);
-
-bool signature_get_preferred_symm_algs(const pgp_signature_t *sig,
-                                       uint8_t **             algs,
-                                       size_t *               count);
-
-bool signature_set_preferred_symm_algs(pgp_signature_t *sig, uint8_t algs[], size_t len);
-
-bool signature_has_preferred_hash_algs(const pgp_signature_t *sig);
-
-bool signature_get_preferred_hash_algs(const pgp_signature_t *sig,
-                                       uint8_t **             algs,
-                                       size_t *               count);
-
-bool signature_set_preferred_hash_algs(pgp_signature_t *sig, uint8_t algs[], size_t len);
-
-bool signature_has_preferred_z_algs(const pgp_signature_t *sig);
-
-bool signature_get_preferred_z_algs(const pgp_signature_t *sig, uint8_t **algs, size_t *count);
-
-bool signature_set_preferred_z_algs(pgp_signature_t *sig, uint8_t algs[], size_t len);
-
-bool signature_has_key_server_prefs(const pgp_signature_t *sig);
-
-uint8_t signature_get_key_server_prefs(const pgp_signature_t *sig);
-
-bool signature_set_key_server_prefs(pgp_signature_t *sig, uint8_t prefs);
-
-bool signature_set_preferred_key_server(pgp_signature_t *sig, const char *uri);
-
-bool signature_has_trust(const pgp_signature_t *sig);
-
-bool signature_get_trust(const pgp_signature_t *sig, uint8_t *level, uint8_t *amount);
-
-bool signature_set_trust(pgp_signature_t *sig, uint8_t level, uint8_t amount);
-
-bool signature_get_revocable(const pgp_signature_t *sig);
-
-bool signature_set_revocable(pgp_signature_t *sig, bool revocable);
-
-bool signature_set_features(pgp_signature_t *sig, uint8_t features);
-
-bool signature_set_signer_uid(pgp_signature_t *sig, uint8_t *uid, size_t len);
-
 bool signature_set_embedded_sig(pgp_signature_t *sig, pgp_signature_t *esig);
 
 bool signature_add_notation_data(pgp_signature_t *sig,
                                  bool             readable,
                                  const char *     name,
                                  const char *     value);
 
-bool signature_has_key_server(const pgp_signature_t *sig);
-
-char *signature_get_key_server(const pgp_signature_t *sig);
-
-bool signature_has_revocation_reason(const pgp_signature_t *sig);
-
-bool signature_get_revocation_reason(const pgp_signature_t *sig,
-                                     pgp_revocation_type_t *code,
-                                     char **                reason);
-
-bool signature_set_revocation_reason(pgp_signature_t *     sig,
-                                     pgp_revocation_type_t code,
-                                     const char *          reason);
-
 /**
  * @brief Fill signature's hashed data. This includes all the fields from signature which are
  *        hashed after the previous document or key fields.
  * @param sig Signature being populated
  * @return true if sig->hashed_data is filled up correctly or false otherwise
  */
 bool signature_fill_hashed_data(pgp_signature_t *sig);
 
--- a/third_party/rnp/src/librepgp/stream-write.cpp
+++ b/third_party/rnp/src/librepgp/stream-write.cpp
@@ -131,26 +131,24 @@ typedef struct pgp_dest_partial_param_t 
     size_t      partlen; /* length of the current part, up to PARTIAL_PKT_BLOCK_SIZE */
     size_t      len;     /* bytes cached in part */
 } pgp_dest_partial_param_t;
 
 static rnp_result_t
 partial_dst_write(pgp_dest_t *dst, const void *buf, size_t len)
 {
     pgp_dest_partial_param_t *param = (pgp_dest_partial_param_t *) dst->param;
-    int                       wrlen;
-
     if (!param) {
         RNP_LOG("wrong param");
         return RNP_ERROR_BAD_PARAMETERS;
     }
 
     if (len > param->partlen - param->len) {
         /* we have full part - in block and in buf */
-        wrlen = param->partlen - param->len;
+        size_t wrlen = param->partlen - param->len;
         dst_write(param->writedst, &param->parthdr, 1);
         dst_write(param->writedst, param->part, param->len);
         dst_write(param->writedst, buf, wrlen);
 
         buf = (uint8_t *) buf + wrlen;
         len -= wrlen;
         param->len = 0;
 
@@ -499,17 +497,17 @@ static rnp_result_t
 encrypted_add_recipient(pgp_write_handler_t *handler,
                         pgp_dest_t *         dst,
                         pgp_key_t *          userkey,
                         const uint8_t *      key,
                         const unsigned       keylen)
 {
     uint8_t                     enckey[PGP_MAX_KEY_SIZE + 3];
     unsigned                    checksum = 0;
-    pgp_pk_sesskey_t            pkey = {0};
+    pgp_pk_sesskey_t            pkey;
     pgp_dest_encrypted_param_t *param = (pgp_dest_encrypted_param_t *) dst->param;
     rnp_result_t                ret = RNP_ERROR_GENERIC;
 
     /* Use primary key if good for encryption, otherwise look in subkey list */
     userkey =
       find_suitable_key(PGP_OP_ENCRYPT_SYM, userkey, handler->key_provider, PGP_KF_ENCRYPT);
     if (!userkey) {
         return RNP_ERROR_NO_SUITABLE_KEY;
@@ -620,17 +618,17 @@ encrypted_sesk_set_ad(pgp_crypt_t *crypt
 
 static rnp_result_t
 encrypted_add_password(rnp_symmetric_pass_info_t * pass,
                        pgp_dest_encrypted_param_t *param,
                        uint8_t *                   key,
                        const unsigned              keylen,
                        bool                        singlepass)
 {
-    pgp_sk_sesskey_t skey = {0};
+    pgp_sk_sesskey_t skey = {};
     unsigned         s2keylen; /* length of the s2k key */
     pgp_crypt_t      kcrypt;
     uint8_t          nonce[PGP_AEAD_MAX_NONCE_LEN];
     bool             res;
 
     skey.alg = param->ctx->ealg;
     skey.s2k = pass->s2k;
 
@@ -844,49 +842,44 @@ init_encrypted_dst(pgp_write_handler_t *
     param->aalg = handler->ctx->aalg;
     param->ctx = handler->ctx;
     param->pkt.origdst = writedst;
     dst->write = param->aead ? encrypted_dst_write_aead : encrypted_dst_write_cfb;
     dst->finish = encrypted_dst_finish;
     dst->close = encrypted_dst_close;
     dst->type = PGP_STREAM_ENCRYPTED;
 
-    pkeycount = list_length(handler->ctx->recipients);
-    skeycount = list_length(handler->ctx->passwords);
+    pkeycount = handler->ctx->recipients.size();
+    skeycount = handler->ctx->passwords.size();
 
     if (!pkeycount && !skeycount) {
         RNP_LOG("no recipients");
         ret = RNP_ERROR_BAD_PARAMETERS;
         goto finish;
     }
 
     if ((pkeycount > 0) || (skeycount > 1) || param->aead) {
         if (!rng_get_data(rnp_ctx_rng_handle(handler->ctx), enckey, keylen)) {
             ret = RNP_ERROR_RNG;
             goto finish;
         }
         singlepass = false;
     }
 
     /* Configuring and writing pk-encrypted session keys */
-    if (pkeycount > 0) {
-        for (list_item *recipient = list_front(handler->ctx->recipients); recipient;
-             recipient = list_next(recipient)) {
-            ret =
-              encrypted_add_recipient(handler, dst, *(pgp_key_t **) recipient, enckey, keylen);
-            if (ret != RNP_SUCCESS) {
-                goto finish;
-            }
+    for (auto recipient : handler->ctx->recipients) {
+        ret = encrypted_add_recipient(handler, dst, recipient, enckey, keylen);
+        if (ret) {
+            goto finish;
         }
     }
 
     /* Configuring and writing sk-encrypted session key(s) */
-    for (list_item *pi = list_front(handler->ctx->passwords); pi; pi = list_next(pi)) {
-        ret = encrypted_add_password(
-          (rnp_symmetric_pass_info_t *) pi, param, enckey, keylen, singlepass);
+    for (auto &passinfo : handler->ctx->passwords) {
+        ret = encrypted_add_password(&passinfo, param, enckey, keylen, singlepass);
         if (ret != RNP_SUCCESS) {
             goto finish;
         }
     }
 
     /* Initializing partial packet writer */
     param->pkt.partial = true;
     param->pkt.indeterminate = false;
@@ -906,28 +899,22 @@ init_encrypted_dst(pgp_write_handler_t *
 
     if (param->aead) {
         /* initialize AEAD encryption */
         ret = encrypted_start_aead(param, enckey);
     } else {
         /* initialize old CFB or CFB with MDC */
         ret = encrypted_start_cfb(param, enckey);
     }
-
 finish:
-    for (list_item *pi = list_front(handler->ctx->passwords); pi; pi = list_next(pi)) {
-        rnp_symmetric_pass_info_t *pass = (rnp_symmetric_pass_info_t *) pi;
-        pgp_forget(pass, sizeof(*pass));
-    }
-    list_destroy(&handler->ctx->passwords);
+    handler->ctx->passwords.clear();
     pgp_forget(enckey, sizeof(enckey));
-    if (ret != RNP_SUCCESS) {
+    if (ret) {
         encrypted_dst_close(dst, true);
     }
-
     return ret;
 }
 
 static rnp_result_t
 signed_dst_write(pgp_dest_t *dst, const void *buf, size_t len)
 {
     pgp_dest_signed_param_t *param = (pgp_dest_signed_param_t *) dst->param;
     dst_write(param->writedst, buf, len);
@@ -1065,26 +1052,29 @@ cleartext_dst_write(pgp_dest_t *dst, con
 static rnp_result_t
 signed_fill_signature(pgp_dest_signed_param_t *param,
                       pgp_signature_t *        sig,
                       pgp_dest_signer_info_t * signer)
 {
     pgp_key_pkt_t *    deckey = NULL;
     pgp_hash_t         hash;
     pgp_password_ctx_t ctx = {.op = PGP_OP_SIGN, .key = signer->key};
-    bool               res;
     rnp_result_t       ret = RNP_ERROR_GENERIC;
 
     /* fill signature fields */
-    res = signature_set_keyfp(sig, pgp_key_get_fp(signer->key)) &&
-          signature_set_keyid(sig, pgp_key_get_keyid(signer->key)) &&
-          signature_set_creation(sig, signer->sigcreate ? signer->sigcreate : time(NULL)) &&
-          signature_set_expiration(sig, signer->sigexpire) && signature_fill_hashed_data(sig);
-
-    if (!res) {
+    try {
+        sig->set_keyfp(pgp_key_get_fp(signer->key));
+        sig->set_keyid(pgp_key_get_keyid(signer->key));
+        sig->set_creation(signer->sigcreate ? signer->sigcreate : time(NULL));
+        sig->set_expiration(signer->sigexpire);
+    } catch (const std::exception &e) {
+        RNP_LOG("failed to setup signature fields: %s", e.what());
+        return RNP_ERROR_OUT_OF_MEMORY;
+    }
+    if (!signature_fill_hashed_data(sig)) {
         RNP_LOG("failed to fill the signature data");
         return RNP_ERROR_OUT_OF_MEMORY;
     }
 
     if (!pgp_hash_copy(&hash, pgp_hash_list_get(param->hashes, sig->halg))) {
         RNP_LOG("failed to obtain hash");
         return RNP_ERROR_BAD_PARAMETERS;
     }
@@ -1118,21 +1108,21 @@ signed_write_signature(pgp_dest_signed_p
 {
     pgp_signature_t sig;
     rnp_result_t    ret;
 
     sig.version = (pgp_version_t) 4;
     if (signer->onepass.version) {
         sig.halg = signer->onepass.halg;
         sig.palg = signer->onepass.palg;
-        sig.type = signer->onepass.type;
+        sig.set_type(signer->onepass.type);
     } else {
         sig.halg = pgp_hash_adjust_alg_to_key(signer->halg, pgp_key_get_pkt(signer->key));
         sig.palg = pgp_key_get_alg(signer->key);
-        sig.type = param->ctx->detached ? PGP_SIG_BINARY : PGP_SIG_TEXT;
+        sig.set_type(param->ctx->detached ? PGP_SIG_BINARY : PGP_SIG_TEXT);
     }
 
     if (!(ret = signed_fill_signature(param, &sig, signer))) {
         ret = stream_write_signature(&sig, writedst) ? RNP_SUCCESS : RNP_ERROR_WRITE;
     }
     return ret;
 }
 
@@ -1323,18 +1313,18 @@ init_signed_dst(pgp_write_handler_t *han
     } else {
         dst->type = PGP_STREAM_SIGNED;
         dst->write = signed_dst_write;
         dst->finish = param->ctx->detached ? signed_detached_dst_finish : signed_dst_finish;
     }
     dst->close = signed_dst_close;
 
     /* Getting signer's infos, writing one-pass signatures if needed */
-    for (list_item *sg = list_front(handler->ctx->signers); sg; sg = list_next(sg)) {
-        ret = signed_add_signer(param, (rnp_signer_info_t *) sg, !list_next(sg));
+    for (auto &sg : handler->ctx->signers) {
+        ret = signed_add_signer(param, &sg, &sg == &handler->ctx->signers.back());
         if (ret) {
             RNP_LOG("failed to add one-pass signature for signer");
             goto finish;
         }
     }
 
     /* Do we have any signatures? */
     if (param->hashes.empty()) {
@@ -1639,17 +1629,17 @@ literal_dst_close(pgp_dest_t *dst, bool 
     dst->param = NULL;
 }
 
 static rnp_result_t
 init_literal_dst(pgp_write_handler_t *handler, pgp_dest_t *dst, pgp_dest_t *writedst)
 {
     pgp_dest_packet_param_t *param;
     rnp_result_t             ret = RNP_ERROR_GENERIC;
-    int                      flen = 0;
+    size_t                   flen = 0;
     uint8_t                  buf[4];
 
     if (!init_dst_common(dst, sizeof(*param))) {
         return RNP_ERROR_OUT_OF_MEMORY;
     }
 
     param = (pgp_dest_packet_param_t *) dst->param;
     dst->write = literal_dst_write;
@@ -1664,27 +1654,25 @@ init_literal_dst(pgp_write_handler_t *ha
     if (!init_streamed_packet(param, writedst)) {
         RNP_LOG("failed to init streamed packet");
         ret = RNP_ERROR_BAD_PARAMETERS;
         goto finish;
     }
     /* content type - forcing binary now */
     buf[0] = (uint8_t) 'b';
     /* filename */
-    if (handler->ctx->filename) {
-        flen = strlen(handler->ctx->filename);
-        if (flen > 255) {
-            RNP_LOG("filename too long, truncating");
-            flen = 255;
-        }
+    flen = handler->ctx->filename.size();
+    if (flen > 255) {
+        RNP_LOG("filename too long, truncating");
+        flen = 255;
     }
     buf[1] = (uint8_t) flen;
     dst_write(param->writedst, buf, 2);
-    if (flen > 0) {
-        dst_write(param->writedst, handler->ctx->filename, flen);
+    if (flen) {
+        dst_write(param->writedst, handler->ctx->filename.c_str(), flen);
     }
     /* timestamp */
     STORE32BE(buf, handler->ctx->filemtime);
     dst_write(param->writedst, buf, 4);
     ret = RNP_SUCCESS;
 finish:
     if (ret != RNP_SUCCESS) {
         literal_dst_close(dst, true);
@@ -1931,80 +1919,77 @@ finish:
     }
     return ret;
 }
 
 rnp_result_t
 rnp_compress_src(pgp_source_t &src, pgp_dest_t &dst, pgp_compression_type_t zalg, int zlevel)
 {
     pgp_write_handler_t handler = {};
-    rnp_ctx_t           ctx = {};
+    rnp_ctx_t           ctx;
     ctx.zalg = zalg;
     ctx.zlevel = zlevel;
     handler.ctx = &ctx;
 
     pgp_dest_t   compressed = {};
     rnp_result_t ret = init_compressed_dst(&handler, &compressed, &dst);
     if (ret) {
         goto done;
     }
     ret = dst_write_src(&src, &compressed);
 done:
     dst_close(&compressed, ret);
-    rnp_ctx_free(&ctx);
     return ret;
 }
 
 rnp_result_t
 rnp_wrap_src(pgp_source_t &src, pgp_dest_t &dst, const std::string &filename, uint32_t modtime)
 {
     pgp_write_handler_t handler = {};
-    rnp_ctx_t           ctx = {};
-    ctx.filename = strdup(filename.c_str());
+    rnp_ctx_t           ctx;
+    ctx.filename = filename;
     ctx.filemtime = modtime;
     handler.ctx = &ctx;
 
     pgp_dest_t   literal = {};
     rnp_result_t ret = init_literal_dst(&handler, &literal, &dst);
     if (ret) {
         goto done;
     }
 
     ret = dst_write_src(&src, &literal);
 done:
     dst_close(&literal, ret);
-    rnp_ctx_free(&ctx);
     return ret;
 }
 
 rnp_result_t
 rnp_raw_encrypt_src(pgp_source_t &src, pgp_dest_t &dst, const std::string &password)
 {
     pgp_write_handler_t handler = {};
-    rnp_ctx_t           ctx = {};
+    rnp_ctx_t           ctx;
     rng_t               rng = {};
 
     if (!rng_init(&rng, RNG_SYSTEM)) {
         return RNP_ERROR_BAD_STATE;
     }
     ctx.rng = &rng;
     ctx.ealg = DEFAULT_PGP_SYMM_ALG;
     handler.ctx = &ctx;
     pgp_dest_t encrypted = {};
 
     rnp_result_t ret = rnp_ctx_add_encryption_password(
-      &ctx, password.c_str(), DEFAULT_PGP_HASH_ALG, DEFAULT_PGP_SYMM_ALG, 0);
+      ctx, password.c_str(), DEFAULT_PGP_HASH_ALG, DEFAULT_PGP_SYMM_ALG, 0);
     if (ret) {
         goto done;
     }
 
     ret = init_encrypted_dst(&handler, &encrypted, &dst);
     if (ret) {
         goto done;
     }
 
     ret = dst_write_src(&src, &encrypted);
 done:
     dst_close(&encrypted, ret);
-    rnp_ctx_free(&ctx);
     rng_destroy(&rng);
     return ret;
 }
--- a/third_party/rnp/src/rnp/fficli.h
+++ b/third_party/rnp/src/rnp/fficli.h
@@ -26,16 +26,17 @@
 
 #ifndef FFICLI_H_
 #define FFICLI_H_
 
 #include <stddef.h>
 #include <stdbool.h>
 #include <time.h>
 #include "rnp/rnp.h"
+#include "rnp/rnp_err.h"
 #include "rnpcfg.h"
 #include "json.h"
 
 typedef struct cli_rnp_t {
     rnp_ffi_t ffi;
     rnp_cfg_t cfg;
     FILE *    resfp;      /* where to put result messages, defaults to stdout */
     FILE *    passfp;     /* file pointer for password input */
--- a/third_party/rnp/src/rnpkeys/rnpkeys.cpp
+++ b/third_party/rnp/src/rnpkeys/rnpkeys.cpp
@@ -159,72 +159,86 @@ imported_key_changed(json_object *key)
     return sec && ((!strcmp(sec, "updated") || !strcmp(sec, "new")));
 }
 
 static bool
 import_keys(cli_rnp_t *rnp, const char *file)
 {
     rnp_input_t input = NULL;
     bool        res = false;
+    bool        updated = false;
 
     if (rnp_input_from_path(&input, file)) {
         ERR_MSG("failed to open file %s", file);
         return false;
     }
 
-    uint32_t     flags = RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS;
-    char *       results = NULL;
-    json_object *jso = NULL;
-    json_object *keys = NULL;
+    uint32_t flags =
+      RNP_LOAD_SAVE_PUBLIC_KEYS | RNP_LOAD_SAVE_SECRET_KEYS | RNP_LOAD_SAVE_SINGLE;
 
     bool permissive = rnp_cfg_getbool(cli_rnp_cfg(rnp), CFG_PERMISSIVE);
     if (permissive) {
         flags |= RNP_LOAD_SAVE_PERMISSIVE;
     }
 
-    if (rnp_import_keys(rnp->ffi, input, flags, &results)) {
-        ERR_MSG("failed to import keys from file %s", file);
-        goto done;
-    }
-    // print information about imported key(s)
-    jso = json_tokener_parse(results);
-    if (!jso || !json_object_object_get_ex(jso, "keys", &keys)) {
-        ERR_MSG("invalid key import result");
-        goto done;
-    }
+    do {
+        /* load keys one-by-one */
+        char *       results = NULL;
+        rnp_result_t ret = rnp_import_keys(rnp->ffi, input, flags, &results);
+        if (ret == RNP_ERROR_EOF) {
+            res = true;
+            break;
+        }
+        if (ret) {
+            ERR_MSG("failed to import key(s), from file %s, stopping.", file);
+            break;
+        }
 
-    for (size_t idx = 0; idx < (size_t) json_object_array_length(keys); idx++) {
-        json_object *    keyinfo = json_object_array_get_idx(keys, idx);
-        rnp_key_handle_t key = NULL;
-        if (!keyinfo || !imported_key_changed(keyinfo)) {
-            continue;
+        // print information about imported key(s)
+        json_object *jso = json_tokener_parse(results);
+        rnp_buffer_destroy(results);
+        if (!jso) {
+            ERR_MSG("invalid key import resulting JSON");
+            break;
+        }
+        json_object *keys = NULL;
+        if (!json_object_object_get_ex(jso, "keys", &keys)) {
+            ERR_MSG("invalid key import JSON contents");
+            json_object_put(jso);
+            break;
         }
-        const char *fphex = json_obj_get_str(keyinfo, "fingerprint");
-        if (rnp_locate_key(rnp->ffi, "fingerprint", fphex, &key) || !key) {
-            ERR_MSG("failed to locate key with fingerprint %s", fphex);
-            continue;
+        for (size_t idx = 0; idx < (size_t) json_object_array_length(keys); idx++) {
+            json_object *    keyinfo = json_object_array_get_idx(keys, idx);
+            rnp_key_handle_t key = NULL;
+            if (!keyinfo || !imported_key_changed(keyinfo)) {
+                continue;
+            }
+            const char *fphex = json_obj_get_str(keyinfo, "fingerprint");
+            if (rnp_locate_key(rnp->ffi, "fingerprint", fphex, &key) || !key) {
+                ERR_MSG("failed to locate key with fingerprint %s", fphex);
+                continue;
+            }
+            cli_rnp_print_key_info(stdout, rnp->ffi, key, true, false);
+            rnp_key_handle_destroy(key);
+            updated = true;
         }
-        cli_rnp_print_key_info(stdout, rnp->ffi, key, true, false);
-        rnp_key_handle_destroy(key);
-    }
+        json_object_put(jso);
+    } while (1);
 
-    // set default key if we didn't have one
-    if (cli_rnp_defkey(rnp).empty()) {
-        cli_rnp_set_default_key(rnp);
-    }
+    if (updated) {
+        // set default key if we didn't have one
+        if (cli_rnp_defkey(rnp).empty()) {
+            cli_rnp_set_default_key(rnp);
+        }
 
-    // save public and secret keyrings
-    if (!cli_rnp_save_keyrings(rnp)) {
-        ERR_MSG("failed to save keyrings");
-        goto done;
+        // save public and secret keyrings
+        if (!cli_rnp_save_keyrings(rnp)) {
+            ERR_MSG("failed to save keyrings");
+        }
     }
-    res = true;
-done:
-    json_object_put(jso);
-    rnp_buffer_destroy(results);
     rnp_input_destroy(input);
     return res;
 }
 
 static bool
 import_sigs(cli_rnp_t *rnp, const char *file)
 {
     rnp_input_t input = NULL;
--- a/third_party/rnp/src/tests/CMakeLists.txt
+++ b/third_party/rnp/src/tests/CMakeLists.txt
@@ -63,20 +63,23 @@ add_executable(rnp_tests
   ../rnp/fficli.cpp
   ../rnp/rnp.cpp
   ../rnpkeys/rnpkeys.cpp
   ../rnpkeys/main.cpp
   ../rnpkeys/tui.cpp
   ../fuzzing/keyring.c
   ../fuzzing/keyring_g10.cpp
   ../fuzzing/keyring_kbx.c
+  ../fuzzing/keyimport.c
   cipher.cpp
   cli.cpp
   exportkey.cpp
   ffi.cpp
+  ffi-enc.cpp
+  file-utils.cpp
   generatekey.cpp
   kbx-nsigs-test.cpp
   key-add-userid.cpp
   key-grip.cpp
   key-prefs.cpp
   key-protect.cpp
   key-store-search.cpp
   key-unlock.cpp
@@ -94,19 +97,21 @@ add_executable(rnp_tests
   support.cpp
   user-prefs.cpp
   utils-hex2bin.cpp
   utils-list.cpp
   utils-rnpcfg.cpp
   issues/1030.cpp
   issues/1115.cpp
   issues/1171.cpp
+  issues/oss-fuzz-25489.cpp
   fuzz_keyring.cpp
   fuzz_keyring_g10.cpp
   fuzz_keyring_kbx.cpp
+  fuzz_keyimport.cpp
 )
 if(MSVC)
   find_package(WindowsSDK)
   GetUMWindowsSDKLibraryDir(WIN_LIBRARY_DIR)
   message (STATUS "WIN_LIBRARY_DIR: ${WIN_LIBRARY_DIR}")
   find_library(SHLWAPI_LIBRARY
     PATHS
        ${WIN_LIBRARY_DIR}
new file mode 100644
--- /dev/null
+++ b/third_party/rnp/src/tests/data/issue1188/armored_revocation_signature.pgp
@@ -0,0 +1,12 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+wsD2BCABCAAgFiEEjfYTgEZi8VK4srNzBBUFBnoZpfIFAl796EQCHQAACgkQBBUFBnoZpfKpMwv8
+CLJNFLb2H5I302hprJPAvFo4VynMZqM9HVRfRQ8sdWJ/qM3cT90L/rS8kAO8Qs/MYtAYQ8Wc07XS
+1/oaljqxVL8ARL0+Bw5OdMy93FW9ot5DbQolGF9L1MN3+T7j9ChbyXOtpiO3JuzIbEZTUyDGn85J
+yMtHaFnOx5V3pGqMUgR8eaPnsg00zUpfFHrzGku5SPKAe0Rf5HfO28fOwEacNYLp21rRQZarAZ7j
+YXF/A/aIWzWxXdu7hzToqmtkWNiL3wKBCjx/xsTRHLq2o2XjllN6WXtDiPjeeCJT912vy0WwS2ih
+yXiq9qIE33nYXz/CPanJUCPajgC1AmWM+LUgHK/fuNULPUQLhzek8iw9GHnqoS6Ywl30WL9sbpA1
+NS3oQSLY3HVYE2p/jMXXNeLwTiunSwiVqfDL5ki6VMI5b/GXEb1omQT9AkWqyh+xAmm7+OJQ3fhy
+bfI3Nv1l+YwuWHZgjb3bHwjOXCgPX7GblCxEMTbl2sHohjYcTgBT/3Gg
+=Yqti
+-----END PGP PUBLIC KEY BLOCK-----
new file mode 100644
--- /dev/null
+++ b/third_party/rnp/src/tests/data/keyrings/1/pubring.gpg.asc
@@ -0,0 +1,67 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mI0EWXDg3AEEAMedwkPY1lO/5eJSo7T/t2+7/bZk15AMDZ5yitSvL81l6wY9QtkAvf40dxrF8CMw
+DlDIi+X8w1syR/t4i44ZZYu3+LA1vRUnGXD2pAGRizjU2v7ZoR2ovEciOC2bWOEiFJdk9J15tDeL
+y191ney3TsYZ9bdYoBBra3UpJqFgtVWJABEBAAG0CWtleTAtdWlkMIi5BBMBAgAjBQJZcODcAhsD
+BwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQe8ZwmxXCOkobpwQAukuqm19euXuEE/cM3vMS
+/W5XoQ5Mutsuq9sE7f4SbTInLaAwot6sWfqLh/pal78dN0NoazadNFOGLVqaidM1vPcHnFW4iMkm
+nY9imNA1H2nIYXywWlacYJuJdCM0OzwM/VLLPXSzy/iNLCehGNgbSrtPdRcfwcIwgnu+rPSf/JCw
+AgADtAlrZXkwLXVpZDGIuQQTAQIAIwUCWXDg7AIbAwcLCQgHAwIBBhUIAgkKCwQWAgMBAh4BAheA
+AAoJEHvGcJsVwjpKChYD/2l/ektJfkwBbeqKBNHcCb/pt33xomA/sogAC4nByJkQ1UbjAzx+wwz7
+DyQELgWNYRFw6/WZ/OMYAm75ffLVoH0BAAgp+7spSMod7/rJynxsmUNRLPyZnEu2gVOqNaSDsi5T
+RCCEDieg1IsWDirG+17PD1w3b46OP1XV4izi7XtZsAIAA7QJa2V5MC11aWQyiLkEEwECACMFAllw
+4PYCGwMHCwkIBwMCAQYVCAIJCgsEFgIDAQIeAQIXgAAKCRB7xnCbFcI6Sl5rBADB2EI6KbcJr+NP
+G+k3ybLh9zFmTCvrkDmENRrR/PFgxDWbND8MB+8Od9Iu65xhUgAIRd0RtG/SH7Wl4dN0QOAK4x0W
+OqtuvZ2J7nTM24hi2lWKaHIZST729y1TNaSHYMhDGCXUa0wn2b8zWogvkgNB52pIePqxb48QCozn
+YYGdebACAAO4jQRZcODcAQQAvekEPqI9CZyz/MPugFY7tVv9VpLWLFA5Q0+uRPA0S0clBFW/E5Oc
+sbIZonRunLlA/Rc75E4Sxmtbu7kk+HSuP0hAEaweLUNiOYuLTijnqiIVdwz2Xp5NNbOn9eZvUt5B
+JN+07u1AbD54rrWaqBRW30Au6A6u7G6TCTfWHtxBFAsAEQEAAYifBBgBAgAJBQJZcODcAhsMAAoJ
+EHvGcJsVwjpKZJ4EAJlEDW7qetP78SOPjdf7V52aCfuasqJin7LaUB4TAAz5Yp2NRAwU213XRLDL
+ur3BEUE0uIfODVUuU3lhbSweWyHA6BH0tMFPTC7vWC/Zq/eamEQWLrz4oy/WBpVZi1goLW6MWt1r
+L5/hrJt9St/6IWNsK/w/DeFzHbRPF4Yz5iqIsAIAA7kBogRZcOD7EQQArLsczgqNlY3iLdTkvePy
+UaIQrLpXjoSyyftM/OBbFynqPVcUie5R07WSkhMsbiteb6I/msRRLScy71LNesE8Prqfwi05bqTl
+xU8GJB1fKaWuB1M1HKaNEM7a3bPienzPR80zX11k8BdD+TyAE3yfMKzOVf7v9FR79Y0rej/ZjUsA
+oNmnbzRTIXhMYQXR/p0ChSblvcGpA/9MGHbFU9c78nNsT9OBHfuV/SBN0FCmkrRHX/jeom0u6kAj
+58XA3bIQkn1v39TU0++5h2zr6B8GIOHIlThAIxmCuZaE/tXF5d6zqCyCCFsL0gC1BLj8UlXS/8Nm
+8Ydy2AcE8BJYbWPbQouTiuSlv0215B+JTqBGv9ED6aJYivhsDAP9E4IPn76M9as0CHl4btquKnqX
+jfXk7QAF+mLmVZgotD2z9hISI1W2VQu19jeQetLPda6Qk65k7/umWQi8D6ZqlNV0PnW336PH1f8u
+8VMnfnTt7ABFYcLEmQbKloLfZCF4s/65CVsYN2+cSggtaVYdZDZEZUwf7AmgvnmRsjhWEwmI7QQY
+AQIADwUCWXDg+wIbAgUJAKIogABSCRB7xnCbFcI6SkcgBBkRAgAGBQJZcOD7AAoJEB1+ilOTyZeo
+7z0An2pChTN7ZDCQImlH1gY6N0eaEjLDAJ9bjnURqyJSOURQNltbckktHmGF+7pqA/4r3ROm5FII
+GXJ8/gM3kbV7cJUgmdt4gVF1lvKgdsrD5MfOEI/vY0hUxayEBN8L7c/Bsn9nL6qgLMc2YRMNz4nI
+e0HJGW/Ers8OvSQtr/QRsiMmZKjCBzR7GsbrQs6OFfUAq7UKLleorWo36LMpCQSMCrNXDZObBXa7
+vVCrC6aII7ACAAO4jQRZcOEoAQQA6THC3fTRsTHdOUOTWTEUSuY9EKJeDug3FGSulfNDBbgA5qR3
+64DEax7CYciJeCKn+0Uw+HNTIoDpWyOqV+5O+inP0MT5+VwatxYeqEcP3mfNXpkZUeQsxJswbnsv
+SIrKLjxny3V9kR2J/ycE+YuvWOyd1P4evBvIbUg/BrAg+vsAEQEAAYifBBgBAgAJBQJZcOEoAhsM
+AAoJEHvGcJsVwjpKTIAEALOhKe7VP9fLQSObAaD7OcqXkivFbTgcaYdghVkUed5puDh8/v/ZP5uJ
+Eps/oa9k5i7ivbXBcCcyP2G/aMCGBVEVg/Bth3jqb7Eqr1cUBfgFs2ntFxMYUIMi8ut5TlmYoIhP
+vlq1oe6v+soc4siKypc4xXUitECMdYupwHnA+OROsAIAA5kBogRZcOFaEQQAkGRX4UkBAr8Sy2EJ
+yuxT92hX0rmnRY4luWFEYALwnHtjsGWcd2fV1FxGim7RAUknTm7FmwUkdg9sFYAsA3PmTan8qsBf
+gru4relznL+rJnN4rj9oYmJe9f4BA3AevvNmZCxzt2Iy1s42FhI2Zbxtqwr7pmbfSzY/C0SDDqpU
+cksAoKBeTvLaPzy3xhil+YSeGyjnBbIHA/0bIz8I4Ljq4LYNxeXuxU76FiVuPVUsPGOdkIAZEdYx
+g3hpA+yT3zJqvgT1GjSqeuzEw3yIVkSsjpL+FajFX5efhmPMOJuoBimSnEUCDYkiGS3mzeo9QYJO
+U4SQ4yxsGOal4xl3G/xQj5h7/t3Q5G6FzUOVTz69RCfAZ6ZO67p9dQP9Gh0mC3LNlWHhlTDfksT1
+Ddvs2svhu83GtLPuxJGZQH77WSJEi2nVWAe1Nbux93yv3jzzFSm0B1zTJsLW+QWEF2SRPEJHowAL
+/RZOu+WFbs4MmbVL1Sy6R9ncAf2jN4pB6Yv5OEW+Hn1xTc1qD7PG6sB6Cp0kMQ5kNL9ZPGNPQHm0
+CWtleTEtdWlkMIh0BBMRAgA0AhsDBQl7x2AAAh4BAheAAwsIAwMVAgsDFgEABQJZfSAhEhhoa3A6
+Ly9wZ3AubWl0LmVkdQAKCRAvyt8F/6UBu40IAJ9KY/DSmvuz0jHhh9Hq82fDrQwjLwCbBo3T2hDN
+t+mdx68XQ6AGDeEKEtSwAgADtAlrZXkxLXVpZDKIaQQTEQIAKQUCWXDiKQIbAwUJe8dgAAcLCQgH
+AwIBBhUIAgkKCwQWAgMBAh4BAheAAAoJEC/K3wX/pQG70S0An0iQvBGrwULpDUEKv0VHSI2FDZhD
+AJ4qj0rhQu65iAliJLXV3kfgRBBoJbACAAO0CWtleTEtdWlkMYhpBBMRAgApBQJZcOIjAhsDBQl7
+x2AABwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQL8rfBf+lAbvJsQCfdDWu1z6PfYp1RqwM
+y8dPfvuB61EAnAo2ZqfwSIloMDIj+fSLLPjOr8FOsAIAA7kBDQRZcOFaEAQA/2KSo7bsUAtIMTG1
+Y2rLbsfVFjtb4WI9bO/JGwTOsJ9P9yCLKdL7U2bFQlhXHC8TZYkJ9q1BZ18eC7HVToWuyO/J0FAS
+y/73BJjVRVwVQUZGKJIrhEyANdIcK0bTnLTQzS/Enebrwf5GxAJaxbF+w9F1VOrlMpCqdI65m5IA
+8ecAAwUD/RAkyaPFzC4MoSfoYtxtho4kviWU4SYjgjMG6usTBQUMF992F+d9fHUIRZpyHiI0p1K6
+pHE4jQllEdOQCC/mBpXkIHklGrU5Q0bnybod/YmT55h5CMjD1XHQxS4v4qM53HKGwhuFWoYA7oOp
+OrBDanuSFkbRZB0l3qp960qGBFOkiE8EGBECAA8FAllw4VoCGwwFCXvHYAAACgkQL8rfBf+lAbvg
+FgCdEb+zLoVdQjKfXJouI78wqSJrbeIAnAo4wlgDWNGZ/KVWa4X/CFdxMBVMsAIAA7kBDQRZcOI1
+EAQAzndho+iMhKoZYsa+leoN4rOcQI/hT12BQhZa2cG7GgosW6yjAWI5iAG9Yj/j7tDXvJoFyGwB
+fZ16QFe7W6PjfSdhw5sjtuwsmJ2C3GJI63pI2PxWgKikqaIr1fnfdAYAsI19KBEj354RmRrs7kRj
+XC+kmUTyq0UEAE2q7N+AHLsAAwUD/01QFjQlR69N3H532lNZ/Qr/pEoIPhT4kBcvzNR9AnC0ZvGl
+9BMCRD+B28jS+N7bI+yRvFqVuO3fEdUN9NAO9dK4RK0olXr17ozaeIaFuBU4xSLgnbikS5RWerBQ
+n8jHXHodRqH3+PXn0ci++JrY73ho1iG0zVh7x1TZO97JvT3NiEkEGBECAAkFAllw4jUCGwwACgkQ
+L8rfBf+lAbsH5gCfcwC7T4ham51ZeOs+8zuYq9F0RTIAniQmTM2k7QshZYNRCxHwW/DWskEQsAIA
+Aw==
+=hqEl
+-----END PGP PUBLIC KEY BLOCK-----
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5f674c45a78119311ff97cd56eec776a10f7d31c
GIT binary patch
literal 1105
zc$_<C%o4}4Utf$<n~jl$@s>M3BO|+m`iFGhlXEN-ng4Cwemds7tm>~R3?6TCYu#7&
zx3QmSvOg>6n3I{DT4$38q8y6y3hWL|U=bGMV6YHVWGQ~jGf{H=iAT5ho_)#^=rAqw
z|2-R4CXg*m(#)*foE+>-Y@%G89NbJSVoc18Omd7&;tdR3oB}Y#DsvhB`$etSv+{F%
zvQ^jaWBRd$OV-YGkh{OOQFixLmIaNX<pzul`!4-o_by}8`Q+nyEh{^|NqVqL-IiE&
z=1+gj-nV+GhFs^uS>o9E4hnH<t1vS&aRB|r#LN=oZ({dcv}w)zV15zT3ZMEr?=lZx
z<nHG$^sajx>sPSUnSa~zXv5}^6b7*c-#0rlGrI9{xn@6KXKWw&XZtS0<IId4?1w5?
zB!FI30DJWeHm?G$Ws>GW_R_)yj0{`;UOzp5TL12tRTtE^t&KS6_$Twlvw*t{D*Bvj
z4mw_TX830`Z_-QFPTg2<{@B+q0+u;*N$c8OUwAi1=a_{Ovx)=rp;8tNpv|(lZ9dEJ
z0?FDJZ~xasZDFuEt;O{I1!t(alg9gyT>F^w{_$7&Z@*ly`Sw*VO=gCFs<*WrN^UHg
z`0CgU8Ix`K)=#{mPd9&9cJpel*MTaFUChU@rV(8baB_9`^iv3M4^Y67Mig+R5v<Oq
za~g@pkw*BzX#^(5J2(<cfXZD4V1l7mV)^}dp~-*o|KAYSM!jYDU$f-}?!=<W#msP9
KRr?>_!~y`Y|Fziw
new file mode 100644
--- /dev/null
+++ b/third_party/rnp/src/tests/data/test_fuzz_keyimport/crash_e932261875271ccf497715de56adf7caf30ca8a7
@@ -0,0 +1,3 @@
+bmessage.txt_4This is test message to be signed, and/or encrypted, cleartext signed and detached signed.
+It will use keys from keyrings/1.
+End of message.
\ No newline at end of file
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6510447f44c0752df53b31eeefe24d6b432ef591
GIT binary patch
literal 925
zc$}MJ%@W75Utf$<n~jl$@s>M3BO|+m`iFGhlXEN-ng4Cwemds7tm>~R3?6TCYu#7&
zx3QmSvj5MJ%HZx7%DhF;F()%QRlz0^L^%}Y71(u5U=bGMV6YHVWGQ}oW}@W!6OV51
zJ^Pd;&|zBU|9dv9Od#8sq?uW{IXT#w*hINFIk=fv#F&^FndBIm#2Xm6I0ayeRpv7M
z_lsJuXXWSkWUH>-$Mj<hm#m%XAa{RlqwMahEDIV%%MBPA_FekF?p?;F^U25aT2^*^
zlk{Mhx-GHl%%A?4y>InW4Y}qlVu_pC>?q8t-N?eg#l*}KeB?uSLh=pO6)s_Bw@>}J
z`E{Po@vg;l@&xmu*PMC~{>Jg-?S<F>bACzI+`c`oaAo&=yUlx&Q*XUYn(tX|7Uh@t
zkDZxG)o`-MHHl}+Z;gWEKZqRqz1Ds~U{^cKL1octa|4Q(Z_2oxo#9@6!piNK$CpI!
z)4qA%cBv(9e8F&M$x@3eiBr9v3q>ECtuRO6(b;Q{W5srKyl0UBhMEHIPy_maNt%hF
z5fM6N3M`Vs91OBRJ?Ecp5ZU*{cCO0j_ZCW@7l|KuVtE>(2NawzksVx&3@74Qeyi$~
zw6<t_zgc(c%T_)6kX^ms*%~ArRad>;@!~xrL&Luv{}%^29nNsQ@~m@i?WT}<uG^P;
z{z+Uo$;#wI2CIT92g6_aMV20W*M3;llD{gwuJmqf_+5ryv!~A6e%-ig_w&!+r!o95
zUd|Y{{nxv<jor7NhS~AFwonpck2~PASzI)UKV)}4@0?XEacq1Cg*dfUn3<V4fQb$m
zF){uocF#qd*1Qkq7jdodslW3s^YBIPe*Qx5y2r781xuazw=Iu0Yz|3b5L@tlvm-O3
z8y}Zz_Vabd_K|<K?=n2j%*eqGiX4L`YtCJlF9*GC^<TDS&u06}b2Z(+I`Fu3tT?UZ
z?8f_=eR;%BS%sayjH*k2@P3VdS7^W7f-&fv8TWe=2X4c=8jt5@#$DI!Jh^MyK6l17
zzdI@jMGnv>Owv5aNq6A_Mushaub-Yjt$%mSstfAd)<&Fj{FC|OS-@Qe6@AV%2OTdv
oGyF4}H|Zs7r*5n_f9&fQ0n41Zq;>7CFT9(hbId}CS;c`F0FYjZW&i*H
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8bca1b0bd04b7441a066eccf620dccb6e5d2bc64
GIT binary patch
literal 925
zc$}MJ%@W75Utf$<n~jl$@s>M3BO|+m`iFGhlXEN-ng4Cwemds7tm>~R3?6TCYu#7&
zx3QmSvj5MJ$`I=3?YBkHF()%QRlz0^L^%}Y71(u5U=bGMV6YHVWGQ}oW}@W!6OV51
zJ^Pd;&|zBU|9dv9Od#8sq?uW{IXT#w*hINFIk=fv#F&^FndBIm#2Xm6I0ayeRpv7M
z_lsJuXXWSkWUH>-$Mj<hm#m%XAa{RlqwMahEDIV%%MBPA_FekF?p?;F^U25aT2^*^
zlk{Mhx-GHl%%A?4y>InW4Y}qlVu_pC>?q8t-N?eg#l*}KeB?uSLh=pO6)s_Bw@>}J
z`E{Po@vg;l@&xmu*PMC~{>Jg-?S<F>bACzI+`c`oaAo&=yUlx&Q*XUYn(tX|7Uh@t
zkDZxG)o`-MHHl}+Z;gWEKZqRqz1Ds~U{^cKL1octa|4Q(Z_2oxo#9@6!piNK$CpI!
z)4qA%cBv(9e8F&M$x@3eiBr9v3q>ECtuRO6(b;Q{W5srKyl0UBhMEHIPy_maNt%hF
z5fM6N3M`Vs91OBRJ?Ecp5ZU*{cCO0j_ZCW@7l|KuVtE>(2NawzksVx&3@74Qeyi$~
zw6<t_zgc(c%T_)6kX^ms*%~ArRad>;@!~xrL&Luv{}%^29nNsQ@~m@i?WT}<uG^P;
z{z+Uo$;#wI2CIT92g6_aMV20W*M3;llD{gwuJmqf_+5ryv!~A6e%-ig_w&!+r!o95
zUd|Y{{nxv<jor7NhS~AFwonpck2~PASzI)UKV)}4@0?XEacq1Cg*dfUn3<V4fQb$m
zF){uocF#qd*1Qkq7jdodslW3s^YBIPe*Qx5y2r781xuazw=Iu0Yz|3b5L@tlvm-O3
z8y}Zz_Vabd_K|<K?=n2j%*eqGiX4L`YtCJlF9*GC^<TDS&u06}b2Z(+I`Fu3tT?UZ
z?8f_=eR;%BS%sayjH*k2@P3VdS7^W7f-&fv8TWe=2X4c=8jt5@#$DI!Jh^MyK6l17
zzdI@jMGnv>Owv5aNq6A_Mushaub-Yjt$%mSstfAd)<&Fj{FC|OS-@Qe6@AV%2OTdv
oGyF4}H|Zs7r*5n_f9&fQ0n41Zq;>7CFT9(hbId}CS;c`F0GoV?wEzGB
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ff4ad28d87c4b7b5f0581378ffc81ab8346be66d
GIT binary patch
literal 2017
zc$|%tc{tRI8i0Q@^RtYdu_jBV6Jmx$L#45GsAQWnwmC;e(<$4GN2aVDNtrQ?#FZG6
zF_oOS)QriNWGl;!>>*^0Y}G*+#;wkCuXCUKJomZ(e}8=M_q^|Sn+f`ay?YA*ftTz$
z`!~I~n#nPbyQN!Yl&Z}qq`p)c**oFM-6bZk&diEi+w}w0BJ+zo8b2aU<=mPWlN<E}
zGe==0BSqs3qipq&MYW2)M%c<d$W4Z2_kgHBPpjamqn;&-<vpsWoJm?@N0nk2t*5B$
zxiXg*8p~zg9EIRus^OkIe*vBjC_(^8DLnX0lqrfDNHV3>fCvZ_K#D=XEeO?wNl3#b
zC16m9xQZkcE+q}BLSYc7HUz2`4=BJDVqS!?RO${M7lQ-9)|Tc25U4!ykafzhA|?^>
zMP~R9rA@L|o}6>#Ai8i6oEIsDn(3oBrrhBopLIskx^HHU7RLTu@By|t?i$KBo8D#d
z^ov9ezMR&)hkYa9_G=5Bz$RksBVW%Pe-=FgX@2Oi%%(@Hbp*e4m1(S>r~XFsc$F(v
zO{ccbG$H0$(NgY4>Nd+hTE$;j=KeQ})HaKhDr_t8x9AhcE;&O&KO}=4Z{amFWpSUl
z{#YIjkfzr*WhwOhI1W2}sa{4XzZ*o0F^P&{v&&f<<LXfITHNOzP8<Y~G!#}DI<;sm
zt~a;DhGf~h8*gOgoOp5>_t4_r6&@PrpqnVC&*^5U%I$q2^lO*@8AC})!Qy+yjK}6<
ze6}gf{)1xW|EBN@27%f)b_Wg1;3ea(nlqSY-sqQozd9SurdlOh?C99|<lkVyGRMkD
zEXrN%M9=0BJpjq0LyD!8P94Gh(XI%acT(e8ss{_n)ol7g#6JdU0R#BtbKx4tj;wq^
z;VkYY`8V0C|Ii)mtlwX=-<M{R2D5!1<P^F7m~ulw>iT=)Wp-5gzcsI(0grI4;W_+`
z`h|GULjuC4XIihZo0Yw5vBO966OKQC_!>ld&SRbiH-el~VY8`rXu?J5MKGbNV)tr9
zG1kdOu}B+bA7I5`xEQ>DsHbvKX2tK$Nelji<td8Cu<h=V(uH}OkRy@Bg6sli&k-B+
zv|RBVIUSDa)qZQMEd5<`+I>*{>m}j;a$DxV4JO?Ifh-4E@`vay;V0w`W}ncDoedXe
z^SA-Iya6|D1R%2(z-BtgC=dS9=kTbtvbt8$*1SBaO%{*FMG-@dwF$ePPb)5!*19^Q
z7rjmT3%_LFc2Gq>UA=44E1u!Q@HRk^ulo)Ko8*obvEq)8tnWG<Vzhyk9X+pA>Z*2a
z|I}Vu1%&(UY(hm4D5~1oCdJHPj-k3Is>eUOa}|mzy;2f+%|hpmcLX)V57u7F^ab}W
zL0THZm~7-n;bOBJ+)?^wiyp6{YY!i(%&kLNk?&15x{B}CI%RmjSORCUGoPu@HZa5=
zi;it~e6|17FQkuZ_G^dZ5wW?ZMQwP|;?hqs%S@waY(Mh^!0CTLF~{wSbS8>*h}h7p
zdnT)Ee}`>3t6ylu^&R?rKE$=_vX(GoQ}^U+xBRqH$5X4r+)(rf8|3@O=R-V&v^dI0
zcX!ufMN-J@w2rv$Xj6uz4N@ba;#Q)lr*U$aUtpXdNs#UY1Yr4w2flNw{?|`Q;cq0s
zk4oO5rw8pA>B$rKYfcI#bm%UeA8R^bGhFZF)esrjCL%#T_u2L3LUYNeNb*4O-snu`
z^29tKwjMBrzim)z&0kScLgJs`r3EXP)acH3YH@0MF=<hl?<4tCK0oMhJrPH!Wg~ZA
z_H3a);q)&>%+CQwiFFNG;w`rlMo7CN`JxIqK^;TMIWCC`^wc69a5&?vGY989i@M3P
z^hCgE^WR;P|L&3ystNt-57!_b@YpuWQ5V!ugaYD!j(lrV>ryf<y*aaB(Hh7Nvb(xJ
zhGd$m7wFh4eh}lBtz=dY<PuV-iiLU}Rt|0l2!!xsC~e|ZVPz0ZWHf}xpYV{>2#*!P
zFgFD;VHvtvgOQirskc6JF0|B7ylhjrv3T0)r^cd0a722(y_WaeS(Aqx<Ch1B2-$Xe
zQ;co12Bo36U9NgJs$@x#hulLdsFN^{+3{l5t}R7n87LG;p}h+J9)y|Z8^XcsQhYDj
zbe7nKs%p1F>3kaUd&#2)UrV0ZS2wiO@p#{mm3_L;KIP+6)(TudxoM0tsW{^gd$Dq0
z>fXiK_=aHZI${$&QtyHAsOjo?oL>A40r}8t!DZdB{dL3I+Rr=6RJ98n<%iF{^2^e2
zpD=D<nUZ5noRVA6htIZB<8ErvxAeg^Mv3<vyIZ9FRI@462`m0ccU|~r$+_Dlj~aX{
z`7fW(IUWS~d4>x;*uGX<Bn!e3TD4@R88wDl?hH)wzv`nLH5^!O87tcA$Fk^3ENos0
zX$n17EvOCCH^UNZ&ON^-=B{$(V`x(32g>8UC0@#6t781Xc{PN(+ZDafvr#TSw>dP|
jXTE~Gg_8A_onuYOEt!^$RJ~H0>r}??&!e8NPPqINC;WJH
--- a/third_party/rnp/src/tests/data/test_key_validity/case5/generate.cpp
+++ b/third_party/rnp/src/tests/data/test_key_validity/case5/generate.cpp
@@ -91,17 +91,17 @@ main(int argc, char **argv)
     binding->hashed_data = NULL;
     binding->hashed_len = 0;
 
     pgp_keyid(keyid, sizeof(keyid), &tskey.key);
     pgp_fingerprint(&keyfp, &tskey.key);
 
     binding->halg = pgp_hash_adjust_alg_to_key(binding->halg, &tskey.key);
     binding->palg = tskey.key.alg;
-    signature_set_keyfp(binding, &keyfp);
+    binding->set_keyfp(keyfp);
 
     pgp_hash_t hash = {};
     pgp_hash_t hashcp = {};
 
     if (!signature_fill_hashed_data(binding) ||
         !signature_hash_binding(binding, &tpkey.key, &subkey->subkey, &hash) ||
         !pgp_hash_copy(&hashcp, &hash)) {
         RNP_LOG("failed to hash signature");
@@ -114,17 +114,17 @@ main(int argc, char **argv)
         return 1;
     }
 
     if (signature_calculate(binding, &tskey.key.material, &hash, &rng)) {
         RNP_LOG("failed to calculate signature");
         return 1;
     }
 
-    pgp_key_flags_t realkf = (pgp_key_flags_t) signature_get_key_flags(binding);
+    pgp_key_flags_t realkf = (pgp_key_flags_t) binding.key_flags();
     if (!realkf) {
         realkf = pgp_pk_alg_capabilities(subkey->subkey.alg);
     }
     if (realkf & PGP_KF_SIGN) {
         pgp_signature_t embsig = {};
         bool            embres;
 
         if (!calculate_primary_binding(
@@ -135,18 +135,20 @@ main(int argc, char **argv)
         embres = signature_set_embedded_sig(binding, &embsig);
         free_signature(&embsig);
         if (!embres) {
             RNP_LOG("failed to add primary key binding signature");
             return 1;
         }
     }
 
-    if (!signature_set_keyid(binding, keyid)) {
-        RNP_LOG("failed to set issuer key id");
+    try {
+        binding->set_keyid(keyid);
+    } catch (const std::exception &e) {
+        RNP_LOG("failed to set issuer key id: %s", e.what());
         return 1;
     }
 
     if (!transferable_key_to_public(&tpkey)) {
         RNP_LOG("Failed to extract public key part.");
         return 1;
     }
 
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..54697b2b844fef865a79918e4edcb76a6723f6f7
GIT binary patch
literal 1702
zc$|&WX<X6?9>?)N@K-_cK)lF7O`OaWbW9>EZ)dEeR2aP2Q8AhwFYx7Psc2KKjR#?-
zW92~*mAUCs1CptciD_D!PF|&^=GCP}wq6P1ZtZKgnP>C-_u%*WzTO=?Fe3T+GYAOW
z=PUf44pLiYCMlQdHyYT@>o=KCijn#kQVRR8J3l%+tL@=C1jsdRM>n;<!?_p)cXDUc
zZFJWFRXVo_bC}z(dV?r8wZ|kcUxu9F?(84d3|++5+zznaiLVTwur)cyT;yleleiC~
z)A(}@2RkrJ4dFtggoHGQEf-^Cv;qJH0U(bM35PRW@M&>O7eyn8hCl(F7WCtTP)nGO
z9s;fdgF>`T;ZTIG9%u%IL7-L;sQGa~7h%NdNUk(h?AmX66$F|BuT@4zHF9#%E6A~b
z8LzX#E*g-VMhLYd;Jn5-$<-b&bP<i!MXYi2)*{cfU`c*IB<$e3QGdoqRuuFRZ?EW7
z(Qf5;ZQ-AeJ^vfgI<AwEbt5vgDyFg^73a2l4|1LI&?8m)aFyp|f6?Np<^JZNG;`}#
zg-Z_SPTitFUHrM?t3OoC{pku_GuTGlPnm)Hj*%frZwkT3&k!xI8?qNfF<z2<K(C;+
zv)pLln<?0yV{J&4p&dxj;xSOkvrFabDGMm;L-zXtVKxMSJE)o+_CCNW*Sa2LC6@bz
zIH}940&kw6UL#)qn}k61x5+iI7xr__47PQsqIwMfLSh#cdC>+YohIiw5uYnu|4^~~
zrz@foK%n)h?=FW1#Pz8l%h#kX$;8T6-;*6@i`{dHn;xqdV(t+u-Mo-GN`s@lgxM+v
z1%Q7&imGS#TFb6X^rd<~*PX(e?W$$T`2}xN|2dK$J4`G5AqBI~b9n_%C0@>p>GZSs
zvOj@rKh&~4GT*rb=JPUsZ~EG=?9)cNr(QBn@H4vq4_y8l^b~m@s-)_+x5q<w%joN&
zC4)}E?tVdH|95VI0bhc&7IbOVuM&(Xl~vW~y>7mH`k-+~0=>D>ZZ%cxwbvU}XNC8R
zb?0&qZhd*p*7PWHIqJdzqV($03pQoU$8Nm-?YwuQXS!HcQDYK1?oD`QAf98DB1nT)
z&wNbuJ^@z<K#Tt@iTIyt<nQ-lUI2k|e|^@Q%s$lu?lkX-DvV!&P*q4ou>#3(uoW6W
zeu(As{E;Rj-#zia@vuqWit=&0mDjCLBTzFKNlsSu&39j+7VBGs$OL7$^HA+d#d&`-
z!tK>d&V$-z5!~>tc-E=N(FA9~L|tX}{_(ZVhY}stUiuS1V(Wv<f8PFLTYmT5$&Z7n
zP#{p({6)7euZ%Z|@1K@Wy$=x?)is3`<x09#Wc72pxx*WeFq49}A$_uv60{S`F^Vnn
zs<;<|w|7-i9%Z)f@oW+(@b0Y3GwngzcGzf#D`Fig@r!DnQTOnubTTumC*aZc=<k^C
z%>6#>IgZW}xYTviG|I(qIZHgpOs^qcARrvN%66k}PA4<89&7l}lUruwt$&AYkS&h1
zQ=ja9|6^iM-wCX$Y~AL-Z^6|i^#ReVW1=L&8*kjp_Pe7Jqmpd)cz=JNvN11l_La4^
z%|vI}PH!A0r}1pAW}tm$Oj_fV1E=ft0y6NHnnLT{X#0L4FXgEY_|7;y>ClKTx3qBj
zO3MM+v~|Hj;WsUhVXsBug{z53*g87LP;lqeQmq?2Bb_xY-j;cew=_KuXsyM*pq<}Z
z?;%|_HpbCz()47@q~grpo-}cBshFu$RY$;Y8&=2vGLY(-)-ZnQ{=f!dfogv&b$$-`
znxVK?uKnO_&Ny>(x>VDMpj(jGRr}%ixKJ!}hyP)+^&CQYC*zD{XDAw>xQqY9$nX;*
z71R={g#bibj{}s?m;!7-3<?Tp|M%o0GwiWKYH8QG8l?vyi1$6Yox^k~wv7uI)ZRr3
zs4#YI0|fM<G*qoE#oa%62c4d>4{yadscMRcX&gt<)zcI>CM8P)Bb||PlFMw$N77G(
zq@8^)Jo2Dz`hK_3Y2~54-?rD~g5!GgJ*|?T{_1>9=yZPv1Fhdv(8=-X!m#g&dko}u
z`0I-(32qCsMxo=z+0-%X+g)V31k}oO31Kz=h$p?0Iv~JPy3#QH(n_r(&GO({z3P12
FzX4s<>7@Vw
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..da34c19d4ce6052791144c398b72f2ca38070624
GIT binary patch
literal 2006
zc$|%sdpy$%AIE>&elBxouE{0qgxE+l3L`pHa@(2P&XH-I5;iYcxpq{FS(7*tv&<?v
z@lcy3B+Mn3Cn{73x#oU5?qi-)&+B=5&U5}b|9}7ZzCW+`yO9C<hJE@70fCn+fy0M)
zxa!d%&wHiYWt7UTr=<6(GO~BWlRHaHUY(v2w>jJmREf+l?r4}tn##F1(MLDy2xd;g
zNP3FKX?oe(V~Z*keT}e{`;c37%g!EAcb-<kb0<AZ6w_x=PdSsc#EL4#&|6PZS<_{%
zjT*~kK5T{HV5;GsyZ}C)1}H)R$SZj8=_peaHHc&?C<hS`D1a1$et8h836qe9OG?0?
z5OEbrC|pV!RE5GIP;CfQEgq19E5tO0F;xTyk7+&xfr{ggm?!<qV-gUnGX1|RZIQk6
z<XkFx(S^O>j7TZeOdrKI<@6W%ZqO4q{4%SxFb-#f53tN}*HM1iv<{2ss}ec*XGzU_
zST_UjytdE@Y9huy_VdaKVA3Lx=7+3hwme&HB6zK93}gK~^$(iID&44RI<*4RD={yM
zmU1^!zftV_6UFr3rjSyFZ3q1lef;Pp7f9%2GWg~ryn3Q6Zh;ek<t72rwA!XDg|6?1
zVb+)GWQ6j&L9`fys2Dc2oV7Wu4kfS0Eq1ZvAb_Nyu+q@EMQd)Oxg|Cv%fZ8VGb`u#
z)62L=7Wc1m(Ktul1UY?nCtX!;Z==w^UH(T5B_#!m?;0{5nu+oKMq&0Rij}`j;U5eF
zwI2>2G%SOc47+JgV4AsutNVU-F`7!XO0d}RW^*B+-hye4m64c}yZ9D8l|%FdB#-nd
zmQvp8@E;6zMA&_j8rD)hSV*p7(Pkt5*_#yDgHJviu5r|MWfg_9xStr%<e>gtXRwQY
zclCb1B$G6l{pWMeksD7aHx;CAd?sFIMYa7`@Tv*WmSY3Y;ceE<#(N#&6Sln4-Wj`F
zIk*)&&YK^1`VPcbBhqu8a5Xp)<eYN5Eww`<u1bx;gv#>WYZ1j*XFJ6rZInZx6`k&C
z@cEIR%0-zK|GOtFcn_DyD4zZHy9Y{VXY4|3Ba8Xj1<GCncIa`r;%Ra^9P>{5qrI~9
zSHT6jp!y$6!vABn%>V91x(fnXj<V#*=nmlm@+RZG(3_PF7iM!gfw|lscWne9vmVG|
zILavZ{`B7Qacf0Yt)jj8v&1%8JQ^283^mp!?5Y`8Tq><~b3xDfm~<DeX5VpCML%D=
zXYx)w!<X)3fFj@U>kBr?9V}wT9UIu#bt=SY6DvD-UaQni?fU+)y-95~Lth3HDuO^!
z<<2%KMh0UD)j3i%yy(GED5~&IN#HgMT{b@v)C?zWyp?IQ9v%F&G=wqP$e+T&X4iY5
z^v&ixUq#ni+g9WXP*&voqYZB2yR}XlJ}8#JS?tVbD75wT@rI&f+nrwRKlu}BUd>_M
zIvx?5Yg*KX7tJmG5VOoMipF*`jsxuOhZJ+%u1FW6*c%ZGdTq~WRqb!EZ5Q<m4Y>D*
z7SD&cbzIgGW^C!6c<r8_R_b(et)CN$p0q=LZm8+wDx}3x20A-C=E@U8rp9%|bqAX=
zEbWjQSITcEh`Jg^`*{V%S0oA2Zvj5|Y||6}cDwH9g~ad=65za&Pw1)M!}Rpzkq6Z$
z_#-+rSN8YSZ(tJ~pX9X=8Q2!$ihS;i8_R{}l2MW5p5ndHnT+L;89;0!a14LPpwx!9
zqNIexKgCP)S1_s3Z`-NGsp-X}Ibpu9<a7D_bH8>)*iy>|?!D~VMlay>FGb8u14oF0
z`YiF5+gAoiyCQj_ayUU9L&-TNi3;-4A{}r%?V>XcXTONL#kKT8zy&p^uNujJ)kp}{
zgo?odjzK)&`AsG#T~I?23W)#v<cnrnmy&Vm&6x#rHbCyV!`Jr5kW5qcf}GxoAH+Ci
zE1A^+xr7v|VxgX=m81Is0wMe;N}G61SaA*}GU`L*k9bOIgvW|tm|OgqungU--pI=y
z)Z2^f3oUgcFWVGu&Yg1pp`j=N9FU%AujT%7#^e#(_~ij2LbjdO6l344L8&ipm#f;1
zDp^wGBKMFA1QO;kI~u1Bx232o1BHAkw0FTj&SA!RhH&tP6wg~Wohf#qvdX<sIzI{d
zHR3^oKO&ykC+J&x^JHJ2l|#DkKILO$HVPbnxp9m#sW{_z2eD_s*!_!B@%6#l0%8*_
zQtzSgi0RsSoL>9{0r|*#)^)?M{dN8N`j0!xRJ9A7<onOO^3T%n7%{GAnv!EooReG7
z)@NF&aksQ++xp--y~Jml)hW_`uGtjoj1_;ZyCM8b#DfO^7qLq`2=MX@XS=ZPTkVld
z2wP~?l9^`I5Nf$IC^6uguX0p>&@;<e(RMeMNmF8C^GZl#=%FfpZJ53pmRNnZ=DL`N
z%GLSM#K=j?lf5P0%3^C`yr3C1gu44xy~U|0SKmAAB)1oS{Jhzcjh3B5P01~pmJL+B
RQo9>e`foK+HEScT{{lJQcUb@c
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..49fa1c572f732f8fe96c73453e09d66bb2d66f42
GIT binary patch
literal 1398
zc$|&WeLT|%0LSs)wjZ_;V|iFwWSzB`Br}N=mC6cR%hMfJ)8%nq&I&_uI%ZAcB(XD+
zT%5YGge3E<L!Qb*$kX+J7UwBry6*ny)qnl}{p0(7y*`b6&^_eUO9%)w2_*J?&uKN2
zW7Jz^>*cJfwFk@>DJmO&3r*^|x3kT6O3}uyAE=g_U)a>}0cWb}+{~L?t@AW<kcIP-
zF}}R=<@=;+O*|%KF&}b`XW26#@6W{+K5{UyBxbpd8f?s9&I=;S$h@cCY{7K-iAKyq
zxtkCb5WprF=K6@~TtFQHK-v%iz7eKGwja|}QURhNPynX@{d^Fr3sX`?z?EQ7h@vJO
zia;uZT2L4Sx&;E&z62l<C{ANYmZrq&nC=}AsC2xW<?U6$iAOJ~4F99C7VMg<N~s()
zDH;T4<QhR{c%slWZ@Acfg_p46kx_#s+n)=t5SY_`A$r{8c9R}0DP_|iCbk#~uAaW}
zjHKt+%!s=0aVpy<iyMYB-)pV1Mty1%R{C_AZ;a2?>C-({<;2$3tCg6>aUK`X=d7lD
zQSACkG5yU7q!w)5?_#9m(HIIO=v@-n`4Um{wmf<^&&O7j2q<%Fn=?_phsR;oF?A|3
zwQZn@0-vECGPRJoI<5l^{un*iD~yH!aDuFgaHJJ0U1@2J3e2>3F<#Bgc6<;^yGzRd
zS!6;xs2{J27xwV9RChGWygJmrC$o~1ZRx#Z#$z)a_b&>wuM~^ltndl|f!aPhD?&Nq
z-nf(QTXKtNbZOUFiqTZco_Nxx&ed6;dQz6Tt%{OV^+K1)R5pVOzz+?nm$AC^#J5Mg
z!}h;Ij$^f~ih`>J-1lLB4JMu*peLOR#T?zYxJ0Cp@)LZT?RCEE383KnYj%4i?o5Rp
zc<p~AeC1EpRTT2dYeuXfqW#I(=fY*`AW&SjxgE(*=Z_J4CaTBhT=G!Gm9EM0q81rt
z^_8bK;hl}^M(%r;ZgFZV+Bn$Ai<M`4v)+Y>H<MD^B5SSpRpv;DdxG;P8=MrkVZ8~r
zOO$A&%>{f^`@m4?SY%X(L)&ieADADs?LS&yLPzD87Pr&o()p8|1-?<FZ9m@;5cc0;
znbWq0Qy2=Jasl+R;be8~@33`>&iMw~i@kF{1v+)dVrA)T`p2I+7o?UscrOp<1)03t
zk9*zlWJrWcjb@GX^mI!r5(1~*=qc)tHm6(e$6?|suE)!J8zzTK3ytI8p2}T-7<{-&
zrFX5@ot;ex?Nb6jXt)KP8MNc2B~9F}IWC^i<DL*6uIYrm&2vjy4pf1yq2ttY9$#4~
zGKWWm2M?6&h|J(GOw0fZE2saa-`HMeQ@W_3fuld5D~lJ&DUn?r?2?qU5~fsE;0}MJ
zR^b0@Z`eL|`N*xN-gT2%8a^g$W*Ruekkn@?wqB1LVQvjCl~*7<b;zvjV{oG1DJ;|C
zpf5#l8X<ffaZO}-3XPCFA%Z&p2B!93U^1vKQ~?3xZNCIizr^IA4`S4zfa3p?&zWIk
zl4xly8HG|CAjjYC@@@{(G{wNr;klv}+2N*!Ssjq$naox%GNA4`=xpKX8G4ksg>hL{
z=?{||4WSDrsBlbZlpIFBCgy~s>t_yz$GWhu&k4`B)=f0EqpnKN968xg91o5t&vev^
zF8;XluF$y2f`Q)9!ENRoXu+`ROFC4mw-N8ntBY`k%tDEhIcHPjlwEtW<^oV8Mw+-5
e{^3u4Q%XR9SCFNy8`82A&R11C7bzDc;ywXubz+(T
new file mode 100644
--- /dev/null
+++ b/third_party/rnp/src/tests/ffi-enc.cpp
@@ -0,0 +1,897 @@
+/*
+ * 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_rnp_success(rnp_input_from_path(&input, "data/keyrings/1/pubring.gpg"));
+    assert_rnp_success(rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_PUBLIC_KEYS));
+    rnp_input_destroy(input);
+    input = NULL;
+    assert_rnp_success(rnp_input_from_path(&input, "data/keyrings/1/secring.gpg"));
+    assert_rnp_success(rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_SECRET_KEYS));
+    rnp_input_destroy(input);
+    input = NULL;
+
+    // write out some data
+    FILE *fp = fopen("plaintext", "wb");
+    assert_non_null(fp);
+    assert_int_equal(1, fwrite(plaintext, strlen(plaintext), 1, fp));
+    assert_int_equal(0, fclose(fp));
+
+    // 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_success(rnp_op_encrypt_create(&op, ffi, input, output));
+    // add password (using all defaults)
+    assert_rnp_success(rnp_op_encrypt_add_password(op, "pass1", NULL, 0, NULL));
+    // add password
+    assert_rnp_success(rnp_op_encrypt_add_password(op, "pass2", "SM3", 12345, "TWOFISH"));
+    // set the data encryption cipher
+    assert_rnp_success(rnp_op_encrypt_set_cipher(op, "CAST5"));
+    // 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_int_equal(
+      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
+    FILE *fp = fopen("plaintext", "wb");
+    assert_non_null(fp);
+    assert_int_equal(1, fwrite(plaintext, strlen(plaintext), 1, fp));
+    assert_int_equal(0, fclose(fp));
+    // 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
+    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_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_rnp_success(rnp_input_from_path(&input, "data/keyrings/1/pubring.gpg"));
+    assert_rnp_success(rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_PUBLIC_KEYS));
+    rnp_input_destroy(input);
+    input = NULL;
+    assert_rnp_success(rnp_input_from_path(&input, "data/keyrings/1/secring.gpg"));
+    assert_rnp_success(rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_SECRET_KEYS));
+    rnp_input_destroy(input);
+    input = NULL;
+
+    // write out some data
+    FILE *fp = fopen("plaintext", "wb");
+    assert_non_null(fp);
+    assert_int_equal(1, fwrite(plaintext, strlen(plaintext), 1, fp));
+    assert_int_equal(0, fclose(fp));
+
+    // 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_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
+    assert_rnp_success(rnp_op_encrypt_set_cipher(op, "CAST5"));
+    // 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);
+}
+
+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_rnp_success(rnp_input_from_path(&input, "data/keyrings/1/pubring.gpg"));
+    assert_rnp_success(rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_PUBLIC_KEYS));
+    rnp_input_destroy(input);
+    input = NULL;
+    assert_rnp_success(rnp_input_from_path(&input, "data/keyrings/1/secring.gpg"));
+    assert_rnp_success(rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_SECRET_KEYS));
+    rnp_input_destroy(input);
+    input = NULL;
+    // write out some data
+    FILE *fp = fopen("plaintext", "wb");
+    assert_non_null(fp);
+    assert_int_equal(1, fwrite(plaintext, strlen(plaintext), 1, fp));
+    assert_int_equal(0, fclose(fp));
+    // 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
+    assert_rnp_success(rnp_op_encrypt_set_cipher(op, "CAST5"));
+    // 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_rnp_success(rnp_input_from_path(&input, "data/keyrings/1/pubring.gpg"));
+    assert_rnp_success(rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_PUBLIC_KEYS));
+    rnp_input_destroy(input);
+    input = NULL;
+    assert_rnp_success(rnp_input_from_path(&input, "data/keyrings/1/secring.gpg"));
+    assert_rnp_success(rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_SECRET_KEYS));
+    rnp_input_destroy(input);
+    input = NULL;
+
+    // write out some data
+    FILE *fp = fopen("plaintext", "wb");
+    assert_non_null(fp);
+    assert_int_equal(1, fwrite(plaintext, strlen(plaintext), 1, fp));
+    assert_int_equal(0, fclose(fp));
+
+    // 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
+    assert_rnp_success(rnp_op_encrypt_set_cipher(op, "CAST5"));
+    // enable armoring
+    assert_rnp_success(rnp_op_encrypt_set_armor(op, true));
+    // add signature
+    assert_rnp_success(rnp_op_encrypt_set_hash(op, "SHA1"));
+    assert_rnp_success(rnp_op_encrypt_set_creation_time(op, 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_success(rnp_op_encrypt_add_signature(op, key, NULL));
+    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_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"));
+
+    // 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;
+    // 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_rnp_success(
+      rnp_input_from_path(&input, "data/test_stream_key_load/key0-sub02.pgp"));
+    assert_rnp_success(rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_PUBLIC_KEYS));
+    rnp_input_destroy(input);
+
+    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_rnp_success(rnp_input_from_path(&input, "data/keyrings/1/secring.gpg"));
+    assert_rnp_success(rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_SECRET_KEYS));
+    rnp_input_destroy(input);
+
+    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_rnp_success(
+      rnp_input_from_path(&input, "data/test_stream_key_load/key0-sub01.pgp"));
+    assert_rnp_success(rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_PUBLIC_KEYS));
+    rnp_input_destroy(input);
+
+    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_rnp_success(rnp_input_from_path(&input, "data/keyrings/1/secring.gpg"));
+    assert_rnp_success(rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_SECRET_KEYS));
+    rnp_input_destroy(input);
+
+    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_rnp_success(rnp_input_from_path(&input, "data/test_stream_key_load/key0-sub1.pgp"));
+    assert_rnp_success(rnp_load_keys(ffi, "GPG", input, RNP_LOAD_SAVE_PUBLIC_KEYS));
+    rnp_input_destroy(input);
+
+    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_failure(rnp_op_encrypt_execute(op));
+    rnp_op_encrypt_destroy(op);
+    rnp_input_destroy(input);
+    rnp_output_destroy(output);
+
+    rnp_ffi_destroy(ffi);
+}
--- a/third_party/rnp/src/tests/ffi.cpp
+++ b/third_party/rnp/src/tests/ffi.cpp
@@ -49,119 +49,117 @@ TEST_F(rnp_tests, test_ffi_homedir)
     char *      pub_path = NULL;
     char *      sec_format = NULL;
     char *      sec_path = NULL;
     rnp_input_t input = NULL;
 
     // get the default homedir (not a very thorough test)
     {
         char *homedir = NULL;
-        assert_int_equal(RNP_SUCCESS, rnp_get_default_homedir(&homedir));
+        assert_rnp_success(rnp_get_default_homedir(&homedir));
         assert_non_null(homedir);
         rnp_buffer_destroy(homedir);