1
1
mirror of https://github.com/profanity-im/profanity.git synced 2025-02-02 15:08:15 -05:00

Merge pull request #1703 from profanity-im/feat/ox-improvements1

Several OX improvements
This commit is contained in:
Michael Vetter 2022-05-04 12:05:39 +02:00 committed by GitHub
commit 1f9a75e8f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 236 additions and 112 deletions

153
docs/profanity-ox-setup.1 Normal file
View File

@ -0,0 +1,153 @@
.TH man 1 "2022-05-03" "0.12.1" "Profanity XMPP client"
.SH NAME
Profanity \- a simple console based XMPP chat client.
.SH DESCRIPTION
.ie "\f[CB]x\f[]"x" \{\
. ftr V B
. ftr VI BI
. ftr VB B
. ftr VBI BI
.\}
.el \{\
. ftr V CR
. ftr VI CI
. ftr VB CB
. ftr VBI CBI
.\}
.PP
This man page is intended to help you set up XEP-0374: OpenPGP for XMPP
Instant Messaging.
Also known as OX.
.PP
For details on usage see man profanity-ox or \f[V]/help ox\f[R].
.PP
Profanity wants to give the user a maximum freedom in setting up their
system.
So we won\[cq]t touch your GPG settings directly.
Which means you will need to do some manual steps.
They are described here
.SH Generate OpenPGP key materials
.PP
The first step is to create a OpenPGP key pair.
The key pair generation will be done with the \f[V]gpg\f[R] command of
GnuPG.
.IP
.nf
\f[C]
gpg --quick-generate-key xmpp:alice\[at]domain.tld future-default default 3y
\f[R]
.fi
.PP
This command will generated a OpenPGP key with a UID
\f[V]xmpp:alice\[at]domain.tld\f[R].
The option \f[V]future-default\f[R] has been used to generate a
ed25519/cv25519 key.
The key is set to expire in threeyears.
.PP
Replace the Jabber ID with your JID and do \f[B]not\f[R] forget the URI
\f[V]xmpp:\f[R] prefix.
.PP
Example output:
.IP
.nf
\f[C]
pub ed25519 2021-09-21 [SC] [verf\[:a]llt: 2024-09-20]
583BAE703A801095B6B71A56BD801174B1A0B84A
uid xmpp:alice\[at]domain.tld
sub cv25519 2021-09-21 [E]
\f[R]
.fi
.SH Export your public key
.PP
You need to export your public key so you can later upload it into a PEP
node.
It\[cq]s just a way how your chat partners can retrieve the public key
from you.
Use the command below to export public key:
.PP
Example command:
.IP
.nf
\f[C]
gpg --export \[rs]
--export-options export-minimal \[rs]
--export-filter \[aq]keep-uid=uid =\[ti] xmpp:alice\[at]domain.tld\[aq] \[rs]
--export-filter \[aq]drop-subkey=usage =\[ti] a\[aq] \[rs]
583BAE703A801095B6B71A56BD801174B1A0B84A \[rs]
> /tmp/pep-key.gpg
\f[R]
.fi
.PP
The key will be exported to \f[V]/tmp/pep-key.gpg\f[R].
You may check the key with the command below:
.PP
\f[V]gpg --show-key --with-sig-list /tmp/pep-key.gpg\f[R]
.PP
Keep in mind: Public keys may have some information (signatures, name,
e-mail address).
Be careful which data will be exported.
The \f[V]export-options\f[R] and \f[V]export-filter\f[R] option of GnuPG
will help you to filter the data.
.SH Publish your key
.PP
You can use profanity to publish your exported key into your account
(PEP).
The \f[V]/ox announce\f[R] command will publish your key.
.IP
.nf
\f[C]
/ox announce /tmp/pep-key.gpg
\f[R]
.fi
.PP
The command will create two PEP node records to store the key.
.SH Discover keys
.PP
To discover public keys of your partners use the \f[V]/ox discover\f[R]
command.
.PP
Example output:
.IP
.nf
\f[C]
/ox discover buddy\[at]domain.tld
Discovering Public Key for buddy\[at]domain.tld
1234567890ABCDEF1234567890ABCDEF12345678
\f[R]
.fi
.PP
To request and import a key, you can use the \f[V]/ox request\f[R]
command.
.IP
.nf
\f[C]
/ox request buddy\[at]domain.tld 1234567890ABCDEF1234567890ABCDEF12345678
Requesting Public Key 1234567890ABCDEF1234567890ABCDEF12345678 for buddy\[at]domain.tld
Public Key imported
\f[R]
.fi
.PP
The key will be imported into your gnupg keyring.
.SH Sign the imported key
.PP
The key can been shown via gpg
\f[V]gpg -k xmpp:buddy\[at]domain.tld\f[R].
Make sure the key is the key of your buddy and sign the key with your
key.
.IP
.nf
\f[C]
gpg --ask-cert-level --default-key 583BAE703A801095B6B71A56BD801174B1A0B84A --sign-key 1234567890ABCDEF1234567890ABCDEF12345678
\f[R]
.fi
.PP
The command \f[V]/ox contacts\f[R] will show the keys with XMPP-UID.
The command \f[V]/ox keys\f[R] will show all known OpenPGP keys.
.PP
Only once you signed the key you can actually use OX with your partner.
.SH Use OX
.PP
Within a chat window you can start OX via \f[V]/ox start\f[R] and stop
it via \f[V]/ox end\f[R].
.PP
Messages will be send signed and encrypted.

