about summary refs log tree commit diff stats
path: root/src/pgp
diff options
context:
space:
mode:
authorDebXWoody <stefan@debxwoody.de>2020-06-21 09:43:42 +0200
committerMichael Vetter <jubalh@iodoru.org>2020-06-29 19:05:41 +0200
commit2c94ee5a88f64332a3f41f41a4d314fc52200e31 (patch)
tree6209fd4796609a7706f950b18a9bb652c6c137fb /src/pgp
parent3afd854dc862696c4842b2ed93efcad44d617910 (diff)
downloadprofani-tty-2c94ee5a88f64332a3f41f41a4d314fc52200e31.tar.gz
Feature request - XEP-0373: OpenPGP for XMPP (OX)
Basic implementation of XEP-0373: OpenPGP for XMPP.
https://xmpp.org/extensions/xep-0373.html

Command /ox

Issue: #1331
Diffstat (limited to 'src/pgp')
-rw-r--r--src/pgp/gpg.c339
-rw-r--r--src/pgp/gpg.h16
2 files changed, 355 insertions, 0 deletions
diff --git a/src/pgp/gpg.c b/src/pgp/gpg.c
index 528b772e..b50dd73f 100644
--- a/src/pgp/gpg.c
+++ b/src/pgp/gpg.c
@@ -72,6 +72,9 @@ static char* _remove_header_footer(char *str, const char *const footer);
 static char* _add_header_footer(const char *const str, const char *const header, const char *const footer);
 static void _save_pubkeys(void);
 
+static gpgme_key_t _ox_key_lookup(const char *const barejid, gboolean secret_only);
+static gboolean _ox_key_is_usable(gpgme_key_t key, const char *const barejid, gboolean secret);
+
 void
 _p_gpg_free_pubkeyid(ProfPGPPubKeyId *pubkeyid)
 {
@@ -787,6 +790,219 @@ p_gpg_format_fp_str(char *fp)
     return result;
 }
 
