about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorMichael Vetter <jubalh@iodoru.org>2022-05-04 12:05:39 +0200
committerGitHub <noreply@github.com>2022-05-04 12:05:39 +0200
commit1f9a75e8f7b066a280639062627cb96b6900687b (patch)
tree7cbbe9d88167cf240e905c7b62600c17e78a9bf4
parentbe55b16bef18532d46161842412236629bde5601 (diff)
parente5e661e92d44ac21a4d19ef429091ef8e38203d3 (diff)
downloadprofani-tty-1f9a75e8f7b066a280639062627cb96b6900687b.tar.gz
Merge pull request #1703 from profanity-im/feat/ox-improvements1
Several OX improvements
-rw-r--r--docs/profanity-ox-setup.1153
-rw-r--r--docs/profanity-ox.md76
-rw-r--r--src/command/cmd_defs.c5
-rw-r--r--src/command/cmd_funcs.c18
-rw-r--r--src/pgp/gpg.c23
-rw-r--r--src/xmpp/message.c10
-rw-r--r--src/xmpp/ox.c63
7 files changed, 236 insertions, 112 deletions
diff --git a/docs/profanity-ox-setup.1 b/docs/profanity-ox-setup.1
new file mode 100644
index 00000000..58f8da41
--- /dev/null
+++ b/docs/profanity-ox-setup.1
@@ -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.
diff --git a/docs/profanity-ox.md b/docs/profanity-ox.md
deleted file mode 100644
index d9513bae..00000000
--- a/docs/profanity-ox.md
+++ /dev/null
@@ -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)
diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c
index a9e45147..b816ddcc 100644
--- a/src/command/cmd_defs.c
+++ b/src/command/cmd_defs.c
@@ -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",
diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c
index e76831fc..f1fa37f1 100644
--- a/src/command/cmd_funcs.c
+++ b/src/command/cmd_funcs.c
@@ -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]);
diff --git a/src/pgp/gpg.c b/src/pgp/gpg.c
index 99d37c64..7dc57d96 100644
--- a/src/pgp/gpg.c
+++ b/src/pgp/gpg.c
@@ -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;
 }
 
diff --git a/src/xmpp/message.c b/src/xmpp/message.c
index c18f9376..fb58285e 100644
--- a/src/xmpp/message.c
+++ b/src/xmpp/message.c
@@ -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';
 
diff --git a/src/xmpp/ox.c b/src/xmpp/ox.c
index 0fa5cece..fab3d675 100644
--- a/src/xmpp/ox.c
+++ b/src/xmpp/ox.c
@@ -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