View File

@ -1,76 +0,0 @@
# Profanity - OpenPGP for XMPP
Implementation of XEP-0373 - OpenPGP for XMPP (OX) in profanity.
## Overview
The current version (2020-05-23) of profanity provides *XEP-0027: Current Jabber
OpenPGP Usage* via the `/pgp` command. This XEP is *Obsolete*. We should
implement *XEP-0373 - OpenPGP for XMPP (OX)* in profanity.
## pgp
```
14:37:52 - Synopsis
14:37:52 - /pgp libver
14:37:52 - /pgp keys
14:37:52 - /pgp contacts
14:37:52 - /pgp setkey <contact> <keyid>
14:37:52 - /pgp start [<contact>]
14:37:52 - /pgp end
14:37:52 - /pgp log on|off|redact
14:37:52 - /pgp char <char>
14:37:52 -
14:37:52 - Description
14:37:52 - Open PGP commands to manage keys, and perform PGP encryption during chat sessions. See the /account command to set your own PGP key.
14:37:52 -
14:37:52 - Arguments
14:37:52 - libver : Show which version of the libgpgme library is being used.
14:37:52 - keys : List all keys known to the system.
14:37:52 - contacts : Show contacts with assigned public keys.
14:37:52 - setkey <contact> <keyid> : Manually associate a contact with a public key.
14:37:52 - start [<contact>] : Start PGP encrypted chat, current contact will be used if not specified.
14:37:52 - end : End PGP encrypted chat with the current recipient.
14:37:52 - log on|off : Enable or disable plaintext logging of PGP encrypted messages.
14:37:52 - log redact : Log PGP encrypted messages, but replace the contents with [redacted]. This is the default.
14:37:52 - char <char> : Set the character to be displayed next to PGP encrypted messages.
```
## OX
We should implement the `/ox` command which can be used for XEP-0373 instead of
XEP-0027.
```
/ox keys - List all public keys known to the system (gnupg's keyring)
/ox contacts - Shows contacts with an assigned public key.
```
The `keys` command will list all public keys of gnupg's Keyring, independent if
the key is in use for XMPP or not.
In profanity we are going to implement the key lookup with a XMPP-URI as OpenPGP
User-ID. An OpenPGP public key can only be used, if the owner of the public key
created an User-ID with the XMPP-URI as Name. https://xmpp.org/extensions/xep-0373.html#openpgp-user-ids
It's not required and possible to assign a contact to an public key.
```
sec rsa3072 2020-05-01 [SC] [verfällt: 2022-05-01]
7FA1EB8644BAC07E7F18E7C9F121E6A6F3A0C7A5
uid [ ultimativ ] Doctor Snuggles <doctor.snuggles@domain.tld>
uid [ ultimativ ] xmpp:doctor.snuggles@domain.tld
ssb rsa3072 2020-05-01 [E] [verfällt: 2022-05-01]
```
The `contacts` command will show all contacts of the roster with a public key in
the keyring, if there is a xmpp user-id within the public key.
OX provides the elements: `<signcrypt/>`, `<sign/>` and `<crypt/>`. Profanity
implements signcrypt, only.
## Keys command
The command `keys` is independent of the XEP. Should we move common commands
(e.g. /pgp keys /ox keys) to /openpgp which will will be the function which are
related to gnupg itself.
## Appendix
* https://xmpp.org/extensions/xep-0373.html - 0.4.0 (2018-07-30)