+/*!
+ * \brief Public keys with XMPP-URI.
+ *
+ * This function will look for all public key with a XMPP-URI as UID.
+ *
+ */
+GHashTable* ox_gpg_public_keys(void){
+    gpgme_error_t error;
+    GHashTable *result = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)_p_gpg_free_key);
+
+    gpgme_ctx_t ctx;
+    error = gpgme_new(&ctx);
+
+    if (error) {
+        log_error("OX - gpgme_new failed: %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        return NULL;
+    }
+
+    error = gpgme_op_keylist_start(ctx, NULL, 0); // all public keys
+    if (error == GPG_ERR_NO_ERROR) {
+        gpgme_key_t key;
+        error = gpgme_op_keylist_next(ctx, &key);
+        if ( error != GPG_ERR_EOF && error != GPG_ERR_NO_ERROR)  {
+            log_error("OX: gpgme_op_keylist_next %s %s", gpgme_strsource(error), gpgme_strerror(error));
+            g_hash_table_destroy(result);
+            return NULL;
+        }
+        while (!error) {
+            // Looking for XMPP URI UID
+            gpgme_user_id_t uid = key->uids;
+            gpgme_user_id_t xmppid = NULL;
+            while (!xmppid && uid) {
+                if( uid->name && strlen(uid->name) >= 10 ) {
+                    if( strstr(uid->name, "xmpp:") == uid->name ) {
+                        xmppid = uid;
+                    }
+                }
+                uid = uid->next;
+            }
+
+            if(xmppid) {
+                // Build Key information about all subkey
+                gpgme_subkey_t sub = key->subkeys;
+
+                ProfPGPKey *p_pgpkey = _p_gpg_key_new();
+                p_pgpkey->id = strdup(sub->keyid);
+                p_pgpkey->name = strdup(xmppid->uid);
+                p_pgpkey->fp = strdup(sub->fpr);
+                if (sub->can_encrypt) p_pgpkey->encrypt = TRUE;
+                if (sub->can_authenticate) p_pgpkey->authenticate = TRUE;
+                if (sub->can_certify) p_pgpkey->certify = TRUE;
+                if (sub->can_sign) p_pgpkey->sign = TRUE;
+
+                sub = sub->next;
+                while (sub) {
+                    if (sub->can_encrypt) p_pgpkey->encrypt = TRUE;
+                    if (sub->can_authenticate) p_pgpkey->authenticate = TRUE;
+                    if (sub->can_certify) p_pgpkey->certify = TRUE;
+                    if (sub->can_sign) p_pgpkey->sign = TRUE;
+
+                    sub = sub->next;
+                }
+
+                g_hash_table_insert(result, strdup(p_pgpkey->name), p_pgpkey);
+
+            }
+            gpgme_key_unref(key);
+            error = gpgme_op_keylist_next(ctx, &key);
+        }
+    }
+    gpgme_release(ctx);
+
+    //autocomplete_clear(key_ac);
+    //    GList *ids = g_hash_table_get_keys(result);
+    //    GList *curr = ids;
+    //    while (curr) {
+    //        ProfPGPKey *key = g_hash_table_lookup(result, curr->data);
+    //        autocomplete_add(key_ac, key->id);
+    //        curr = curr->next;
+    //    }
+    //    g_list_free(ids);
+
+    return result;
+
+}
+
+char* p_ox_gpg_signcrypt(const char* const sender_barejid, const char* const recipient_barejid , const char* const message) {
+  setlocale (LC_ALL, "");
+  gpgme_check_version (NULL);
+  gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
+  gpgme_ctx_t ctx;
+  gpgme_error_t error = gpgme_new (&ctx);
+  if(GPG_ERR_NO_ERROR != error ) {
+    printf("gpgme_new: %d\n", error);
+    return NULL;
+  }
+  error = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OPENPGP);
+  if(error != 0) {
+    log_error("GpgME Error: %s", gpgme_strerror(error));
+  }
+  gpgme_set_armor(ctx,0);
+  gpgme_set_textmode(ctx,0);
+  gpgme_set_offline(ctx,1);
+  gpgme_set_keylist_mode(ctx, GPGME_KEYLIST_MODE_LOCAL);
+  if(error != 0) {
+    log_error("GpgME Error: %s", gpgme_strerror(error));
+  }
+
+  gpgme_key_t recp[3];
+  recp[0] = NULL,
+  recp[1] = NULL;
+
+  char* xmpp_jid_me = alloca( (strlen(sender_barejid)+6) * sizeof(char) );
+  char* xmpp_jid_recipient =  alloca( (strlen(recipient_barejid)+6) * sizeof(char) );
+
+  strcpy(xmpp_jid_me, "xmpp:");
+  strcpy(xmpp_jid_recipient, "xmpp:");
+  strcat(xmpp_jid_me, sender_barejid);
+  strcat(xmpp_jid_recipient,recipient_barejid);
+
+  gpgme_signers_clear(ctx);
+
+  // lookup own key
+  recp[0] =  _ox_key_lookup(sender_barejid, TRUE);
+  if(error != 0) {
+    log_error("Key not found for %s. GpgME Error: %s", xmpp_jid_me, gpgme_strerror(error));
+    return NULL;
+  }
+
+  error = gpgme_signers_add(ctx,recp[0]);
+  if(error != 0) {
+    log_error("gpgme_signers_add %s. GpgME Error: %s", xmpp_jid_me, gpgme_strerror(error));
+    return NULL;
+  }
+
+
+  // lookup key of recipient
+  recp[1] =  _ox_key_lookup(recipient_barejid, FALSE);
+  if(error != 0) {
+    log_error("Key not found for %s. GpgME 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);
+
+  gpgme_encrypt_flags_t flags = 0;
+
+  gpgme_data_t plain;
+  gpgme_data_t cipher;
+
+  error = gpgme_data_new (&plain);
+  if(error != 0) {
+    log_error("GpgME Error: %s", gpgme_strerror(error));
+    return NULL;
+  }
+
+  error = gpgme_data_new_from_mem(&plain, message, strlen(message),0);
+  if(error != 0) {
+    log_error("GpgME Error: %s", gpgme_strerror(error));
+    return NULL;
+  }
+  error = gpgme_data_new (&cipher);
+  if(error != 0) {
+    log_error("GpgME Error: %s", gpgme_strerror(error));
+    return NULL;
+  }
+
+  error = gpgme_op_encrypt_sign ( ctx, recp, flags, plain, cipher);
+  if(error != 0) {
+    log_error("GpgME Error: %s", gpgme_strerror(error));
+    return NULL;
+  }
+
+  size_t len;
+  char *cipher_str = gpgme_data_release_and_get_mem(cipher, &len);
+  char* result = g_base64_encode( (unsigned char*) cipher_str,len);
+  gpgme_key_release (recp[0]);
+  gpgme_key_release (recp[1]);
+  gpgme_release (ctx);
+  return result;
+
+}
+
+gboolean ox_is_private_key_available(const char *const barejid) {
+    g_assert(barejid);
+    gboolean result = FALSE;
+
+    gpgme_key_t key = _ox_key_lookup(barejid, TRUE);
+    if(key) {
+        if (_ox_key_is_usable(key, barejid, TRUE) ) {
+            result = TRUE;
+        }
+    gpgme_key_unref(key);
+    }
+
+    return result;
+}
+
+gboolean ox_is_public_key_available(const char *const barejid) {
+    g_assert(barejid);
+    gboolean result = FALSE;
+    gpgme_key_t key = _ox_key_lookup(barejid, FALSE);
+    if(key) {
+        if (_ox_key_is_usable(key, barejid, FALSE) ) {
+            result = TRUE;
+        }
+    gpgme_key_unref(key);
+    }
+    return result;
+}
+
+
 static char*
 _remove_header_footer(char *str, const char *const footer)
 {
@@ -837,3 +1053,126 @@ _save_pubkeys(void)
     g_chmod(pubsloc, S_IRUSR | S_IWUSR);
     g_free(g_pubkeys_data);
 }
+
+static gpgme_key_t _ox_key_lookup(const char *const barejid, gboolean secret_only) {
+    g_assert(barejid);
+    log_debug("Looking for %s key: %s", secret_only == TRUE ? "Private" : "Public",   barejid);
+    gpgme_key_t key = NULL;
+    gpgme_error_t error;
+
+    gpgme_ctx_t ctx;
+    error = gpgme_new(&ctx);
+
+    if (error) {
+        log_error("OX - gpgme_new failed: %s %s", gpgme_strsource(error), gpgme_strerror(error));
+        return NULL;
+    }
+
+    error = gpgme_op_keylist_start(ctx, NULL, secret_only);
+    if (error == GPG_ERR_NO_ERROR) {
+        error = gpgme_op_keylist_next(ctx, &key);
+        if ( error != GPG_ERR_EOF && error != GPG_ERR_NO_ERROR)  {
+            log_error("OX: gpgme_op_keylist_next %s %s", gpgme_strsource(error), gpgme_strerror(error));
+            return NULL;
+        }
+
+        GString* xmppuri = g_string_new("xmpp:");
+        g_string_append(xmppuri,barejid);
+
+        while (!error) {
+            // Looking for XMPP URI UID
+            gpgme_user_id_t uid = key->uids;
+
+            while ( uid ) {
+                if( uid->name && strlen(uid->name) >= 10 ) {
+                    if( g_strcmp0(uid->name, xmppuri->str) == 0 ) {
+                        gpgme_release(ctx);
+                        return key;
+                    }
+                }
+                uid = uid->next;
+            }
+            gpgme_key_unref(key);
+            error = gpgme_op_keylist_next(ctx, &key);
+        }
+    }
+    gpgme_release(ctx);
+
+    return key;
+}
+
+static gboolean _ox_key_is_usable(gpgme_key_t key, const char *const barejid, gboolean secret) {
+    gboolean result = TRUE;
+
+    if(key->revoked) result = FALSE;
+    if(key->expired) result = FALSE;
+    if(key->disabled) result = FALSE;
+
+    return result;
+}
+
+/*!
+ * @brief XMPP-OX: Decrypt OX Message.
+ *
+ *
+ *
+ * @param base64  base64_encode OpenPGP message.
+ *
+ * @result decrypt XMPP OX Message NULL terminated C-String
+ */
+char* p_ox_gpg_decrypt(char* base64) {
+  setlocale (LC_ALL, "");
+  gpgme_check_version (NULL);
+  gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
+  gpgme_ctx_t ctx;
+  gpgme_error_t error = gpgme_new (&ctx);
+  if(GPG_ERR_NO_ERROR != error ) {
+    printf("gpgme_new: %d\n", error);
+    return NULL;
+  }
+  error = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OPENPGP);
+  if(error != 0) {
+    log_error("GpgME Error: %s", gpgme_strerror(error));
+  }
+  gpgme_set_armor(ctx,0);
+  gpgme_set_textmode(ctx,0);
+  gpgme_set_offline(ctx,1);
+  gpgme_set_keylist_mode(ctx, GPGME_KEYLIST_MODE_LOCAL);
+  if(error != 0) {
+    log_error("GpgME Error: %s", gpgme_strerror(error));
+  }
+
+  gpgme_data_t plain = NULL;
+  gpgme_data_t cipher = NULL;
+
+  gsize s;
+  guchar* encypted = g_base64_decode(base64, &s);
+  error = gpgme_data_new_from_mem(&cipher, (char*)encypted, s,0);
+  if(error != 0) {
+    log_error("GpgME Error gpgme_data_new_from_mem: %s", gpgme_strerror(error));
+    return NULL;
+  }
+
+  error = gpgme_data_new (&plain);
+  if(error != 0) {
+    log_error("GpgME Error: %s", gpgme_strerror(error));
+    return NULL;
+  }
+
+  error = gpgme_op_decrypt_verify(ctx, cipher, plain);
+  if(error != 0) {
+    log_error("GpgME Error gpgme_op_decrypt: %s", gpgme_strerror(error));
+    error = gpgme_op_decrypt(ctx, cipher, plain);
+    if ( error  != 0 ) {
+        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);
+  result[len] = '\0';
+  return result;
+}
+
+
diff --git a/src/pgp/gpg.h b/src/pgp/gpg.h
index 0417c8a5..3eae6032 100644
--- a/src/pgp/gpg.h
+++ b/src/pgp/gpg.h
@@ -72,4 +72,20 @@ char* p_gpg_autocomplete_key(const char *const search_str, gboolean previous, vo
 void p_gpg_autocomplete_key_reset(void);
 char* p_gpg_format_fp_str(char *fp);
 
+char* p_ox_gpg_signcrypt(const char* const sender_barejid, const char* const recipient_barejid , const char* const message);
+
+char* p_ox_gpg_decrypt(char* base64);
+
+/*!
+ * \brief List of public keys with xmpp-URI.
+ *
+ * @returns GHashTable* with GString* xmpp-uri and ProfPGPKey* value. Empty
+ * hash, if there is no key. NULL in case of error.
+ *
+ */
+GHashTable* ox_gpg_public_keys(void);
+
+gboolean ox_is_private_key_available(const char *const barejid);
+gboolean ox_is_public_key_available(const char *const barejid);
+
 #endif