diff --git a/README.org b/README.org index 8b95555..d1ab7a2 100644 --- a/README.org +++ b/README.org @@ -136,7 +136,12 @@ * [ ] Delete bookmarks * [ ] Roster * [ ] OTR (libotr) - * [ ] PGP (libgpgme) + * [X] PGP (rnpgp) + * [X] Use keyrings (e.g. exported from gnupg) + * [X] Presence + * [X] Decryption + * [X] Encryption + * [X] Custom set/clear key (/pgp) * [ ] Room Explorer (https://search.jabber.network/docs/api) ** TODO [#C] Implement completion engine (milestone v0.3) ** TODO [#D] Close all issues (milestone v1.0) diff --git a/account.c b/account.c index 664fcf3..cb437e9 100644 --- a/account.c +++ b/account.c @@ -34,6 +34,7 @@ char *account_options[ACCOUNT_NUM_OPTIONS][2] = { "status", "probably about to segfault" }, { "pgp_pubring_path", "${weechat_data_dir}/pubring.gpg" }, { "pgp_secring_path", "${weechat_data_dir}/secring.gpg" }, + { "pgp_keyid", "" }, }; struct t_account *account__search(const char *name) diff --git a/account.h b/account.h index 038ba61..b66a20a 100644 --- a/account.h +++ b/account.h @@ -19,6 +19,7 @@ enum t_account_option ACCOUNT_OPTION_STATUS, ACCOUNT_OPTION_PGP_PUBRING_PATH, ACCOUNT_OPTION_PGP_SECRING_PATH, + ACCOUNT_OPTION_PGP_KEYID, ACCOUNT_NUM_OPTIONS, }; @@ -60,6 +61,8 @@ enum t_account_option weechat_config_string(account->options[ACCOUNT_OPTION_PGP_PUBRING_PATH]) #define account_pgp_secring_path(account) \ weechat_config_string(account->options[ACCOUNT_OPTION_PGP_SECRING_PATH]) +#define account_pgp_keyid(account) \ + weechat_config_string(account->options[ACCOUNT_OPTION_PGP_KEYID]) struct t_device { diff --git a/command.c b/command.c index 7b2662e..2b60858 100644 --- a/command.c +++ b/command.c @@ -662,7 +662,6 @@ int command__pgp(const void *pointer, void *data, { struct t_account *ptr_account = NULL; struct t_channel *ptr_channel = NULL; - xmpp_stanza_t *message; char *keyid; (void) pointer; diff --git a/config.c b/config.c index f468605..0686d10 100644 --- a/config.c +++ b/config.c @@ -218,6 +218,22 @@ config__account_new_option (struct t_config_file *config_file, callback_change_data, NULL, NULL, NULL); break; + case ACCOUNT_OPTION_PGP_KEYID: + new_option = weechat_config_new_option ( + config_file, section, + option_name, "string", + N_("XMPP Account PGP Key ID"), + NULL, 0, 0, + default_value, value, + null_value_allowed, + callback_check_value, + callback_check_value_pointer, + callback_check_value_data, + callback_change, + callback_change_pointer, + callback_change_data, + NULL, NULL, NULL); + break; case ACCOUNT_NUM_OPTIONS: break; } diff --git a/connection.c b/connection.c index 99dbae6..34146f0 100644 --- a/connection.c +++ b/connection.c @@ -908,7 +908,8 @@ void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status, if (status == XMPP_CONN_CONNECT) { - xmpp_stanza_t *pres, *pres__c, *pres__status, *pres__status__text, **children; + xmpp_stanza_t *pres, *pres__c, *pres__status, *pres__status__text, + *pres__x, *pres__x__text, **children; char cap_hash[28+1] = {0}; xmpp_handler_add(conn, &connection__version_handler, @@ -922,8 +923,14 @@ void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status, xmpp_handler_add(conn, &connection__iq_handler, NULL, "iq", NULL, account); + pgp__init(&account->pgp, + weechat_string_eval_expression(account_pgp_pubring_path(account), + NULL, NULL, NULL), + weechat_string_eval_expression(account_pgp_secring_path(account), + NULL, NULL, NULL)); + /* Send initial so that we appear online to contacts */ - children = malloc(sizeof(*children) * (2 + 1)); + children = malloc(sizeof(*children) * (3 + 1)); pres__c = xmpp_stanza_new(account->context); xmpp_stanza_set_name(pres__c, "c"); @@ -943,8 +950,25 @@ void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status, xmpp_stanza_release(pres__status__text); children[1] = pres__status; - children[2] = NULL; + + if (account->pgp) + { + pres__x = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(pres__x, "x"); + xmpp_stanza_set_ns(pres__x, "jabber:x:signed"); + + pres__x__text = xmpp_stanza_new(account->context); + char *signature = pgp__sign(account->buffer, account->pgp, account_pgp_keyid(account), account_status(account)); + xmpp_stanza_set_text(pres__x__text, signature ? signature : ""); + free(signature); + xmpp_stanza_add_child(pres__x, pres__x__text); + xmpp_stanza_release(pres__x__text); + + children[2] = pres__x; + children[3] = NULL; + } + pres = stanza__presence(account->context, NULL, children, NULL, strdup(account_jid(account)), NULL, NULL); @@ -1042,13 +1066,6 @@ void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status, weechat_command(account->buffer, *command); weechat_string_dyn_free(command, 1); } - - - pgp__init(&account->pgp, - weechat_string_eval_expression(account_pgp_pubring_path(account), - NULL, NULL, NULL), - weechat_string_eval_expression(account_pgp_secring_path(account), - NULL, NULL, NULL)); } else { diff --git a/pgp.c b/pgp.c index 22f3e7b..dc1c65c 100644 --- a/pgp.c +++ b/pgp.c @@ -284,3 +284,81 @@ verify_finish: rnp_input_destroy(signature); return result; } + +char *pgp__sign(struct t_gui_buffer *buffer, struct t_pgp *pgp, const char *source, const char *message) +{ + rnp_input_t keyfile = NULL; + rnp_input_t input = NULL; + rnp_output_t output = NULL; + rnp_op_sign_t sign = NULL; + rnp_key_handle_t key = NULL; + uint8_t * buf = NULL; + size_t buf_len = 0; + char * result = NULL; + + /* create file input and memory output objects for the encrypted message and decrypted + * message */ + if (rnp_input_from_memory(&input, (uint8_t *)message, strlen(message), false) != + RNP_SUCCESS) { + weechat_printf(buffer, "[PGP]\tfailed to create input object\n"); + goto sign_finish; + } + + if (rnp_output_to_memory(&output, 0) != RNP_SUCCESS) { + weechat_printf(buffer, "[PGP]\tfailed to create output object\n"); + goto sign_finish; + } + + /* initialize and configure sign operation */ + if (rnp_op_sign_detached_create(&sign, pgp->context, input, output) != RNP_SUCCESS) { + weechat_printf(buffer, "[PGP]\tfailed to create sign operation\n"); + goto sign_finish; + } + + /* armor, file name, compression */ + rnp_op_sign_set_armor(sign, true); + rnp_op_sign_set_file_name(sign, "message.txt"); + rnp_op_sign_set_file_mtime(sign, time(NULL)); + rnp_op_sign_set_compression(sign, "ZIP", 6); + /* signatures creation time - by default will be set to the current time as well */ + rnp_op_sign_set_creation_time(sign, time(NULL)); + /* signatures expiration time - by default will be 0, i.e. never expire */ + rnp_op_sign_set_expiration_time(sign, 365 * 24 * 60 * 60); + /* set hash algorithm - should be compatible for all signatures */ + rnp_op_sign_set_hash(sign, RNP_ALGNAME_SHA256); + + /* now add signatures. First locate the signing key, then add and setup signature */ + if (rnp_locate_key(pgp->context, "keyid", source, &key) != RNP_SUCCESS) { + weechat_printf(buffer, "[PGP]\tfailed to locate signing key: %s\n", source); + goto sign_finish; + } + + if (rnp_op_sign_add_signature(sign, key, NULL) != RNP_SUCCESS) { + weechat_printf(buffer, "[PGP]\tfailed to add signature for key: %s\n", source); + goto sign_finish; + } + + rnp_key_handle_destroy(key); + key = NULL; + + /* finally do signing */ + if (rnp_op_sign_execute(sign) != RNP_SUCCESS) { + weechat_printf(buffer, "[PGP]\tfailed to sign with key: %s\n", source); + goto sign_finish; + } + + /* get the signature from the output structure */ + if (rnp_output_memory_get_buf(output, &buf, &buf_len, false) != RNP_SUCCESS) { + goto sign_finish; + } + + result = strndup((char *)buf + strlen(PGP_SIGNATURE_HEADER), + buf_len - strlen(PGP_SIGNATURE_HEADER) - strlen(PGP_SIGNATURE_FOOTER)); +sign_finish: + rnp_input_destroy(keyfile); + rnp_key_handle_destroy(key); + rnp_op_sign_destroy(sign); + rnp_input_destroy(input); + rnp_output_destroy(output); + return result; +} diff --git a/pgp.h b/pgp.h index d1bd996..ab73475 100644 --- a/pgp.h +++ b/pgp.h @@ -23,4 +23,6 @@ char *pgp__encrypt(struct t_gui_buffer *buffer, struct t_pgp *pgp, const char *t char *pgp__verify(struct t_gui_buffer *buffer, struct t_pgp *pgp, const char *certificate); +char *pgp__sign(struct t_gui_buffer *buffer, struct t_pgp *pgp, const char *source, const char *message); + #endif /*WEECHAT_XMPP_PGP_H*/