From 56bce87783fe26f58c09392a2c9b533dc61a8386 Mon Sep 17 00:00:00 2001 From: Nickolay Olshevsky Date: Fri, 15 Sep 2023 13:38:24 +0300 Subject: [PATCH] FFI: Add functions rnp_key_signature_create/rnp_signature_set_revoker/rnp_key_signature_sign. --- include/rnp/rnp.h | 44 +++++++++++++ src/lib/ffi-priv-types.h | 1 + src/lib/rnp.cpp | 129 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 174 insertions(+) diff --git a/include/rnp/rnp.h b/include/rnp/rnp.h index c98f68934f..ee41269c4a 100644 --- a/include/rnp/rnp.h +++ b/include/rnp/rnp.h @@ -138,6 +138,11 @@ typedef uint32_t rnp_result_t; #define RNP_VERIFY_REQUIRE_ALL_SIGS (1U << 1) #define RNP_VERIFY_ALLOW_HIDDEN_RECIPIENT (1U << 2) +/** + * Revocation key flags. + */ +#define RNP_REVOKER_SENSITIVE (1U << 0) + /** * Return a constant string describing the result code */ @@ -1519,6 +1524,45 @@ RNP_API rnp_result_t rnp_key_get_signature_at(rnp_key_handle_t key, size_t idx, rnp_signature_handle_t *sig); +/** + * @brief Create new blank signature, issued by signer. It may be customized via the + * rnp_signature_set_* calls, and finalized via the rnp_signature_sign() call. + * + * @param signer signing key, must be secret, and must exist in the keyring up to the + * rnp_signature_sign() call. Cannot be NULL. + * @param sigtype type of the signature. See rnp_signature_get_type() for possible values. + * @param sig on success signature handle will be stored here. It is initialized with current + * creation time, default hash algorithm and version. + * @return RNP_SUCCESS or error code if failued. + */ +RNP_API rnp_result_t rnp_key_signature_create(rnp_key_handle_t signer, + const char * sigtype, + rnp_signature_handle_t *sig); + +/** + * @brief Add designated revoker subpacket to the signature. See RFC 4880, section 5.2.3.15. + * Only single revoker could be set - subsequent calls would overwrite the previous one. + * + * @param sig editable key signature handle, i.e. created with rnp_key_signature_create(). + * @param revoker revoker's key. + * @param flags additional flags. The following flag is currently supported: + * RNP_REVOKER_SENSITIVE: information about the revocation key should be + * considered as sensitive. See RFC for the details. + * @return RNP_SUCCESS or error code if failued. + */ +RNP_API rnp_result_t rnp_key_signature_set_revoker(rnp_signature_handle_t sig, + rnp_key_handle_t revoker, + uint32_t flags); + +/** + * @brief Finalize populating and sign signature, created with rnp_key_signature_create, and + * add it to the corresponding key. + * + * @param sig signature handle. + * @return RNP_SUCCESS or error code if failed. + */ +RNP_API rnp_result_t rnp_key_signature_sign(rnp_signature_handle_t sig); + /** * @brief Get number of the designated revokers for the key. Designated revoker is a key, which * is allowed to revoke this key. diff --git a/src/lib/ffi-priv-types.h b/src/lib/ffi-priv-types.h index 26cf0214c3..3ade628fa4 100644 --- a/src/lib/ffi-priv-types.h +++ b/src/lib/ffi-priv-types.h @@ -50,6 +50,7 @@ struct rnp_signature_handle_st { const pgp_key_t *key; pgp_subsig_t * sig; bool own_sig; + bool new_sig; }; struct rnp_recipient_handle_st { diff --git a/src/lib/rnp.cpp b/src/lib/rnp.cpp index 19e3d7e784..304ead0d1a 100644 --- a/src/lib/rnp.cpp +++ b/src/lib/rnp.cpp @@ -6174,6 +6174,135 @@ try { } FFI_GUARD +static bool +is_key_signature_supported(int stype) +{ + /* We support creation of only direct-key signature for now */ + switch (stype) { + case PGP_SIG_DIRECT: + return true; + default: + return false; + } +} + +rnp_result_t +rnp_key_signature_create(rnp_key_handle_t signer, + const char * sigtype, + rnp_signature_handle_t *sig) +try { + if (!signer || !sigtype || !sig) { + return RNP_ERROR_NULL_POINTER; + } + auto stype = id_str_pair::lookup(sig_type_map, sigtype); + if (!is_key_signature_supported(stype)) { + FFI_LOG(signer->ffi, "Not yet supported sigtype: %s", sigtype); + return RNP_ERROR_BAD_PARAMETERS; + } + + pgp_key_t *sigkey = get_key_require_secret(signer); + if (!sigkey) { + return RNP_ERROR_BAD_PARAMETERS; + } + *sig = (rnp_signature_handle_t) calloc(1, sizeof(**sig)); + if (!*sig) { + return RNP_ERROR_OUT_OF_MEMORY; + } + try { + pgp_signature_t sigpkt; + sigkey->sign_init(signer->ffi->rng(), + sigpkt, + DEFAULT_PGP_HASH_ALG, + signer->ffi->context.time(), + sigkey->version()); + sigpkt.set_type(static_cast(stype)); + (*sig)->sig = new pgp_subsig_t(sigpkt); + (*sig)->ffi = signer->ffi; + (*sig)->key = sigkey; + (*sig)->own_sig = true; + (*sig)->new_sig = true; + } catch (const std::exception &e) { + FFI_LOG(signer->ffi, "%s", e.what()); + free(*sig); + *sig = NULL; + return RNP_ERROR_OUT_OF_MEMORY; + } + + return RNP_SUCCESS; +} +FFI_GUARD + +rnp_result_t +rnp_key_signature_set_revoker(rnp_signature_handle_t sig, + rnp_key_handle_t revoker, + uint32_t flags) +try { + if (!sig || !revoker) { + return RNP_ERROR_NULL_POINTER; + } + if (!sig->new_sig) { + return RNP_ERROR_BAD_PARAMETERS; + } + bool sensitive = extract_flag(flags, RNP_REVOKER_SENSITIVE); + if (flags) { + FFI_LOG(sig->ffi, "Unsupported flags: %" PRIu32, flags); + return RNP_ERROR_BAD_PARAMETERS; + } + auto key = get_key_prefer_public(revoker); + if (!key) { + return RNP_ERROR_BAD_PARAMETERS; + } + sig->sig->sig.set_revoker(key->pkt(), sensitive); + return RNP_SUCCESS; +} +FFI_GUARD + +rnp_result_t +rnp_key_signature_sign(rnp_signature_handle_t sig) +try { + if (!sig) { + return RNP_ERROR_NULL_POINTER; + } + if (!sig->new_sig) { + return RNP_ERROR_BAD_PARAMETERS; + } + auto &sigpkt = sig->sig->sig; + if (!is_key_signature_supported(sigpkt.type())) { + return RNP_ERROR_BAD_STATE; + } + /* Locate signer */ + auto signer = sig->ffi->secring->get_signer(sigpkt); + if (!signer) { + return RNP_ERROR_BAD_STATE; + } + /* Unlock if needed */ + rnp::KeyLocker seclock(*signer); + if (signer->is_locked() && !signer->unlock(sig->ffi->pass_provider)) { + FFI_LOG(sig->ffi, "Failed to unlock secret key"); + return RNP_ERROR_BAD_PASSWORD; + } + /* Sign */ + switch (sigpkt.type()) { + case PGP_SIG_DIRECT: + signer->sign_direct(signer->pkt(), sigpkt, sig->ffi->context); + break; + default: + FFI_LOG(sig->ffi, "Not yet supported signature type."); + return RNP_ERROR_BAD_STATE; + } + /* Add to the keyring(s) */ + signer->add_sig(sigpkt, PGP_UID_NONE, true); + signer->refresh_data(sig->ffi->context); + auto sigpub = sig->ffi->pubring->get_signer(sigpkt); + if (sigpub) { + sigpub->add_sig(sigpkt, PGP_UID_NONE, true); + sigpub->refresh_data(sig->ffi->context); + } + + return RNP_SUCCESS; +} +FFI_GUARD + rnp_result_t rnp_key_get_revoker_count(rnp_key_handle_t handle, size_t *count) try {