View File

@ -1734,7 +1734,8 @@ static struct cmd_t command_defs[] = {
CMD_DESC(
"OpenPGP (OX) commands to manage keys, and perform OpenPGP encryption during chat sessions. "
"Your OpenPGP key needs a user-id with your JID URI (xmpp:local@domain.tld). "
"A key can be generated with \"gpg --quick-gen-key xmpp:local@domain.tld future-default default 3y\".")
"A key can be generated with \"gpg --quick-gen-key xmpp:local@domain.tld future-default default 3y\". "
"See man profanity-ox-setup for details on how to set up OX the first time.")
CMD_ARGS(
{ "keys", "List all keys known to the system." },
{ "contacts", "Show contacts with assigned public keys." },
@ -1745,7 +1746,7 @@ static struct cmd_t command_defs[] = {
{ "char <char>", "Set the character to be displayed next to PGP encrypted messages." },
{ "announce <file>", "Announce a public key by pushing it on the XMPP Server" },
{ "discover <jid>", "Discover public keys of a jid. The OpenPGP Key IDs will be displayed" },
{ "request <jid>", "Request public keys" },
{ "request <jid> <keyid>", "Request public key. See /ox discover to to get available key IDs." },
{ "sendfile on|off", "Allow /sendfile to send unencrypted files while otherwise using PGP." })
CMD_EXAMPLES(
"/ox log off",

View File

@ -7650,7 +7650,7 @@ cmd_ox(ProfWin* window, const char* const command, gchar** args)
}
if (chatwin->is_ox) {
win_println(window, THEME_DEFAULT, "!", "You have already started OX encryption.");
win_println(window, THEME_DEFAULT, "!", "You have already started an OX encrypted session.");
return TRUE;
}
@ -7671,6 +7671,22 @@ cmd_ox(ProfWin* window, const char* const command, gchar** args)
chatwin->is_ox = TRUE;
win_println(window, THEME_DEFAULT, "!", "OX encryption enabled.");
return TRUE;
} else if (g_strcmp0(args[0], "end") == 0) {
if (window->type != WIN_CHAT && args[1] == NULL) {
cons_show("You must be in a regular chat window to stop OX encryption.");
return TRUE;
}
ProfChatWin* chatwin = (ProfChatWin*)window;
assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
if (!chatwin->is_ox) {
win_println(window, THEME_DEFAULT, "!", "No OX session has been started.");
} else {
chatwin->is_ox = FALSE;
win_println(window, THEME_DEFAULT, "!", "OX encryption disabled.");
}
return TRUE;
} else if (g_strcmp0(args[0], "announce") == 0) {
if (args[1]) {
gchar* filename = get_expanded_path(args[1]);

View File

@ -914,6 +914,7 @@ p_ox_gpg_signcrypt(const char* const sender_barejid, const char* const recipient
// lookup own key
recp[0] = _ox_key_lookup(sender_barejid, TRUE);
if (error != 0) {
cons_show_error("Can't find OX key for %s", xmpp_jid_me);
log_error("OX: Key not found for %s. Error: %s", xmpp_jid_me, gpgme_strerror(error));
return NULL;
}
@ -927,13 +928,14 @@ p_ox_gpg_signcrypt(const char* const sender_barejid, const char* const recipient
// lookup key of recipient
recp[1] = _ox_key_lookup(recipient_barejid, FALSE);
if (error != 0) {
cons_show_error("Can't find OX key for %s", xmpp_jid_recipient);
log_error("OX: Key not found for %s. Error: %s", xmpp_jid_recipient, gpgme_strerror(error));
return NULL;
}
recp[2] = NULL;
log_debug("%s <%s>", recp[0]->uids->name, recp[0]->uids->email);
log_debug("%s <%s>", recp[1]->uids->name, recp[1]->uids->email);
log_debug("OX: %s <%s>", recp[0]->uids->name, recp[0]->uids->email);
log_debug("OX: %s <%s>", recp[1]->uids->name, recp[1]->uids->email);
gpgme_encrypt_flags_t flags = 0;
@ -1110,9 +1112,22 @@ _ox_key_is_usable(gpgme_key_t key, const char* const barejid, gboolean secret)
gboolean result = TRUE;
if (key->revoked || key->expired || key->disabled) {
cons_show_error("%s's key is revoked, expired or disabled", barejid);
log_info("OX: %s's key is revoked, expired or disabled", barejid);
result = FALSE;
}
// This might be a nice features but AFAIK is not defined in the XEP.
// If we add this we need to expand our documentation on how to set the
// trust leven in gpg. I'll add an example to this commit body.
/*
if (key->owner_trust < GPGME_VALIDITY_MARGINAL) {
cons_show_error(" %s's key is has a trust level lower than marginal", barejid);
log_info("OX: Owner trust of %s's key is < GPGME_VALIDITY_MARGINAL", barejid);
result = FALSE;
}
*/
return result;
}
@ -1180,11 +1195,13 @@ p_ox_gpg_decrypt(char* base64)
return NULL;
}
}
size_t len;
char* plain_str = gpgme_data_release_and_get_mem(plain, &len);
char* result = malloc(len + 1);
strcpy(result, plain_str);
memcpy(result, plain_str, len);
result[len] = '\0';
gpgme_free(plain_str);
return result;
}

