Skip to content

Commit

Permalink
FFI: add functions rnp_key_revocation_signature_create(), rnp_key_sig…
Browse files Browse the repository at this point in the history
…nature_set_hash() and rnp_key_signature_set_revocation_reason().
  • Loading branch information
ni4 committed Jan 17, 2024
1 parent bb61d58 commit bdbfba0
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 39 deletions.
14 changes: 14 additions & 0 deletions include/rekey/rnp_key_store.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,20 @@ class KeyStore {
*/
pgp_key_t *add_key(pgp_key_t &key);

/**
* @brief Add signature of the specific key to the keystore, revalidating and refresing
* key's data. Currently supports only direct-key or subkey binding signature.
*
* @param keyfp key's fingerprint.
* @param sig signature packet.
* @param front set to true if signature should be added to the beggining of the signature
* list.
* @return pgp_subsig_t*
*/
pgp_subsig_t *add_key_sig(const pgp_fingerprint_t &keyfp,
const pgp_signature_t & sig,
bool front);

/**
* @brief Add transferable key to the keystore.
*
Expand Down
39 changes: 39 additions & 0 deletions include/rnp/rnp.h
Original file line number Diff line number Diff line change
Expand Up @@ -1542,6 +1542,45 @@ RNP_API rnp_result_t rnp_key_direct_signature_create(rnp_key_handle_t sig
rnp_key_handle_t target,
rnp_signature_handle_t *sig);

/**
* @brief Create new key or subkey revocation signature. It may be
* customized via the rnp_signature_set_* calls, and finalized via the
* rnp_signature_sign() call.
*
* @param signer revoker's key, must be secret, and must exist in the keyring up to the
* rnp_signature_sign() call. Cannot be NULL.
* @param target target key for which signature should be made. May be NULL, then signer will
* revoke itself.
*
* @param sig on success signature handle will be stored here. It is initialized with current
* creation time, default hash algorithm and version. Cannot be NULL.
* @return RNP_SUCCESS or error code if failed.
*/
RNP_API rnp_result_t rnp_key_revocation_signature_create(rnp_key_handle_t signer,
rnp_key_handle_t target,
rnp_signature_handle_t *sig);

/**
* @brief Set hash algorithm, used during signing.
*
* @param sig editable key signature handle, i.e. created with rnp_key_*_signature_create().
* @param hash hash algorithm name, i.e. "SHA256" or others.
* @return RNP_SUCCESS or error code if failed.
*/
RNP_API rnp_result_t rnp_key_signature_set_hash(rnp_signature_handle_t sig, const char *hash);

/**
* @brief Set revocation reason and code for the revocation signature.
* See `rnp_key_revoke()` for the details.
* @param sig editable key signature handle, i.e. created with rnp_key_*_signature_create().
* @param code revocation reason code. Could be NULL, then default one will be set.
* @param reason human-readable reason for revocation. Could be NULL or empty string.
* @return RNP_SUCCESS or error code if failed.
*/
RNP_API rnp_result_t rnp_key_signature_set_revocation_reason(rnp_signature_handle_t sig,
const char * code,
const char * reason);

/**
* @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.
Expand Down
148 changes: 109 additions & 39 deletions src/lib/rnp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4080,6 +4080,27 @@ rnp_key_get_revoker(rnp_key_handle_t key)
return get_key_require_secret(key);
}

static bool
fill_revocation_reason(rnp_ffi_t ffi,
pgp_revoke_t &revinfo,
const char * code,
const char * reason)
{
revinfo = {};
if (code && !str_to_revocation_type(code, &revinfo.code)) {
FFI_LOG(ffi, "Wrong revocation code: %s", code);
return false;
}
if (revinfo.code > PGP_REVOCATION_RETIRED) {
FFI_LOG(ffi, "Wrong key revocation code: %d", (int) revinfo.code);
return false;
}
if (reason) {
revinfo.reason = reason;
}
return true;
}

static rnp_result_t
rnp_key_get_revocation(rnp_ffi_t ffi,
pgp_key_t * key,
Expand All @@ -4098,22 +4119,9 @@ rnp_key_get_revocation(rnp_ffi_t ffi,
return RNP_ERROR_BAD_PARAMETERS;
}
pgp_revoke_t revinfo = {};
if (code && !str_to_revocation_type(code, &revinfo.code)) {
FFI_LOG(ffi, "Wrong revocation code: %s", code);
if (!fill_revocation_reason(ffi, revinfo, code, reason)) {
return RNP_ERROR_BAD_PARAMETERS;
}
if (revinfo.code > PGP_REVOCATION_RETIRED) {
FFI_LOG(ffi, "Wrong key revocation code: %d", (int) revinfo.code);
return RNP_ERROR_BAD_PARAMETERS;
}
if (reason) {
try {
revinfo.reason = reason;
} catch (const std::exception &e) {
FFI_LOG(ffi, "%s", e.what());
return RNP_ERROR_OUT_OF_MEMORY;
}
}
/* unlock the secret key if needed */
rnp::KeyLocker revlock(*revoker);
if (revoker->is_locked() && !revoker->unlock(ffi->pass_provider)) {
Expand Down Expand Up @@ -6174,11 +6182,12 @@ try {
}
FFI_GUARD

rnp_result_t
rnp_key_direct_signature_create(rnp_key_handle_t signer,
rnp_key_handle_t target,
rnp_signature_handle_t *sig)
try {
static rnp_result_t
create_key_signature(rnp_key_handle_t signer,
rnp_key_handle_t target,
rnp_signature_handle_t *sig,
pgp_sig_type_t type)
{
if (!signer || !sig) {
return RNP_ERROR_NULL_POINTER;
}
Expand All @@ -6190,8 +6199,19 @@ try {
if (!sigkey || !tgkey) {
return RNP_ERROR_BAD_PARAMETERS;
}
if (!tgkey->is_primary()) {
return RNP_ERROR_BAD_PARAMETERS;
switch (type) {
case PGP_SIG_DIRECT:
if (!tgkey->is_primary()) {
return RNP_ERROR_BAD_PARAMETERS;
}
break;
case PGP_SIG_REV_KEY:
if (!tgkey->is_primary()) {
type = PGP_SIG_REV_SUBKEY;
}
break;
default:
return RNP_ERROR_NOT_IMPLEMENTED;
}
*sig = (rnp_signature_handle_t) calloc(1, sizeof(**sig));
if (!*sig) {
Expand All @@ -6204,7 +6224,7 @@ try {
DEFAULT_PGP_HASH_ALG,
signer->ffi->context.time(),
sigkey->version());
sigpkt.set_type(PGP_SIG_DIRECT);
sigpkt.set_type(type);
(*sig)->sig = new pgp_subsig_t(sigpkt);
(*sig)->ffi = signer->ffi;
(*sig)->key = tgkey;
Expand All @@ -6219,6 +6239,57 @@ try {

return RNP_SUCCESS;
}

rnp_result_t
rnp_key_direct_signature_create(rnp_key_handle_t signer,
rnp_key_handle_t target,
rnp_signature_handle_t *sig)
try {
return create_key_signature(signer, target, sig, PGP_SIG_DIRECT);
}
FFI_GUARD

rnp_result_t
rnp_key_revocation_signature_create(rnp_key_handle_t signer,
rnp_key_handle_t target,
rnp_signature_handle_t *sig)
try {
return create_key_signature(signer, target, sig, PGP_SIG_REV_KEY);
}
FFI_GUARD

rnp_result_t
rnp_key_signature_set_hash(rnp_signature_handle_t sig, const char *hash)
try {
if (!sig || !hash) {
return RNP_ERROR_NULL_POINTER;
}
if (!sig->new_sig) {
return RNP_ERROR_BAD_PARAMETERS;
}
if (!str_to_hash_alg(hash, &sig->sig->sig.halg)) {
FFI_LOG(sig->ffi, "Invalid hash: %s", hash);
return RNP_ERROR_BAD_PARAMETERS;
}
return RNP_SUCCESS;
}
FFI_GUARD

rnp_result_t
rnp_key_signature_set_revocation_reason(rnp_signature_handle_t sig,
const char * code,
const char * reason)
try {
if (!sig) {
return RNP_ERROR_NULL_POINTER;
}
pgp_revoke_t revinfo = {};
if (!fill_revocation_reason(sig->ffi, revinfo, code, reason)) {
return RNP_ERROR_BAD_PARAMETERS;
}
sig->sig->sig.set_revocation_reason(revinfo.code, revinfo.reason);
return RNP_SUCCESS;
}
FFI_GUARD

rnp_result_t
Expand Down Expand Up @@ -6268,31 +6339,30 @@ try {
return RNP_ERROR_BAD_PASSWORD;
}
/* Sign */
if (sigpkt.type() == PGP_SIG_DIRECT) {
switch (sigpkt.type()) {
case PGP_SIG_DIRECT:
signer->sign_direct(sig->key->pkt(), sigpkt, sig->ffi->context);
} else {
break;
case PGP_SIG_REV_KEY:
signer->sign_direct(sig->key->pkt(), sigpkt, sig->ffi->context);
break;
case PGP_SIG_REV_SUBKEY:
signer->sign_binding(sig->key->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) */
pgp_subsig_t *newsig = NULL;
pgp_key_t * key = sig->ffi->secring->get_key(sig->key->fp());
if (key) {
newsig = &key->add_sig(sigpkt, PGP_UID_NONE, true);
key->refresh_data(sig->ffi->context);
}
key = sig->ffi->pubring->get_key(sig->key->fp());
if (key) {
newsig = &key->add_sig(sigpkt, PGP_UID_NONE, true);
key->refresh_data(sig->ffi->context);
}
pgp_subsig_t *secsig = sig->ffi->secring->add_key_sig(sig->key->fp(), sigpkt, true);
pgp_subsig_t *pubsig = sig->ffi->pubring->add_key_sig(sig->key->fp(), sigpkt, true);
/* Should not happen but let's check */
if (!newsig) {
if (!secsig && !pubsig) {
return RNP_ERROR_BAD_STATE;
}
/* Replace owned sig with pointer to the key's one */
delete sig->sig;
sig->sig = newsig;
sig->sig = pubsig ? pubsig : secsig;
sig->own_sig = false;
sig->new_sig = false;

Expand Down Expand Up @@ -6532,13 +6602,13 @@ rnp_signature_get_revocation_reason(rnp_signature_handle_t sig, char **code, cha
rreason = sig->sig->sig.revocation_reason();
}
if (code) {
rnp_result_t ret = ret_str_value("", code);
rnp_result_t ret = ret_str_value(rcode.c_str(), code);
if (ret) {
return ret;
}
}
if (reason) {
rnp_result_t ret = ret_str_value("", reason);
rnp_result_t ret = ret_str_value(rreason.c_str(), reason);
if (ret) {
if (code) {
free(*code);
Expand Down
33 changes: 33 additions & 0 deletions src/librekey/rnp_key_store.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,39 @@ KeyStore::add_key(pgp_key_t &srckey)
return added_key;
}

pgp_subsig_t *
KeyStore::add_key_sig(const pgp_fingerprint_t &keyfp, const pgp_signature_t &sig, bool front)
{
pgp_key_t *key = get_key(keyfp);
if (!key) {
return NULL;
}

bool desig_rev = false;
pgp_key_t *signer = get_signer(sig);
switch (sig.type()) {
case PGP_SIG_REV_KEY:
desig_rev = signer && (signer->fp() != key->fp());
break;
case PGP_SIG_REV_SUBKEY:
desig_rev = signer && (signer->fp() != key->primary_fp());
break;
default:
break;
}
/* Add to the keyring(s) */
pgp_subsig_t &newsig = key->add_sig(sig, PGP_UID_NONE, front);
if (desig_rev) {
key->validate_desig_revokes(*this);
}
if (key->is_primary()) {
key->refresh_data(secctx);
} else {
key->refresh_data(primary_key(*key), secctx);
}
return &newsig;
}

pgp_key_t *
KeyStore::import_key(pgp_key_t &srckey, bool pubkey, pgp_key_import_status_t *status)
{
Expand Down

0 comments on commit bdbfba0

Please sign in to comment.