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
This commit is contained in:
committed by
Michael Vetter
parent
3afd854dc8
commit
2c94ee5a88
339
src/pgp/gpg.c
339
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user