Archive of RubyForge sup-devel mailing list
 help / color / mirror / Atom feed
From: Michael Stapelberg <michael+sup@stapelberg.de>
To: Rich Lane <rlane@club.cc.cmu.edu>
Cc: sup-devel <sup-devel@rubyforge.org>
Subject: Re: [sup-devel] [PATCH] Implement inline GPG (updated)
Date: Fri, 12 Mar 2010 12:02:00 +0100	[thread overview]
Message-ID: <1268390058-sup-9413@midna.zekjur.net> (raw)
In-Reply-To: <1268368142-sup-4547@zyrg.net>

[-- Attachment #1: Type: text/plain, Size: 815 bytes --]

Hi Rich,

Excerpts from Rich Lane's message of Fr Mär 12 05:43:38 +0100 2010:
> Since the regexes only match whole lines, why not just do string
> comparisons? I'd also like those strings to be constants but I won't
> insist on that. 
Good point, I changed that.

> The body assignment should be a ternary.
I avoided that because the line gets incredibly long then (91 characters
vs. 79 characters inside the if. Are you sure you want that ternary?
If so, please just change it yourself.

> I really dislike the flip-flop operator but it looks like the best way
> to do this. Please package those selects into a commented Enumerable
> method.
Done.

> Please factor your two cases in message_to_chunks into very well
> documented methods. message_to_chunks is already too complicated.
Done.

Best regards,
Michael

[-- Attachment #2: 0001-Implement-inline-GPG.patch --]
[-- Type: application/octet-stream, Size: 8476 bytes --]

From 827d72529b46d0930bf3ee721284a8ec543ecbc5 Mon Sep 17 00:00:00 2001
From: Michael Stapelberg <michael@stapelberg.de>
Date: Tue, 9 Mar 2010 17:40:48 +0100
Subject: [PATCH] Implement inline GPG
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The SIG_PATTERN had to be changed because GPG, when clearsigning (which
is what happens when you send inline GPG messages), kind of escapes
lines beginning with dashes (so that the -----BEGIN PGP MESSAGE-----
lines don’t get messed up). Therefore, signatures, starting with "-- "
will be escaped as "- -- ". The manpage of GPG states that the process
of clearsigning is not reversible. Thus, there is no method in GPG to
get the original message.
---
 lib/sup/crypto.rb  |   62 ++++++++++++++++++++++++++++++++-------------------
 lib/sup/message.rb |   50 +++++++++++++++++++++++++++++++++++++++--
 lib/sup/util.rb    |    5 ++++
 3 files changed, 91 insertions(+), 26 deletions(-)

diff --git a/lib/sup/crypto.rb b/lib/sup/crypto.rb
index 5ece6d9..abbcb98 100644
--- a/lib/sup/crypto.rb
+++ b/lib/sup/crypto.rb
@@ -97,18 +97,24 @@ EOS
     encrypt from, to, payload, true
   end
 
-  def verify payload, signature # both RubyMail::Message objects
+  def verify payload, signature, detached=true # both RubyMail::Message objects
     return unknown_status(cant_find_binary) unless @cmd
 
-    payload_fn = Tempfile.new "redwood.payload"
-    payload_fn.write format_payload(payload)
-    payload_fn.close
+    if detached
+      payload_fn = Tempfile.new "redwood.payload"
+      payload_fn.write format_payload(payload)
+      payload_fn.close
+    end
 
     signature_fn = Tempfile.new "redwood.signature"
     signature_fn.write signature.decode
     signature_fn.close
 
-    output = run_gpg "--verify #{signature_fn.path} #{payload_fn.path}"
+    if detached
+      output = run_gpg "--verify #{signature_fn.path} #{payload_fn.path}"
+    else
+      output = run_gpg "--verify #{signature_fn.path}"
+    end
     output_lines = output.split(/\n/)
 
     if output =~ /^gpg: (.* signature from .*$)/
@@ -123,7 +129,7 @@ EOS
   end
 
   ## returns decrypted_message, status, desc, lines
-  def decrypt payload # a RubyMail::Message object
+  def decrypt payload, armor=false # a RubyMail::Message object
     return unknown_status(cant_find_binary) unless @cmd
 
     payload_fn = Tempfile.new "redwood.payload"
@@ -153,24 +159,34 @@ EOS
       Chunk::CryptoNotice.new :invalid, $1, message.split("\n")
     end
 
-    # This is gross. This decrypted payload could very well be a multipart
-    # element itself, as opposed to a simple payload. For example, a
-    # multipart/signed element, like those generated by Mutt when encrypting
-    # and signing a message (instead of just clearsigning the body).
-    # Supposedly, decrypted_payload being a multipart element ought to work
-    # out nicely because Message::multipart_encrypted_to_chunks() runs the
-    # decrypted message through message_to_chunks() again to get any
-    # children. However, it does not work as intended because these inner
-    # payloads need not carry a MIME-Version header, yet they are fed to
-    # RMail as a top-level message, for which the MIME-Version header is
-    # required. This causes for the part not to be detected as multipart,
-    # hence being shown as an attachment. If we detect this is happening,
-    # we force the decrypted payload to be interpreted as MIME.
-    msg = RMail::Parser.read output
-    if msg.header.content_type =~ %r{^multipart/} && !msg.multipart?
-      output = "MIME-Version: 1.0\n" + output
-      output.force_encoding Encoding::ASCII_8BIT if output.respond_to? :force_encoding
+    if armor
+      msg = RMail::Message.new
+      # Look for Charset, they are put before the base64 crypted part
+      charsets = payload.body.split("\n").grep(/^Charset:/)
+      if !charsets.empty? and charsets[0] =~ /^Charset: (.+)$/
+        output = Iconv.easy_decode($encoding, $1, output)
+      end
+      msg.body = output
+    else
+      # This is gross. This decrypted payload could very well be a multipart
+      # element itself, as opposed to a simple payload. For example, a
+      # multipart/signed element, like those generated by Mutt when encrypting
+      # and signing a message (instead of just clearsigning the body).
+      # Supposedly, decrypted_payload being a multipart element ought to work
+      # out nicely because Message::multipart_encrypted_to_chunks() runs the
+      # decrypted message through message_to_chunks() again to get any
+      # children. However, it does not work as intended because these inner
+      # payloads need not carry a MIME-Version header, yet they are fed to
+      # RMail as a top-level message, for which the MIME-Version header is
+      # required. This causes for the part not to be detected as multipart,
+      # hence being shown as an attachment. If we detect this is happening,
+      # we force the decrypted payload to be interpreted as MIME.
       msg = RMail::Parser.read output
+      if msg.header.content_type =~ %r{^multipart/} && !msg.multipart?
+        output = "MIME-Version: 1.0\n" + output
+        output.force_encoding Encoding::ASCII_8BIT if output.respond_to? :force_encoding
+        msg = RMail::Parser.read output
+      end
     end
     notice = Chunk::CryptoNotice.new :valid, "This message has been decrypted for display"
     [notice, sig, msg]
diff --git a/lib/sup/message.rb b/lib/sup/message.rb
index ebc73fc..30ccaf8 100644
--- a/lib/sup/message.rb
+++ b/lib/sup/message.rb
@@ -26,7 +26,13 @@ class Message
 
   QUOTE_PATTERN = /^\s{0,4}[>|\}]/
   BLOCK_QUOTE_PATTERN = /^-----\s*Original Message\s*----+$/
-  SIG_PATTERN = /(^-- ?$)|(^\s*----------+\s*$)|(^\s*_________+\s*$)|(^\s*--~--~-)|(^\s*--\+\+\*\*==)/
+  SIG_PATTERN = /(^(- )*-- ?$)|(^\s*----------+\s*$)|(^\s*_________+\s*$)|(^\s*--~--~-)|(^\s*--\+\+\*\*==)/
+
+  GPG_SIGNED_START = "-----BEGIN PGP SIGNED MESSAGE-----"
+  GPG_SIGNED_END = "-----END PGP SIGNED MESSAGE-----"
+  GPG_START = "-----BEGIN PGP MESSAGE-----"
+  GPG_END = "-----END PGP MESSAGE-----"
+  GPG_SIG_END = "-----BEGIN PGP SIGNATURE-----"
 
   MAX_SIG_DISTANCE = 15 # lines from the end
   DEFAULT_SUBJECT = ""
@@ -511,8 +517,46 @@ private
         ## if there's no charset, use the current encoding as the charset.
         ## this ensures that the body is normalized to avoid non-displayable
         ## characters
-        body = Iconv.easy_decode($encoding, m.charset || $encoding, m.decode) if m.body
-        text_to_chunks((body || "").normalize_whitespace.split("\n"), encrypted)
+        if m.body
+          body = Iconv.easy_decode($encoding, m.charset || $encoding, m.decode)
+        else
+          body = ""
+        end
+
+        ## Check for inline-PGP
+        chunks = inline_gpg_to_chunks body.split("\n")
+        return chunks if chunks
+
+        text_to_chunks(body.normalize_whitespace.split("\n"), encrypted)
+      end
+    end
+  end
+
+  ## looks for gpg signed (but not encrypted) inline  messages inside the
+  ## message body (there is no extra header for inline GPG) or for encrypted
+  ## (and possible signed) inline GPG messages
+  def inline_gpg_to_chunks lines
+    gpg = lines.between(GPG_SIGNED_START, GPG_SIGNED_END)
+    if !gpg.empty?
+      msg = RMail::Message.new
+      msg.body = gpg.join("\n")
+
+      sig = lines.between(GPG_SIGNED_START, GPG_SIG_END)
+      payload = RMail::Message.new
+      payload.body = sig[1, sig.count-2].join("\n")
+      return [CryptoManager.verify(nil, msg, false), message_to_chunks(payload)].flatten.compact
+    end
+
+    gpg = lines.between(GPG_START, GPG_END)
+    if !gpg.empty?
+      msg = RMail::Message.new
+      msg.body = gpg.join("\n")
+      notice, sig, decryptedm = CryptoManager.decrypt msg, true
+      if decryptedm # managed to decrypt
+        children = message_to_chunks(decryptedm, true)
+        return [notice, sig].compact + children
+      else
+        return [notice]
       end
     end
   end
diff --git a/lib/sup/util.rb b/lib/sup/util.rb
index ab32d7c..fb9e0c3 100644
--- a/lib/sup/util.rb
+++ b/lib/sup/util.rb
@@ -459,6 +459,11 @@ module Enumerable
   def max_of
     map { |e| yield e }.max
   end
+
+  ## returns all the entries which are equal to startline up to endline
+  def between startline, endline
+    select { |l| true if l == startline .. l == endline }
+  end
 end
 
 class Array
-- 
1.6.5


[-- Attachment #3: Type: text/plain, Size: 143 bytes --]

_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel

  reply	other threads:[~2010-03-12 11:02 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-02-18 11:40 [sup-devel] [PATCH] Implement inline GPG Michael Stapelberg
2010-02-26 21:24 ` Rich Lane
2010-02-27 13:11   ` Michael Stapelberg
2010-02-27 18:05     ` Rich Lane
2010-03-01 13:45       ` Michael Stapelberg
2010-03-01 14:36         ` Christian Dietrich
2010-03-01 16:49           ` Michael Stapelberg
2010-03-01 17:46             ` Christian Dietrich
2010-03-09 16:43               ` [sup-devel] [PATCH] Implement inline GPG (updated) Michael Stapelberg
2010-03-10 21:23                 ` Michael Stapelberg
2010-03-12  4:43                   ` Rich Lane
2010-03-12 11:02                     ` Michael Stapelberg [this message]
2010-03-15  5:19                       ` Rich Lane

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1268390058-sup-9413@midna.zekjur.net \
    --to=michael+sup@stapelberg.de \
    --cc=rlane@club.cc.cmu.edu \
    --cc=sup-devel@rubyforge.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox