[gmime] Added support for inline-PGP
- From: Jeffrey Stedfast <fejj src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gmime] Added support for inline-PGP
- Date: Tue, 14 Mar 2017 14:38:33 +0000 (UTC)
commit 16dbb8152318889bba3af91b8b96cb71bd45e7cc
Author: Jeffrey Stedfast <jestedfa microsoft com>
Date: Tue Mar 14 10:33:13 2017 -0400
Added support for inline-PGP
gmime/gmime-crypto-context.c | 11 +-
gmime/gmime-error.h | 11 +-
gmime/gmime-part.c | 289 ++++++++++++++++++++++++++++++++++++++----
gmime/gmime-part.h | 13 ++-
tests/test-pgpmime.c | 164 ++++++++++++++++++++++++
5 files changed, 448 insertions(+), 40 deletions(-)
---
diff --git a/gmime/gmime-crypto-context.c b/gmime/gmime-crypto-context.c
index a428b29..1d6a11f 100644
--- a/gmime/gmime-crypto-context.c
+++ b/gmime/gmime-crypto-context.c
@@ -439,10 +439,9 @@ crypto_encrypt (GMimeCryptoContext *ctx, gboolean sign, const char *userid, GMim
* g_mime_crypto_context_encrypt:
* @ctx: a #GMimeCryptoContext
* @sign: sign as well as encrypt
- * @userid: key id (or email address) to use when signing (assuming @sign is %TRUE)
- * @flags: a #GMimeEncryptFlags
- * @recipients: (element-type utf8): an array of recipient key ids
- * and/or email addresses
+ * @userid: the key id (or email address) to use when signing (assuming @sign is %TRUE)
+ * @flags: a set of #GMimeEncryptFlags
+ * @recipients: (element-type utf8): an array of recipient key ids and/or email addresses
* @istream: cleartext input stream
* @ostream: ciphertext output stream
* @err: a #GError
@@ -478,8 +477,8 @@ crypto_decrypt (GMimeCryptoContext *ctx, GMimeDecryptFlags flags, const char *se
/**
* g_mime_crypto_context_decrypt:
* @ctx: a #GMimeCryptoContext
- * @flags: a #GMimeDecryptFlags
- * @session_key: session key to use or %NULL
+ * @flags: a set of #GMimeDecryptFlags
+ * @session_key: the session key to use or %NULL
* @istream: input/ciphertext stream
* @ostream: output/cleartext stream
* @err: a #GError
diff --git a/gmime/gmime-error.h b/gmime/gmime-error.h
index 8bd63ae..7511057 100644
--- a/gmime/gmime-error.h
+++ b/gmime/gmime-error.h
@@ -48,12 +48,11 @@ extern GQuark gmime_error_quark;
/* errno is a positive value, so negative values should be safe to use */
enum {
- GMIME_ERROR_GENERAL = 0,
- GMIME_ERROR_NOT_SUPPORTED = -1,
- GMIME_ERROR_PARSE_ERROR = -2,
- GMIME_ERROR_PROTOCOL_ERROR = -3,
- GMIME_ERROR_BAD_PASSWORD = -4,
- GMIME_ERROR_NO_VALID_RECIPIENTS = -5
+ GMIME_ERROR_GENERAL = -1,
+ GMIME_ERROR_NOT_SUPPORTED = -2,
+ GMIME_ERROR_INVALID_OPERATION = -3,
+ GMIME_ERROR_PARSE_ERROR = -4,
+ GMIME_ERROR_PROTOCOL_ERROR = -5
};
diff --git a/gmime/gmime-part.c b/gmime/gmime-part.c
index ea06c6d..211ea27 100644
--- a/gmime/gmime-part.c
+++ b/gmime/gmime-part.c
@@ -28,6 +28,7 @@
#include <string.h>
#include "gmime-part.h"
+#include "gmime-error.h"
#include "gmime-utils.h"
#include "gmime-common.h"
#include "gmime-internal.h"
@@ -40,6 +41,7 @@
#include "gmime-filter-md5.h"
#include "gmime-table-private.h"
+#define _(x) x
#define d(x)
@@ -916,6 +918,55 @@ g_mime_part_get_filename (GMimePart *mime_part)
}
+static void
+set_content (GMimePart *mime_part, GMimeDataWrapper *content)
+{
+ if (mime_part->content)
+ g_object_unref (mime_part->content);
+
+ mime_part->content = content;
+ g_object_ref (content);
+}
+
+
+/**
+ * g_mime_part_set_content:
+ * @mime_part: a #GMimePart object
+ * @content: a #GMimeDataWrapper content object
+ *
+ * Sets the content on the mime part.
+ **/
+void
+g_mime_part_set_content (GMimePart *mime_part, GMimeDataWrapper *content)
+{
+ g_return_if_fail (GMIME_IS_PART (mime_part));
+
+ if (mime_part->content == content)
+ return;
+
+ GMIME_PART_GET_CLASS (mime_part)->set_content (mime_part, content);
+}
+
+
+/**
+ * g_mime_part_get_content:
+ * @mime_part: a #GMimePart object
+ *
+ * Gets the internal data-wrapper of the specified mime part, or %NULL
+ * on error.
+ *
+ * Returns: (transfer none): the data-wrapper for the mime part's
+ * contents.
+ **/
+GMimeDataWrapper *
+g_mime_part_get_content (GMimePart *mime_part)
+{
+ g_return_val_if_fail (GMIME_IS_PART (mime_part), NULL);
+
+ return mime_part->content;
+}
+
+
/**
* g_mime_part_set_openpgp_data:
* @mime_part: a #GMimePart
@@ -951,50 +1002,236 @@ g_mime_part_get_openpgp_data (GMimePart *mime_part)
}
-static void
-set_content (GMimePart *mime_part, GMimeDataWrapper *content)
+/**
+ * g_mime_part_openpgp_encrypt:
+ * @mime_part: a #GMimePart
+ * @sign: %TRUE if the content should also be signed; otherwise, %FALSE
+ * @userid: the key id (or email address) to use when signing (assuming @sign is %TRUE)
+ * @flags: a set of #GMimeEncryptFlags
+ * @recipients: (element-type utf8): an array of recipient key ids and/or email addresses
+ * @err: a #GError
+ *
+ * Encrypts (and optionally signs) the content of the @mime_part and then replaces
+ * the content with the new, encrypted, content.
+ *
+ * Returns: %TRUE on success or %FALSE on error.
+ **/
+gboolean
+g_mime_part_openpgp_encrypt (GMimePart *mime_part, gboolean sign, const char *userid,
+ GMimeEncryptFlags flags, GPtrArray *recipients, GError **err)
{
- if (mime_part->content)
- g_object_unref (mime_part->content);
+ GMimeStream *istream, *encrypted;
+ GMimeCryptoContext *ctx;
+ int rv;
- mime_part->content = content;
- g_object_ref (content);
+ g_return_val_if_fail (GMIME_IS_PART (mime_part), FALSE);
+
+ if (mime_part->content == NULL) {
+ g_set_error_literal (err, GMIME_ERROR, GMIME_ERROR_INVALID_OPERATION,
+ _("No content set on the MIME part."));
+ return FALSE;
+ }
+
+ if (!(ctx = g_mime_crypto_context_new ("application/pgp-encrypted"))) {
+ g_set_error_literal (err, GMIME_ERROR, GMIME_ERROR_NOT_SUPPORTED,
+ _("No crypto context registered for application/pgp-encrypted."));
+ return FALSE;
+ }
+
+ encrypted = g_mime_stream_mem_new ();
+ istream = g_mime_stream_mem_new ();
+ g_mime_data_wrapper_write_to_stream (mime_part->content, istream);
+ g_mime_stream_reset (istream);
+
+ rv = g_mime_crypto_context_encrypt (ctx, sign, userid, flags, recipients, istream, encrypted, err);
+ g_object_unref (istream);
+ g_object_unref (ctx);
+
+ if (rv == -1) {
+ g_object_unref (encrypted);
+ return FALSE;
+ }
+
+ g_mime_stream_reset (encrypted);
+
+ g_mime_data_wrapper_set_encoding (mime_part->content, GMIME_CONTENT_ENCODING_DEFAULT);
+ g_mime_data_wrapper_set_stream (mime_part->content, encrypted);
+ mime_part->encoding = GMIME_CONTENT_ENCODING_7BIT;
+ mime_part->openpgp = GMIME_OPENPGP_DATA_ENCRYPTED;
+ g_object_unref (encrypted);
+
+ return TRUE;
}
/**
- * g_mime_part_set_content:
- * @mime_part: a #GMimePart object
- * @content: a #GMimeDataWrapper content object
+ * g_mime_part_openpgp_decrypt:
+ * @mime_part: a #GMimePart
+ * @flags: a set of #GMimeDecryptFlags
+ * @session_key: the session key to use or %NULL
+ * @err: a #GError
*
- * Sets the content on the mime part.
+ * Decrypts the content of the @mime_part and then replaces the content with
+ * the new, decrypted, content.
+ *
+ * Returns: (transfer full): a #GMimeDecryptResult on success or %NULL on error.
**/
-void
-g_mime_part_set_content (GMimePart *mime_part, GMimeDataWrapper *content)
+GMimeDecryptResult *
+g_mime_part_openpgp_decrypt (GMimePart *mime_part, GMimeDecryptFlags flags, const char *session_key, GError
**err)
{
- g_return_if_fail (GMIME_IS_PART (mime_part));
+ GMimeStream *istream, *decrypted;
+ GMimeDecryptResult *result;
+ GMimeCryptoContext *ctx;
- if (mime_part->content == content)
- return;
+ g_return_val_if_fail (GMIME_IS_PART (mime_part), FALSE);
- GMIME_PART_GET_CLASS (mime_part)->set_content (mime_part, content);
+ if (mime_part->content == NULL) {
+ g_set_error_literal (err, GMIME_ERROR, GMIME_ERROR_INVALID_OPERATION,
+ _("No content set on the MIME part."));
+ return NULL;
+ }
+
+ if (!(ctx = g_mime_crypto_context_new ("application/pgp-encrypted"))) {
+ g_set_error_literal (err, GMIME_ERROR, GMIME_ERROR_NOT_SUPPORTED,
+ _("No crypto context registered for application/pgp-encrypted."));
+ return NULL;
+ }
+
+ decrypted = g_mime_stream_mem_new ();
+ istream = g_mime_stream_mem_new ();
+ g_mime_data_wrapper_write_to_stream (mime_part->content, istream);
+ g_mime_stream_reset (istream);
+
+ result = g_mime_crypto_context_decrypt (ctx, flags, session_key, istream, decrypted, err);
+ g_object_unref (istream);
+ g_object_unref (ctx);
+
+ if (result == NULL) {
+ g_object_unref (decrypted);
+ return NULL;
+ }
+
+ g_mime_stream_reset (decrypted);
+
+ g_mime_data_wrapper_set_encoding (mime_part->content, GMIME_CONTENT_ENCODING_DEFAULT);
+ g_mime_data_wrapper_set_stream (mime_part->content, decrypted);
+ mime_part->openpgp = GMIME_OPENPGP_DATA_NONE;
+ g_object_unref (decrypted);
+
+ return result;
}
/**
- * g_mime_part_get_content:
- * @mime_part: a #GMimePart object
+ * g_mime_part_openpgp_sign:
+ * @mime_part: a #GMimePart
+ * @userid: the key id (or email address) to use for signing
+ * @err: a #GError
*
- * Gets the internal data-wrapper of the specified mime part, or %NULL
- * on error.
+ * Signs the content of the @mime_part and then replaces the content with
+ * the new, signed, content.
*
- * Returns: (transfer none): the data-wrapper for the mime part's
- * contents.
+ * Returns: %TRUE on success or %FALSE on error.
**/
-GMimeDataWrapper *
-g_mime_part_get_content (GMimePart *mime_part)
+gboolean
+g_mime_part_openpgp_sign (GMimePart *mime_part, const char *userid, GError **err)
{
- g_return_val_if_fail (GMIME_IS_PART (mime_part), NULL);
+ GMimeStream *istream, *ostream;
+ GMimeCryptoContext *ctx;
+ int rv;
- return mime_part->content;
+ g_return_val_if_fail (GMIME_IS_PART (mime_part), FALSE);
+
+ if (mime_part->content == NULL) {
+ g_set_error_literal (err, GMIME_ERROR, GMIME_ERROR_INVALID_OPERATION,
+ _("No content set on the MIME part."));
+ return FALSE;
+ }
+
+ if (!(ctx = g_mime_crypto_context_new ("application/pgp-signature"))) {
+ g_set_error_literal (err, GMIME_ERROR, GMIME_ERROR_NOT_SUPPORTED,
+ _("No crypto context registered for application/pgp-signature."));
+ return FALSE;
+ }
+
+ ostream = g_mime_stream_mem_new ();
+ istream = g_mime_stream_mem_new ();
+ g_mime_data_wrapper_write_to_stream (mime_part->content, istream);
+ g_mime_stream_reset (istream);
+
+ rv = g_mime_crypto_context_sign (ctx, FALSE, userid, istream, ostream, err);
+ g_object_unref (istream);
+ g_object_unref (ctx);
+
+ if (rv == -1) {
+ g_object_unref (ostream);
+ return FALSE;
+ }
+
+ g_mime_stream_reset (ostream);
+
+ g_mime_data_wrapper_set_encoding (mime_part->content, GMIME_CONTENT_ENCODING_DEFAULT);
+ g_mime_data_wrapper_set_stream (mime_part->content, ostream);
+ mime_part->encoding = GMIME_CONTENT_ENCODING_7BIT;
+ mime_part->openpgp = GMIME_OPENPGP_DATA_SIGNED;
+ g_object_unref (ostream);
+
+ return TRUE;
+}
+
+
+/**
+ * g_mime_part_openpgp_verify:
+ * @mime_part: a #GMimePart
+ * @flags: a set of #GMimeVerifyFlags
+ * @err: a #GError
+ *
+ * Verifies the OpenPGP signature of the @mime_part and then replaces the content
+ * with the original, raw, content.
+ *
+ * Returns: (transfer full): a #GMimeSignatureList on success or %NULL on error.
+ **/
+GMimeSignatureList *
+g_mime_part_openpgp_verify (GMimePart *mime_part, GMimeVerifyFlags flags, GError **err)
+{
+ GMimeStream *istream, *extracted;
+ GMimeSignatureList *signatures;
+ GMimeCryptoContext *ctx;
+
+ g_return_val_if_fail (GMIME_IS_PART (mime_part), FALSE);
+
+ if (mime_part->content == NULL) {
+ g_set_error_literal (err, GMIME_ERROR, GMIME_ERROR_INVALID_OPERATION,
+ _("No content set on the MIME part."));
+ return NULL;
+ }
+
+ if (!(ctx = g_mime_crypto_context_new ("application/pgp-signature"))) {
+ g_set_error_literal (err, GMIME_ERROR, GMIME_ERROR_NOT_SUPPORTED,
+ _("No crypto context registered for application/pgp-signature."));
+ return NULL;
+ }
+
+ extracted = g_mime_stream_mem_new ();
+ istream = g_mime_stream_mem_new ();
+ g_mime_data_wrapper_write_to_stream (mime_part->content, istream);
+ g_mime_stream_reset (istream);
+
+ signatures = g_mime_crypto_context_verify (ctx, flags, istream, NULL, extracted, err);
+ g_object_unref (istream);
+ g_object_unref (ctx);
+
+ if (signatures == NULL) {
+ g_object_unref (extracted);
+ return NULL;
+ }
+
+ g_mime_stream_reset (extracted);
+
+ g_mime_data_wrapper_set_encoding (mime_part->content, GMIME_CONTENT_ENCODING_DEFAULT);
+ g_mime_data_wrapper_set_stream (mime_part->content, extracted);
+ mime_part->openpgp = GMIME_OPENPGP_DATA_NONE;
+ g_object_unref (extracted);
+
+ return signatures;
}
diff --git a/gmime/gmime-part.h b/gmime/gmime-part.h
index 38a9098..d2f720d 100644
--- a/gmime/gmime-part.h
+++ b/gmime/gmime-part.h
@@ -29,6 +29,7 @@
#include <gmime/gmime-encodings.h>
#include <gmime/gmime-filter-best.h>
#include <gmime/gmime-data-wrapper.h>
+#include <gmime/gmime-crypto-context.h>
G_BEGIN_DECLS
@@ -117,11 +118,19 @@ gboolean g_mime_part_is_attachment (GMimePart *mime_part);
void g_mime_part_set_filename (GMimePart *mime_part, const char *filename);
const char *g_mime_part_get_filename (GMimePart *mime_part);
+void g_mime_part_set_content (GMimePart *mime_part, GMimeDataWrapper *content);
+GMimeDataWrapper *g_mime_part_get_content (GMimePart *mime_part);
+
void g_mime_part_set_openpgp_data (GMimePart *mime_part, GMimeOpenPGPData data);
GMimeOpenPGPData g_mime_part_get_openpgp_data (GMimePart *mime_part);
-void g_mime_part_set_content (GMimePart *mime_part, GMimeDataWrapper *content);
-GMimeDataWrapper *g_mime_part_get_content (GMimePart *mime_part);
+gboolean g_mime_part_openpgp_encrypt (GMimePart *mime_part, gboolean sign, const char *userid,
+ GMimeEncryptFlags flags, GPtrArray *recipients, GError **err);
+GMimeDecryptResult *g_mime_part_openpgp_decrypt (GMimePart *mime_part, GMimeDecryptFlags flags,
+ const char *session_key, GError **err);
+
+gboolean g_mime_part_openpgp_sign (GMimePart *mime_part, const char *userid, GError **err);
+GMimeSignatureList *g_mime_part_openpgp_verify (GMimePart *mime_part, GMimeVerifyFlags flags, GError **err);
G_END_DECLS
diff --git a/tests/test-pgpmime.c b/tests/test-pgpmime.c
index 56f67a8..ce573fd 100644
--- a/tests/test-pgpmime.c
+++ b/tests/test-pgpmime.c
@@ -448,6 +448,146 @@ import_key (GMimeCryptoContext *ctx, const char *path)
}
}
+static GMimePart *
+create_mime_part (void)
+{
+ GMimeTextPart *part;
+
+ part = g_mime_text_part_new_with_subtype ("plain");
+ g_mime_text_part_set_text (part, "This is the body of the message...\n\n"
+ "Does inline-PGP support work properly?\n\n"
+ "Let's find out!\n\n");
+ g_mime_part_set_content_encoding ((GMimePart *) part, GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE);
+ g_mime_text_part_set_charset (part, "UTF-8");
+
+ return (GMimePart *) part;
+}
+
+static void
+test_openpgp_sign (void)
+{
+ GMimeSignatureList *signatures;
+ GMimeStream *original;
+ GMimePart *mime_part;
+ Exception *ex = NULL;
+ GError *err = NULL;
+ GByteArray *buf[2];
+
+ mime_part = create_mime_part ();
+ original = mime_part->content->stream;
+ g_object_ref (original);
+
+ if (!g_mime_part_openpgp_sign (mime_part, "no.user@no.domain", &err)) {
+ ex = exception_new ("signing failed: %s", err->message);
+ g_object_unref (mime_part);
+ g_object_unref (original);
+ g_error_free (err);
+ throw (ex);
+ }
+
+ if (g_mime_part_get_openpgp_data (mime_part) != GMIME_OPENPGP_DATA_SIGNED) {
+ g_object_unref (mime_part);
+ g_object_unref (original);
+
+ throw (exception_new ("OpenPGP data property not updated after signing"));
+ }
+
+ if (!(signatures = g_mime_part_openpgp_verify (mime_part, 0, &err))) {
+ ex = exception_new ("verifying failed: %s", err->message);
+ g_object_unref (mime_part);
+ g_object_unref (original);
+ g_error_free (err);
+ throw (ex);
+ }
+
+ if (g_mime_part_get_openpgp_data (mime_part) != GMIME_OPENPGP_DATA_NONE) {
+ g_object_unref (signatures);
+ g_object_unref (mime_part);
+ g_object_unref (original);
+
+ throw (exception_new ("OpenPGP data property not updated after verifying"));
+ }
+
+ buf[0] = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (original));
+ buf[1] = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (mime_part->content->stream));
+
+ if (buf[0]->len != buf[1]->len || memcmp (buf[0]->data, buf[1]->data, buf[0]->len) != 0)
+ ex = exception_new ("extracted data does not match original cleartext");
+
+ g_object_unref (signatures);
+ g_object_unref (mime_part);
+ g_object_unref (original);
+
+ if (ex != NULL)
+ throw (ex);
+}
+
+static void
+test_openpgp_encrypt (gboolean sign)
+{
+ GMimeDecryptResult *result;
+ GMimeStream *original;
+ GMimePart *mime_part;
+ Exception *ex = NULL;
+ GError *err = NULL;
+ GByteArray *buf[2];
+ GPtrArray *rcpts;
+
+ rcpts = g_ptr_array_new ();
+ g_ptr_array_add (rcpts, "no.user@no.domain");
+
+ mime_part = create_mime_part ();
+ original = mime_part->content->stream;
+ g_object_ref (original);
+
+ if (!g_mime_part_openpgp_encrypt (mime_part, sign, "no.user@no.domain",
GMIME_ENCRYPT_FLAGS_ALWAYS_TRUST, rcpts, &err)) {
+ ex = exception_new ("encrypting failed: %s", err->message);
+ g_ptr_array_free (rcpts, TRUE);
+ g_object_unref (mime_part);
+ g_object_unref (original);
+ g_error_free (err);
+ throw (ex);
+ }
+
+ g_ptr_array_free (rcpts, TRUE);
+
+ if (g_mime_part_get_openpgp_data (mime_part) != GMIME_OPENPGP_DATA_ENCRYPTED) {
+ g_object_unref (mime_part);
+ g_object_unref (original);
+
+ throw (exception_new ("OpenPGP data property not updated after encrypting"));
+ }
+
+ if (!(result = g_mime_part_openpgp_decrypt (mime_part, 0, NULL, &err))) {
+ ex = exception_new ("decrypting failed: %s", err->message);
+ g_object_unref (mime_part);
+ g_object_unref (original);
+ g_error_free (err);
+ throw (ex);
+ }
+
+ if (g_mime_part_get_openpgp_data (mime_part) != GMIME_OPENPGP_DATA_NONE) {
+ g_object_unref (mime_part);
+ g_object_unref (original);
+ g_object_unref (result);
+
+ throw (exception_new ("OpenPGP data property not updated after decrypting"));
+ }
+
+ buf[0] = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (original));
+ buf[1] = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (mime_part->content->stream));
+
+ if (buf[0]->len != buf[1]->len || memcmp (buf[0]->data, buf[1]->data, buf[0]->len) != 0)
+ ex = exception_new ("decrypted data does not match original cleartext");
+
+ g_object_unref (mime_part);
+ g_object_unref (original);
+ g_object_unref (result);
+
+ if (ex != NULL)
+ throw (ex);
+}
+
int main (int argc, char *argv[])
{
#ifdef ENABLE_CRYPTO
@@ -560,6 +700,30 @@ int main (int argc, char *argv[])
}
g_free (session_key);
+
+ testsuite_check ("rfc2440 sign");
+ try {
+ test_openpgp_sign ();
+ testsuite_check_passed ();
+ } catch (ex) {
+ testsuite_check_failed ("rfc2440 sign failed: %s", ex->message);
+ } finally;
+
+ testsuite_check ("rfc2440 encrypt");
+ try {
+ test_openpgp_encrypt (FALSE);
+ testsuite_check_passed ();
+ } catch (ex) {
+ testsuite_check_failed ("rfc2440 encrypt failed: %s", ex->message);
+ } finally;
+
+ testsuite_check ("rfc2440 sign+encrypt");
+ try {
+ test_openpgp_encrypt (TRUE);
+ testsuite_check_passed ();
+ } catch (ex) {
+ testsuite_check_failed ("rfc2440 sign+encrypt failed: %s", ex->message);
+ } finally;
#endif /* GPGME_VERSION NUMBER >= 0x010700 */
g_object_unref (ctx);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]