# include # include # include "utils/gmime/gmime-compat.h" # include # include # include "astroid.hh" # include "config.hh" # include "crypto.hh" # include "utils/address.hh" # include "chunk.hh" namespace Astroid { Crypto::Crypto (ustring _protocol) { id = Chunk::nextid++; using std::endl; config = astroid->config ("crypto"); gpgpath = ustring (config.get ("gpg.path")); always_trust = config.get ("gpg.always_trust"); gpgenabled = config.get ("gpg.enabled"); LOG (debug) << "crypto: gpg: " << gpgpath; protocol = _protocol.lowercase (); if ((protocol == "application/pgp-encrypted" || protocol == "application/pgp-signature")) { isgpg = true; if (! gpgenabled) { ready = false; return; } create_gpg_context (); } else { LOG (error) << "crypto: unsupported protocol: " << protocol; ready = false; return; } ready = true; } Crypto::~Crypto () { LOG (debug) << "crypto: deconstruct."; /* if (slist) g_object_unref (slist); */ /* if (rlist) g_object_unref (rlist); */ if (decrypt_res) g_object_unref (decrypt_res); if (gpgctx) g_object_unref (gpgctx); } GMimeObject * Crypto::decrypt_and_verify (GMimeObject * part) { using std::endl; LOG (debug) << "crypto: decrypting and verifiying.."; decrypt_tried = true; if (!GMIME_IS_MULTIPART_ENCRYPTED (part)) { LOG (error) << "crypto: part is not encrypted."; return NULL; } GError *err = NULL; GMimeMultipartEncrypted * ep = GMIME_MULTIPART_ENCRYPTED (part); GMimeObject * dp = g_mime_multipart_encrypted_decrypt (ep, GMIME_DECRYPT_NONE, NULL, &decrypt_res, &err); /* GMimeDecryptResult and GMimeCertificates * * Only the certificates of the signature are fully populated, the certificates * listed in GMimeDecryptResult->recipients are only a list of the alleged * key ids of the receivers. These can be spoofed by the sender, or normally set to * 0x0 if the receivers should be anonymous (-R for gpg). * * It is not possible not have a certificate entry for a receiver, so it is always * possible to see how many recipients there are for a message. * * For decrypted messages the certificate only has the key_id field set, the rest * of the information has to be fetched manually (note that the key_id is for the * subkey that supports encryption - E). When the rest of the information is fetched * from an available public key we cannot be sure that this is really the recipient. * * * Encryption and Decryption: * * The option always_trust on the gpg context must be set if the local key is not * trusted (or considered valid). Otherwise it is not used for encryption-target. * * There is some confusion about the term 'trust' here since it normally means whether * you trust the keys signing of other keys, in this case it means 'is the key valid' * 'or is the key really the one from the recipient'. * */ if (decrypt_res) { rlist = g_mime_decrypt_result_get_recipients (decrypt_res); slist = g_mime_decrypt_result_get_signatures (decrypt_res); for (int i = 0; i < g_mime_certificate_list_length (rlist); i++) { GMimeCertificate * ce = g_mime_certificate_list_get_certificate (rlist, i); const char * c = NULL; ustring fp = (c = g_mime_certificate_get_fingerprint (ce), c ? c : ""); ustring nm = (c = g_mime_certificate_get_name (ce), c ? c : ""); ustring em = (c = g_mime_certificate_get_email (ce), c ? c : ""); ustring key = (c = g_mime_certificate_get_key_id (ce), c ? c : ""); LOG (debug) << "cr: encrypted for: " << nm << "(" << em << ") [" << fp << "] [" << key << "]"; } } if (dp == NULL) { LOG (error) << "crypto: failed to decrypt message: " << err->message; decrypted = false; decrypt_error = err->message; } else { LOG (info) << "crypto: successfully decrypted message."; decrypted = true; verify_tried = (slist != NULL); verified = verify_signature_list (slist); } return dp; } GMimeMessage * Crypto::decrypt_message (GMimeMessage * in) { GMimeStream * ins = GMIME_STREAM (g_mime_stream_mem_new ()); g_mime_object_write_to_stream (GMIME_OBJECT(in), NULL, ins); g_mime_stream_seek (ins, 0, GMIME_STREAM_SEEK_SET); GError * err = NULL; GMimeStream * outs = GMIME_STREAM (g_mime_stream_mem_new ()); GMimeDecryptResult * decrypt_res = g_mime_crypto_context_decrypt (gpgctx, GMIME_DECRYPT_NONE, NULL, ins, outs, &err); g_mime_stream_flush (outs); g_mime_stream_seek (outs, 0, GMIME_STREAM_SEEK_SET); g_object_unref (ins); /* check if message */ GMimeParser * parser = g_mime_parser_new_with_stream (outs); GMimeMessage * dp = g_mime_parser_construct_message (parser, NULL); g_object_unref (parser); g_object_unref (outs); if (decrypt_res) { rlist = g_mime_decrypt_result_get_recipients (decrypt_res); slist = g_mime_decrypt_result_get_signatures (decrypt_res); for (int i = 0; i < g_mime_certificate_list_length (rlist); i++) { GMimeCertificate * ce = g_mime_certificate_list_get_certificate (rlist, i); const char * c = NULL; ustring fp = (c = g_mime_certificate_get_fingerprint (ce), c ? c : ""); ustring nm = (c = g_mime_certificate_get_name (ce), c ? c : ""); ustring em = (c = g_mime_certificate_get_email (ce), c ? c : ""); ustring key = (c = g_mime_certificate_get_key_id (ce), c ? c : ""); LOG (debug) << "cr: encrypted for: " << nm << "(" << em << ") [" << fp << "] [" << key << "]"; } } if (dp == NULL) { LOG (error) << "crypto: failed to decrypt message: " << err->message; decrypted = false; decrypt_error = err->message; } else { LOG (info) << "crypto: successfully decrypted message."; decrypted = true; verify_tried = (slist != NULL); verified = verify_signature_list (slist); } return dp; } bool Crypto::verify_signature (GMimeObject * mo) { GError * err = NULL; verify_tried = true; slist = g_mime_multipart_signed_verify (GMIME_MULTIPART_SIGNED(mo), GMIME_VERIFY_NONE, &err); verified = verify_signature_list (slist); return verified; } bool Crypto::verify_signature_list (GMimeSignatureList * list) { if (list == NULL) return false; bool res = g_mime_signature_list_length (list) > 0; for (int i = 0; i < g_mime_signature_list_length (list); i++) { GMimeSignature * s = g_mime_signature_list_get_signature (list, i); res &= g_mime_signature_status_good(g_mime_signature_get_status (s)); } return res; } bool Crypto::encrypt (GMimeObject * mo, bool sign, ustring userid, InternetAddress * from, ustring to, GMimeMultipartEncrypted ** out, GError ** err) { return encrypt (mo, sign, userid, from, AddressList (to), out, err); } bool Crypto::encrypt (GMimeObject * mo, bool sign, ustring userid, InternetAddress * from, AddressList to, GMimeMultipartEncrypted ** out, GError ** err) { /* build receipients */ AddressList recp = to + Address (from); recp.remove_duplicates (); GPtrArray * recpa = g_ptr_array_sized_new (recp.size ()); std::vector ur; for (Address &a : recp.addresses) { ur.push_back (a.email ()); } LOG (debug) << "cr: encrypting for: "; for (ustring &u : ur) { g_ptr_array_add (recpa, (gpointer) u.c_str ()); LOG (debug) << u << " "; } *out = g_mime_multipart_encrypted_encrypt ( gpgctx, mo, sign, userid.c_str (), # if (GMIME_MAJOR_VERSION < 3) GMIME_ENCRYPT_NONE, # else always_trust ? GMIME_ENCRYPT_ALWAYS_TRUST : GMIME_ENCRYPT_NONE, # endif recpa, err); g_ptr_array_free (recpa, true); if (*out != NULL) { LOG (debug) << "crypto: successfully encrypted message."; return true; } else { LOG (debug) << "crypto: failed to encrypt message: " << (*err)->message; return false; } } bool Crypto::sign (GMimeObject * mo, ustring userid, GMimeMultipartSigned ** out, GError ** err) { *out = g_mime_multipart_signed_sign ( gpgctx, mo, userid.c_str (), err); if (*out != NULL) { LOG (debug) << "crypto: successfully signed message."; return true; } else { LOG (debug) << "crypto: failed to sign message: " << (*err)->message; return false; } } bool Crypto::create_gpg_context () { if (!astroid->in_test ()) { # if (GMIME_MAJOR_VERSION < 3) gpgctx = g_mime_gpg_context_new(NULL, gpgpath.length() ? gpgpath.c_str () : "gpg"); g_mime_gpg_context_set_use_agent ((GMimeGpgContext *) gpgctx, TRUE); g_mime_gpg_context_set_always_trust ((GMimeGpgContext *) gpgctx, always_trust); # else gpgctx = g_mime_gpg_context_new (); # endif } else { LOG (debug) << "crypto: in test"; # if (GMIME_MAJOR_VERSION < 3) gpgctx = g_mime_gpg_context_new(NULL, "gpg"); g_mime_gpg_context_set_use_agent ((GMimeGpgContext *) gpgctx, TRUE); g_mime_gpg_context_set_always_trust ((GMimeGpgContext *) gpgctx, TRUE); # else gpgctx = g_mime_gpg_context_new (); # endif } if (! gpgctx) { LOG (error) << "crypto: failed to create gpg context."; return false; } return true; } ustring Crypto::get_md5_digest (ustring str) { std::string cs = Glib::Checksum::compute_checksum (Glib::Checksum::ChecksumType::CHECKSUM_MD5, str); return cs; } gssize Crypto::get_md5_length () { return Glib::Checksum::get_length (Glib::Checksum::ChecksumType::CHECKSUM_MD5); } refptr Crypto::get_md5_digest_b (ustring str) { if (str.empty ()) { guint8 buffer[get_md5_length ()]; return Glib::Bytes::create (buffer, get_md5_length ()); } guint8 buffer[get_md5_length ()]; gsize len = get_md5_length (); Glib::Checksum chk (Glib::Checksum::ChecksumType::CHECKSUM_MD5); chk.update (str); chk.get_digest (buffer, &len); return Glib::Bytes::create (buffer, len); } }