View File

@ -1616,11 +1616,13 @@ _openpgp_signcrypt(xmpp_ctx_t* ctx, const char* const to, const char* const text
struct tm* tm = localtime(&now);
char buf[255];
strftime(buf, sizeof(buf), "%FT%T%z", tm);
int randnr = rand() % 5;
char rpad_data[randnr];
for (int i = 0; i < randnr - 1; i++) {
rpad_data[i] = 'c';
// build rpad
int randnr = (rand() % 100) + 1;
char rpad_data[randnr];
for (int i = 0; i < randnr; i++) {
int rchar = (rand() % 52) + 65;
rpad_data[i] = rchar;
}
rpad_data[randnr - 1] = '\0';

View File

@ -42,6 +42,7 @@
#include "ui/ui.h"
#include "xmpp/connection.h"
#include "xmpp/stanza.h"
#include "xmpp/iq.h"
#include "pgp/gpg.h"
#ifdef HAVE_LIBGPGME
@ -49,19 +50,18 @@
#define KEYID_LENGTH 40
static void _ox_metadata_node__public_key(const char* const fingerprint);
static int _ox_metadata_result(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* const userdata);
static int _ox_metadata_result(xmpp_stanza_t* const stanza, void* const userdata);
static void _ox_request_public_key(const char* const jid, const char* const fingerprint);
static int _ox_public_key_result(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* const userdata);
static int _ox_public_key_result(xmpp_stanza_t* const stanza, void* const userdata);
/*!
* \brief Current Date and Time.
/* Return Current Date and Time.
*
* XEP-0082: XMPP Date and Time Profiles
* https://xmpp.org/extensions/xep-0082.html
*
* \return YYYY-MM-DDThh:mm:ssZ
*
* According to ISO8601
* YYYY-MM-DDThh:mm:ssZ
*/
static char* _gettimestamp();
@ -106,7 +106,7 @@ ox_announce_public_key(const char* const filename)
log_info("[OX] Announce OpenPGP Key for Fingerprint: %s", fp);
xmpp_ctx_t* const ctx = connection_get_ctx();
char* id = xmpp_uuid_gen(ctx);
char* id = connection_create_stanza_id();
xmpp_stanza_t* iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id);
xmpp_stanza_set_from(iq, xmpp_conn_get_jid(connection_get_conn()));
@ -143,7 +143,15 @@ ox_announce_public_key(const char* const filename)
xmpp_stanza_add_child(publish, item);
xmpp_stanza_add_child(pubsub, publish);
xmpp_stanza_add_child(iq, pubsub);
xmpp_send(connection_get_conn(), iq);
if (connection_supports(XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS)) {
stanza_attach_publish_options(ctx, iq, "pubsub#access_model", "open");
} else {
log_debug("[OX] Cannot publish public key: no PUBSUB feature announced");
}
iq_send_stanza(iq);
xmpp_stanza_release(iq);
_ox_metadata_node__public_key(fp);
@ -174,7 +182,7 @@ ox_discover_public_key(const char* const jid)
cons_show("Discovering Public Key for %s", jid);
// iq
xmpp_ctx_t* const ctx = connection_get_ctx();
char* id = xmpp_uuid_gen(ctx);
char* id = connection_create_stanza_id();
xmpp_stanza_t* iq = xmpp_iq_new(ctx, STANZA_TYPE_GET, id);
xmpp_stanza_set_from(iq, xmpp_conn_get_jid(connection_get_conn()));
xmpp_stanza_set_to(iq, jid);
@ -190,8 +198,9 @@ ox_discover_public_key(const char* const jid)
xmpp_stanza_add_child(pubsub, items);
xmpp_stanza_add_child(iq, pubsub);
xmpp_id_handler_add(connection_get_conn(), _ox_metadata_result, id, strdup(jid));
xmpp_send(connection_get_conn(), iq);
iq_id_handler_add(xmpp_stanza_get_id(iq), _ox_metadata_result, NULL, NULL);
iq_send_stanza(iq);
xmpp_stanza_release(iq);
}
@ -236,7 +245,7 @@ _ox_metadata_node__public_key(const char* const fingerprint)
assert(strlen(fingerprint) == KEYID_LENGTH);
// iq
xmpp_ctx_t* const ctx = connection_get_ctx();
char* id = xmpp_uuid_gen(ctx);
char* id = connection_create_stanza_id();
xmpp_stanza_t* iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id);
xmpp_stanza_set_from(iq, xmpp_conn_get_jid(connection_get_conn()));
// pubsub
@ -258,18 +267,22 @@ _ox_metadata_node__public_key(const char* const fingerprint)
xmpp_stanza_t* pubkeymetadata = xmpp_stanza_new(ctx);
xmpp_stanza_set_name(pubkeymetadata, STANZA_NAME_PUBKEY_METADATA);
xmpp_stanza_set_attribute(pubkeymetadata, STANZA_ATTR_V4_FINGERPRINT, fingerprint);
xmpp_stanza_set_attribute(pubkeymetadata, STANZA_ATTR_DATE, _gettimestamp());
char* timestamp = _gettimestamp();
xmpp_stanza_set_attribute(pubkeymetadata, STANZA_ATTR_DATE, timestamp);
free(timestamp);
xmpp_stanza_add_child(publickeyslist, pubkeymetadata);
xmpp_stanza_add_child(item, publickeyslist);
xmpp_stanza_add_child(publish, item);
xmpp_stanza_add_child(pubsub, publish);
xmpp_stanza_add_child(iq, pubsub);
xmpp_send(connection_get_conn(), iq);
iq_send_stanza(iq);
xmpp_stanza_release(iq);
}
static int
_ox_metadata_result(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* const userdata)
_ox_metadata_result(xmpp_stanza_t* const stanza, void* const userdata)
{
log_debug("[OX] Processing result %s's metadata.", (char*)userdata);
@ -349,7 +362,7 @@ _ox_request_public_key(const char* const jid, const char* const fingerprint)
log_info("[OX] Request %s's public key %s.", jid, fingerprint);
// iq
xmpp_ctx_t* const ctx = connection_get_ctx();
char* id = xmpp_uuid_gen(ctx);
char* id = connection_create_stanza_id();
xmpp_stanza_t* iq = xmpp_iq_new(ctx, STANZA_TYPE_GET, id);
xmpp_stanza_set_from(iq, xmpp_conn_get_jid(connection_get_conn()));
xmpp_stanza_set_to(iq, jid);
@ -370,9 +383,10 @@ _ox_request_public_key(const char* const jid, const char* const fingerprint)
xmpp_stanza_add_child(pubsub, items);
xmpp_stanza_add_child(iq, pubsub);
xmpp_id_handler_add(connection_get_conn(), _ox_public_key_result, id, NULL);
iq_id_handler_add(xmpp_stanza_get_id(iq), _ox_public_key_result, NULL, NULL);
xmpp_send(connection_get_conn(), iq);
iq_send_stanza(iq);
xmpp_stanza_release(iq);
}
/*!
@ -400,7 +414,7 @@ _ox_request_public_key(const char* const jid, const char* const fingerprint)
*/
int
_ox_public_key_result(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void* const userdata)
_ox_public_key_result(xmpp_stanza_t* const stanza, void* const userdata)
{
log_debug("[OX] Processing result public key");
@ -463,13 +477,10 @@ _ox_public_key_result(xmpp_conn_t* const conn, xmpp_stanza_t* const stanza, void
char*
_gettimestamp()
{
time_t now = time(NULL);
struct tm* tm = localtime(&now);
char buf[255];
strftime(buf, sizeof(buf), "%FT%T", tm);
GString* d = g_string_new(buf);
g_string_append(d, "Z");
return strdup(d->str);
GDateTime* dt = g_date_time_new_now_local();
gchar* datestr = g_date_time_format(dt, "%FT%TZ");
g_date_time_unref(dt);
return datestr;
}
#endif // HAVE_LIBGPGME