[gmime: 21/27] Autocrypt header injection, alternate form (#32)



commit edc0723f07a495c60e61baea20825b7780e5c6b4
Author: dkg <dkg fifthhorseman net>
Date:   Mon Nov 13 22:50:53 2017 +0800

    Autocrypt header injection, alternate form (#32)
    
    * Autocrypt: create header without newlines
    
    When creating the string form, create it unwrapped (but wrappable,
    with sensible space for linebreaks).  Other parts later in the
    toolchain can do the wrapping as needed.
    
    * rename g_mime_autocrypt_header_get_string to g_mime_autocrypt_header_to_string
    
    * test Autocrypt header injection
    
    * add "gossip" argument to g_mime_autocrypt_header_to_string
    
    * add full round-trip test functions, including gossip headers
    
    Here we test and verify inserting and removing Autocrypt-Gossip
    headers, as well as including them inside the crypto layer.

 docs/reference/gmime-sections.txt |    2 +-
 gmime/gmime-autocrypt.c           |   13 ++-
 gmime/gmime-autocrypt.h           |    2 +-
 tests/test-autocrypt.c            |  222 +++++++++++++++++++++++++++++++++++--
 4 files changed, 225 insertions(+), 14 deletions(-)
---
diff --git a/docs/reference/gmime-sections.txt b/docs/reference/gmime-sections.txt
index 8cad81e..2f0825e 100644
--- a/docs/reference/gmime-sections.txt
+++ b/docs/reference/gmime-sections.txt
@@ -1611,7 +1611,7 @@ g_mime_autocrypt_header_set_keydata
 g_mime_autocrypt_header_get_effective_date
 g_mime_autocrypt_header_set_effective_date
 g_mime_autocrypt_header_is_complete
-g_mime_autocrypt_header_get_string
+g_mime_autocrypt_header_to_string
 g_mime_autocrypt_header_compare
 g_mime_autocrypt_header_clone
 GMimeAutocryptHeaderList
diff --git a/gmime/gmime-autocrypt.c b/gmime/gmime-autocrypt.c
index 2eed84c..104c27c 100644
--- a/gmime/gmime-autocrypt.c
+++ b/gmime/gmime-autocrypt.c
@@ -414,25 +414,30 @@ g_mime_autocrypt_header_is_complete (GMimeAutocryptHeader *ah)
 
 
 /**
- * g_mime_autocrypt_header_get_string:
+ * g_mime_autocrypt_header_to_string:
  * @ah: a #GMimeAutocryptHeader object
+ * @gossip: a #gboolean, indicating whether this header is for use with gossip
  *
  * Gets the string representation of the Autocrypt header, or %NULL on
  * error.  For example, it might return:
  *
  *     prefer-encrypt=mutual; addr=bob\@example.com; keydata=AAAB15BE...
  *
+ * If you are using this object to populate an Autocrypt-Gossip
+ * header, you should set @gossip to %TRUE (this will suppress
+ * inclusion of prefer-encrypt).
+ *
  * Returns: (transfer full): the string representation of the
  * Autocrypt header.
  **/
 char *
-g_mime_autocrypt_header_get_string (GMimeAutocryptHeader *ah)
+g_mime_autocrypt_header_to_string (GMimeAutocryptHeader *ah, gboolean gossip)
 {
        g_return_val_if_fail (GMIME_IS_AUTOCRYPT_HEADER (ah), NULL);
        if (!g_mime_autocrypt_header_is_complete (ah))
                return NULL;
        char *pe = "";
-       if (ah->prefer_encrypt == GMIME_AUTOCRYPT_PREFER_ENCRYPT_MUTUAL)
+       if (!gossip && ah->prefer_encrypt == GMIME_AUTOCRYPT_PREFER_ENCRYPT_MUTUAL)
                pe = "prefer-encrypt=mutual; ";
        const char *addr = internet_address_mailbox_get_addr (ah->address);
        GPtrArray *lines = g_ptr_array_new_with_free_func (g_free);
@@ -466,7 +471,7 @@ g_mime_autocrypt_header_get_string (GMimeAutocryptHeader *ah)
 
        g_ptr_array_add (lines, NULL);
 
-       char *ret = g_strjoinv ("\r\n ", (gchar**)(lines->pdata));
+       char *ret = g_strjoinv (" ", (gchar**)(lines->pdata));
        g_ptr_array_unref (lines);
        return ret;
 }
diff --git a/gmime/gmime-autocrypt.h b/gmime/gmime-autocrypt.h
index 3d0bcaf..c7eb3fc 100644
--- a/gmime/gmime-autocrypt.h
+++ b/gmime/gmime-autocrypt.h
@@ -107,7 +107,7 @@ GBytes *g_mime_autocrypt_header_get_keydata (GMimeAutocryptHeader *ah);
 void g_mime_autocrypt_header_set_effective_date (GMimeAutocryptHeader *ah, GDateTime *effective_date);
 GDateTime *g_mime_autocrypt_header_get_effective_date (GMimeAutocryptHeader *ah);
 
-char *g_mime_autocrypt_header_get_string (GMimeAutocryptHeader *ah);
+char *g_mime_autocrypt_header_to_string (GMimeAutocryptHeader *ah, gboolean gossip);
 gboolean g_mime_autocrypt_header_is_complete (GMimeAutocryptHeader *ah);
 
 int g_mime_autocrypt_header_compare (GMimeAutocryptHeader *ah1, GMimeAutocryptHeader *ah2);
diff --git a/tests/test-autocrypt.c b/tests/test-autocrypt.c
index 80418d1..0bbf443 100644
--- a/tests/test-autocrypt.c
+++ b/tests/test-autocrypt.c
@@ -94,8 +94,8 @@ const static struct _ah_gen_test gen_test_data[] = {
        { .addr = "test example org",
          .keydatacount = 102,
          .keybyte = '\013',
-         .txt = "addr=test example org; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
-         " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
+         .txt = "addr=test example org; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL"
+         " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL"
          " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL",
        }
 };
@@ -117,7 +117,7 @@ test_ah_generation (void)
                        const struct _ah_gen_test *test = gen_test_data + i;
                        ah = _gen_header (test);
 
-                       str = g_mime_autocrypt_header_get_string (ah);
+                       str = g_mime_autocrypt_header_to_string (ah, FALSE);
                        if (strcmp (test->txt, str)) {
                                fprintf (stderr, "expected[%u]: \n%s\n\ngot:\n%s\n", i,
                                         test->txt, str);
@@ -126,7 +126,7 @@ test_ah_generation (void)
                        GMimeAutocryptHeader *ah2 = g_mime_autocrypt_header_new_from_string (str);
                        gint cmp = g_mime_autocrypt_header_compare (ah, ah2);
                        if (cmp) {
-                               char *x = g_mime_autocrypt_header_get_string (ah2);
+                               char *x = g_mime_autocrypt_header_to_string (ah2, FALSE);
                                fprintf (stderr, "after-rebuild[%u] (%d) \nexpected: \n%s\n\ngot:\n%s\n", i,
                                         cmp, test->txt, x);
                                g_free(x);
@@ -153,6 +153,23 @@ struct _ah_parse_test {
        const char *innerpart;
 };
 
+
+struct _ah_inject_test {
+       const char *name;
+       const struct _ah_gen_test *acheader;
+       const struct _ah_gen_test **gossipheaders;
+       const char **encrypt_to;
+       const char *before;
+       const char *after;
+       const char *inner_after;
+};
+
+const char * local_recipients[] =
+       {
+               "0x0D211DC5D9F4567271AC0582D8DECFBFC9346CD4",
+               NULL,
+       };
+
 const static struct _ah_gen_test alice_addr =
        { .addr = "alice example org",
          .keydatacount = 102,
@@ -195,6 +212,56 @@ const static struct _ah_gen_test *bob_and_carol[] = {
        NULL,
 };
 
+const static struct _ah_inject_test inject_test_data[] = {
+       { .name = "simple",
+         .acheader = &alice_addr,
+         .before = "From: alice example org\r\n"
+         "To: bob example org\r\n"
+         "Subject: A lovely day\r\n"
+         "Message-Id: <lovely-day example net>\r\n"
+         "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
+         "Mime-Version: 1.0\r\n"
+         "Content-Type: text/plain\r\n"
+         "\r\n"
+         "Isn't it a lovely day?\r\n",
+         .after = "From: alice example org\n"
+         "To: bob example org\n"
+         "Subject: A lovely day\n"
+         "Message-Id: <lovely-day example net>\n"
+         "Date: Mon, 23 Oct 2017 11:54:14 -0400\n"
+         "Mime-Version: 1.0\n"
+         "Content-Type: text/plain\n"
+         "Autocrypt: addr=alice example org; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\n"
+         " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\n"
+         " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\n"
+         "\n"
+         "Isn't it a lovely day?\n",
+       },
+       { .name = "gossip injection",
+         .acheader = &alice_addr,
+         .gossipheaders = bob_and_carol,
+         .encrypt_to = local_recipients,
+         .before = "From: alice example org\r\n"
+         "To: bob example org\r\n"
+         "Subject: A lovely day\r\n"
+         "Message-Id: <lovely-day example net>\r\n"
+         "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
+         "Mime-Version: 1.0\r\n"
+         "Content-Type: text/plain\r\n"
+         "\r\n"
+         "Isn't it a lovely day?\r\n",
+         .inner_after = "Content-Type: text/plain\n"
+         "Autocrypt-Gossip: addr=bob example org; keydata=W1tbW1tbW1tbW1tbW1tbW1tbW1tb\n"
+         " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\n"
+         " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\n"
+         "Autocrypt-Gossip: addr=carol example org; keydata=WVlZWVlZWVlZWVlZWVlZWVlZWVlZ\n"
+         " WVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZ\n"
+         " WVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZ\n"
+         "\n"
+         "Isn't it a lovely day?\n",
+       },
+};
+
 const static struct _ah_parse_test parse_test_data[] = {
        { .name = "simple",
          .acheader = &alice_addr,
@@ -606,6 +673,141 @@ const static struct _ah_parse_test parse_test_data[] = {
 };
 
 
+
+
+static void
+test_ah_injection (void)
+{
+       unsigned int i;
+       for (i = 0; i < G_N_ELEMENTS(inject_test_data); i++) {
+               GMimeAutocryptHeader *ah = NULL;
+               char *str = NULL;
+               GMimeStream *stream = NULL;
+               GMimeParser *parser = NULL;
+               GMimeMessage *before = NULL;
+               GMimeStream *afterstream = NULL;
+               GByteArray *after = NULL;
+               GMimeAutocryptHeaderList *ahl = NULL;
+               GMimeCryptoContext *ctx = NULL;
+               GPtrArray *recip = NULL;
+               GMimeMultipartEncrypted *encrypted = NULL;
+               GMimeObject *cleartext = NULL;
+               GMimeStream *innerafterstream = NULL;
+               GByteArray *innerafter = NULL;
+               GError *err = NULL;
+               try {
+                       const struct _ah_inject_test *test = inject_test_data + i;
+                       testsuite_check ("Autocrypt injection[%u] (%s)", i, test->name);
+
+                       stream = g_mime_stream_mem_new_with_buffer (test->before, strlen(test->before));
+                       parser = g_mime_parser_new_with_stream (stream);
+                       before = g_mime_parser_construct_message (parser, NULL);
+
+                       if (test->acheader) {
+                               ah = _gen_header (test->acheader);
+                               g_mime_object_set_header (GMIME_OBJECT (before), "Autocrypt",
+                                                        g_mime_autocrypt_header_to_string (ah, FALSE), NULL);
+                       }
+
+                       if (test->encrypt_to) {
+                               ctx = g_mime_gpg_context_new ();
+                               recip = g_ptr_array_new ();
+                               for (int r = 0; test->encrypt_to[r]; r++)
+                                       g_ptr_array_add (recip, (gpointer)(test->encrypt_to[r]));
+                               /* get_mime_part is "transfer none" so mainpart does not need to be cleaned 
up */
+                               GMimeObject *mainpart = g_mime_message_get_mime_part (before);
+                               if (!mainpart) {
+                                       throw (exception_new ("failed to find main part!\n"));
+                               }
+                               if (test->gossipheaders) {
+                                       ahl = _gen_header_list (test->gossipheaders);
+                                       for (int ix = 0; ix < g_mime_autocrypt_header_list_get_count (ahl); 
ix++) {
+                                               g_mime_object_append_header (mainpart, "Autocrypt-Gossip",
+                                                                            
g_mime_autocrypt_header_to_string (g_mime_autocrypt_header_list_get_header_at (ahl, ix), TRUE), NULL);
+                                       }
+                               }
+                               
+                               encrypted = g_mime_multipart_encrypted_encrypt (ctx, mainpart, TRUE, NULL,
+                                                                               GMIME_ENCRYPT_ALWAYS_TRUST,
+                                                                               recip, &err);
+                               if (!encrypted) {
+                                       throw (exception_new ("failed to encrypt: %s", err->message));
+                               }
+                               g_mime_message_set_mime_part (before, GMIME_OBJECT (encrypted));
+                       }
+
+                       if (test->after) {
+                               afterstream = g_mime_stream_mem_new ();
+                               g_mime_object_write_to_stream (GMIME_OBJECT (before), NULL, afterstream);
+
+                               after = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (afterstream));
+                               
+                               gchar *got = (gchar*)after->data;
+                               if (memcmp (got, test->after, strlen(test->after))) {
+                                       fprintf (stderr, "Expected: %s\nGot: %s\n", test->after, got);
+                                       throw (exception_new ("failed to match"));
+                               }
+                       }
+                       if (test->inner_after) {
+                               if (!encrypted) {
+                                       throw (exception_new ("inner_after, but no encrypted part!\n"));
+                               }
+                               
+                               cleartext = g_mime_multipart_encrypted_decrypt (encrypted,
+                                                                               GMIME_DECRYPT_NONE,
+                                                                               NULL, NULL, &err);
+                               if (!cleartext) {
+                                       throw (exception_new ("decryption failed: %s!\n", err->message));
+                               }
+                               innerafterstream = g_mime_stream_mem_new ();
+                               g_mime_object_write_to_stream (cleartext, NULL, innerafterstream);
+
+                               innerafter = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM 
(innerafterstream));
+                               
+                               gchar *got = (gchar*)innerafter->data;
+                               if (memcmp (got, test->inner_after, strlen(test->inner_after))) {
+                                       fprintf (stderr, "Expected: %s\nGot: %s\n", test->inner_after, got);
+                                       throw (exception_new ("failed to match"));
+                               }
+                       }
+                       testsuite_check_passed ();
+               } catch (ex) {
+                       testsuite_check_failed ("autocrypt header injection failed: %s", ex->message);
+               } finally;
+               if (ah)
+                       g_object_unref (ah);
+               if (stream)
+                       g_object_unref (stream);
+               if (parser)
+                       g_object_unref (parser);
+               if (before)
+                       g_object_unref (before);
+               if (afterstream)
+                       g_object_unref (afterstream);
+               if (after)
+                       g_byte_array_unref (after);
+               if (ahl)
+                       g_object_unref (ahl);
+               if (ctx)
+                       g_object_unref (ctx);
+               if (recip)
+                       g_ptr_array_unref (recip);
+               if (encrypted)
+                       g_object_unref (encrypted);
+               if (cleartext)
+                       g_object_unref (cleartext);
+               if (err)
+                       g_error_free (err);
+               if (innerafterstream)
+                       g_object_unref (innerafterstream);
+               if (innerafter)
+                       g_byte_array_unref (innerafter);
+               g_free (str);
+               str = NULL;
+       }
+}
+
+
 /* returns a non-NULL error if they're not the same */
 char *
 _acheaderlists_compare (GMimeAutocryptHeaderList *expected, GMimeAutocryptHeaderList *got)
@@ -621,8 +823,8 @@ _acheaderlists_compare (GMimeAutocryptHeaderList *expected, GMimeAutocryptHeader
                GMimeAutocryptHeader *ahg = g_mime_autocrypt_header_list_get_header_for_address (got, 
g_mime_autocrypt_header_get_address (ahe));
                gint cmp = g_mime_autocrypt_header_compare (ahe, ahg);
                if (cmp) {
-                       char *e = g_mime_autocrypt_header_get_string (ahe);
-                       char *g = g_mime_autocrypt_header_get_string (ahg);
+                       char *e = g_mime_autocrypt_header_to_string (ahe, FALSE);
+                       char *g = g_mime_autocrypt_header_to_string (ahg, FALSE);
                        char *ret = g_strdup_printf ("comparing <%s> got cmp = %d \nexpected: 
\n%s\n\ngot:\n%s\n",
                                                     internet_address_mailbox_get_idn_addr 
(g_mime_autocrypt_header_get_address (ahe)),
                                                     cmp, e, g);
@@ -672,7 +874,7 @@ test_ah_message_parse (void)
                                throw (exception_new ("failed to extract Autocrypt header from message!"));
                        if (ah_got && !ah_expected)
                                throw (exception_new ("extracted Autocrypt header when we shouldn't!\n%s\n",
-                                                     g_mime_autocrypt_header_get_string (ah_got)));
+                                                     g_mime_autocrypt_header_to_string (ah_got, FALSE)));
                        if (ah_expected)
                                if (g_mime_autocrypt_header_compare (ah_expected, ah_got))
                                        throw (exception_new ("Autocrypt header did not match"));
@@ -727,7 +929,7 @@ test_ah_message_parse (void)
 static void
 import_secret_key (void)
 {
-       /* generated with GnuPG via:
+       /* generated key 0x0D211DC5D9F4567271AC0582D8DECFBFC9346CD4 with GnuPG via:
         *
         * export GNUPGHOME=$(mktemp -d)
         * gpg --pinentry-mode loopback --passphrase '' --batch --quick-gen-key $(uuidgen)@autocrypt.org
@@ -867,6 +1069,10 @@ int main (int argc, char **argv)
        test_ah_message_parse ();
        testsuite_end ();
        
+       testsuite_start ("Autocrypt: inject headers");
+       test_ah_injection ();
+       testsuite_end ();
+
        g_mime_shutdown ();
        
 #ifdef ENABLE_CRYPTO


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]