Subject: Re: [sup-devel] [PATCH] Converted crypto to use the gpgme gem

From: Tero Tilus <[email-masked]>
Date: Tue, 30 Nov 2010 08:22:48 +0200

Gaudenz Steinlin, 2010-11-29 11:41:
> As far as I understood the branch layout the flow of changes is
> master -> next -> release. So applying to next would mean it ends up
> in the next release (0.12).

Master is considered "stable" and next "unstable".  Releases are
tagged from master and small changes may go directly to master.  New
features are introduced in next and merged to master when they are
considered stable (enough).

Applying to next would mean it ends up in 0.13 (or later if there are
bugfix releases).

--
Tero Tilus ## 050 3635 235 ## http://tero.tilus.net/
_______________________________________________
Sup-devel mailing list
[email-masked]
http://rubyforge.org/mailman/listinfo/sup-devel

---

From: Gaudenz Steinlin <[email-masked]>
Date: Thu, 11 Nov 2010 10:09:18 +0100

Hi

Excerpts from Hamish D's message of Mon Nov 08 23:32:15 +0100 2010:
> OK, the second patch fixes the problem with the first patch.

I tried your patches and the problem with crash on missing keys seems
to be solved. However I have some more comments:

- With this patch the output of the gpg run is no longer available.
  Like this the plus sign in front of the message does not make sense.
  Is there a way to get at the gpg output with your approach?
- Signatures made by keys that are available in my keyring but aren't
  trusted are shown as valid. They should be specially marked!
  This is not a new problem. It also exists with the current code, but
  there you can access the gpg output which shows you that the
  signature is valid but not trusted.

Gaudenz

> 
> Hamish Downer
> 
> On 8 November 2010 11:21, Hamish D <[email-masked]> wrote:
> > Best hang fire on this patch. It appears to crash when verifying a signature
> > when the public key is not available. I'm investigating the problem and how
> > to fix it cleanly and I'll resubmit once I've done that.
> >
> > Hamish
> >
> > On Nov 6, 2010 8:08 PM, "Hamish D" <[email-masked]> wrote:
> >
> > I often find that loading long threads of encrypted messages (I have
> > several of over 10 messages and one of nearly 40) leads to lots of
> > flickering as the console replaces sup, sup comes back, the console
> > comes back again ... It is also very slow, and involves writing
> > decrypted messages to disk (if only temporarily) which could be a
> > security hole. So I've looked about and found the gpgme gem which
> > provides an API to use, and allows decryption entirely in memory.
> >
> > So I've rewritten lib/sup/crypto.rb to use gpgme. The functionality is
> > pretty much the same. Things I'm aware of that are different:
> >
> > * we can't set the signature algorithm, so we have to use whatever is
> > set in the user's preferences
> > * the gpg-args hook has been replaced by the gpg-options hook
> >
> > Other than that I think it is the same, although it took some work to
> > get the signature output to be the same. The other main difference is
> > that it's much faster and nicer now :)
> >
> > It could do with some testing - I don't have much in the way of
> > messages that cause gpg to complain, so if you do, please try opening
> > those messages with this code and see if the behaviour is reasonable -
> > no crashes, given messages about why your message was bad etc.
> >
> > Also I guess I should ask if people are happy to use this gem. Is it
> > hard to use on Macs? I guess I could rewrite this patch so it falls
> > back to the gpg binary if gpgme is not available ...
> >
> > To install this patch on Debian/Ubuntu you can either
> >
> > * apt-get install libgpgme-ruby
> > * apt-get install libgpgme11-dev; gem install gpgme
> >
> > Hamish Downer
> >
--
Ever tried. Ever failed. No matter.
Try again. Fail again. Fail better.
~ Samuel Beckett ~
_______________________________________________
Sup-devel mailing list
[email-masked]
http://rubyforge.org/mailman/listinfo/sup-devel

---

From: Hamish D <[email-masked]>
Date: Mon, 8 Nov 2010 11:21:12 +0000

Hamish

Hamish Downer

---

From: Hamish D <[email-masked]>
Date: Tue, 16 Nov 2010 14:20:03 +0000

> The "+" character is right in front of the string "Good signature from
> ...". If you move the cursor to that line and press enter it changes
> to a "-" character, but no additional text is shown. The gpg command
> output used to show up there when pressing enter.
>
> I expect there to be some additional output about the signature
> validation like keyid, signature date, trust level, ...

Right, now I know what you are referring to. It is a CryptoNotice
object. That didn't work in the first version of the patch, but I
fixed it in the patch I submitted the second time.

So with the patches submitted in the message with time stamp "8
November 2010 22:32" there will be lines when you expand, telling you
the key ID, the timestamp of the signature and all names and email
addresses associated with that key.

I am also working on having extra information generated when the key
is not trusted, but this is not done yet. And I am also working on a
hook where you can generate as much information as you want from the
signature for the CryptoNotice. Hopefully be ready to submit before
the weekend.

While doing this I'm wondering about the preferred way of submitting
patches that represent quite a bit of work. Should I use git rebase -i
to just have a single patch with all changes, or is it preferred to
have a series of smaller changes?

I also have my code at http://github.com/foobacca/sup - this patch is
being developed in the gpgme branch -
https://github.com/foobacca/sup/tree/gpgme

Hamish

---

From: Hamish D <[email-masked]>
Date: Sat, 6 Nov 2010 20:08:01 +0000

Hamish Downer
From 52441d1eb749bb1e3b5026e42a334e9c8f455833 Mon Sep 17 00:00:00 2001
From: Hamish Downer <[email-masked]>
Date: Fri, 5 Nov 2010 22:30:55 +0000
Subject: [PATCH] Converted crypto to use the gpgme gem

---
 bin/sup           |   11 +++
 lib/sup/crypto.rb |  231 ++++++++++++++++++++++++++++++-----------------------
 2 files changed, 141 insertions(+), 101 deletions(-)

diff --git a/bin/sup b/bin/sup
index 10be161..ad7a0d1 100755
--- a/bin/sup
+++ b/bin/sup
@@ -10,6 +10,13 @@ rescue LoadError
   no_ncursesw = true
 end
 
+no_gpgme = false
+begin
+  require 'gpgme'
+rescue LoadError
+  no_gpgme = true
+end
+
 require 'fileutils'
 require 'trollop'
 require "sup"; Redwood::check_library_version_against "git"
@@ -23,6 +30,10 @@ if no_ncursesw
   info "No 'ncursesw' gem detected. Install it for wide character support."
 end
 
+if no_gpgme
+  info "No 'gpgme' gem detected. Install it for email encryption, decryption and signatures."
+end
+
 $opts = Trollop::options do
   version "sup v#{Redwood::VERSION}"
   banner <<EOS
diff --git a/lib/sup/crypto.rb b/lib/sup/crypto.rb
index c7b57c1..9d21ea0 100644
--- a/lib/sup/crypto.rb
+++ b/lib/sup/crypto.rb
@@ -1,3 +1,8 @@
+begin
+  require 'gpgme'
+rescue LoadError
+end
+
 module Redwood
 
 class CryptoManager
@@ -11,76 +16,79 @@ class CryptoManager
     [:encrypt, "Encrypt only"]
   )
 
-  HookManager.register "gpg-args", <<EOS
-Runs before gpg is executed, allowing you to modify the arguments (most
+  HookManager.register "gpg-options", <<EOS
+Runs before gpg is called, allowing you to modify the options (most
 likely you would want to add something to certain commands, like
---trust-model always to signing/encrypting a message, but who knows).
+{:always_trust => true} to encrypting a message, but who knows).
 
 Variables:
-args: arguments for running GPG
+operation: what operation will be done ("sign", "encrypt", "decrypt" or "verify")
+options: a dictionary of values to be passed to GPGME
 
-Return value: the arguments for running GPG
+Return value: a dictionary to be passed to GPGME
 EOS
 
   def initialize
     @mutex = Mutex.new
 
-    bin = `which gpg`.chomp
-    @cmd = case bin
-    when /\S/
-      debug "crypto: detected gpg binary in #{bin}"
-      "#{bin} --quiet --batch --no-verbose --logger-fd 1 --use-agent"
-    else
-      debug "crypto: no gpg binary detected"
-      nil
+    # test if the gpgme gem is available
+    @gpgme_present = true
+    begin
+    GPGME.check_version({:protocol => GPGME::PROTOCOL_OpenPGP})
+    rescue NameError, GPGME::Error
+      @gpgme_present = false
     end
   end
 
-  def have_crypto?; [email-masked]? end
+  def have_crypto?; @gpgme_present end
 
   def sign from, to, payload
-    payload_fn = Tempfile.new "redwood.payload"
-    payload_fn.write format_payload(payload)
-    payload_fn.close
+    return unknown_status(cant_find_gpgme) unless @gpgme_present
 
-    sig_fn = Tempfile.new "redwood.signature"; sig_fn.close
+    gpg_opts = {:protocol => GPGME::PROTOCOL_OpenPGP, :armor => true, :textmode => true}
+    gpg_opts.merge(gen_sign_user_opts(from))
+    gpg_opts = HookManager.run("gpg-options", 
+                               {:operation => "sign", :options => gpg_opts}) || gpg_opts
 
-    sign_user_opts = gen_sign_user_opts from
-    message = run_gpg "--output #{sig_fn.path} --yes --armor --detach-sign --textmode --digest-algo sha256 #{sign_user_opts} #{payload_fn.path}", :interactive => true
-    unless $?.success?
-      info "Error while running gpg: #{message}"
+    begin
+      sig = GPGME.detach_sign(format_payload(payload), gpg_opts)
+    rescue GPGME::Error => exc
+      info "Error while running gpg: #{exc.message}"
       raise Error, "GPG command failed. See log for details."
     end
 
     envelope = RMail::Message.new
-    envelope.header["Content-Type"] = 'multipart/signed; protocol=application/pgp-signature; micalg=pgp-sha256'
+    envelope.header["Content-Type"] = 'multipart/signed; protocol=application/pgp-signature'
 
     envelope.add_part payload
-    signature = RMail::Message.make_attachment IO.read(sig_fn.path), "application/pgp-signature", nil, "signature.asc"
+    signature = RMail::Message.make_attachment sig, "application/pgp-signature", nil, "signature.asc"
     envelope.add_part signature
     envelope
   end
 
   def encrypt from, to, payload, sign=false
-    payload_fn = Tempfile.new "redwood.payload"
-    payload_fn.write format_payload(payload)
-    payload_fn.close
-
-    encrypted_fn = Tempfile.new "redwood.encrypted"; encrypted_fn.close
-
-    recipient_opts = (to + [ from ] ).map { |r| "--recipient '<#{r}>'" }.join(" ")
-    sign_opts = ""
-    sign_opts = "--sign --digest-algo sha256 " + gen_sign_user_opts(from) if sign
-    message = run_gpg "--output #{encrypted_fn.path} --yes --armor --encrypt --textmode #{sign_opts} #{recipient_opts} #{payload_fn.path}", :interactive => true
-    unless $?.success?
-      info "Error while running gpg: #{message}"
+    return unknown_status(cant_find_gpgme) unless @gpgme_present
+
+    gpg_opts = {:protocol => GPGME::PROTOCOL_OpenPGP, :armor => true, :textmode => true}
+    if sign
+      gpg_opts.merge(gen_sign_user_opts(from)) 
+      gpg_opts.merge({:sign => true})
+    end
+    gpg_opts = HookManager.run("gpg-options", 
+                               {:operation => "encrypt", :options => gpg_opts}) || gpg_opts
+    recipients = to + [from]
+
+    begin
+      cipher = GPGME.encrypt(recipients, format_payload(payload), gpg_opts)
+    rescue GPGME::Error => exc
+      info "Error while running gpg: #{exc.message}"
       raise Error, "GPG command failed. See log for details."
     end
 
     encrypted_payload = RMail::Message.new
     encrypted_payload.header["Content-Type"] = "application/octet-stream"
     encrypted_payload.header["Content-Disposition"] = 'inline; filename="msg.asc"'
-    encrypted_payload.body = IO.read(encrypted_fn.path)
+    encrypted_payload.body = cipher
 
     control = RMail::Message.new
     control.header["Content-Type"] = "application/pgp-encrypted"
@@ -99,70 +107,85 @@ EOS
     encrypt from, to, payload, true
   end
 
-  def verified_ok? output, rc
-    output_lines = output.split(/\n/)
-
-    if output =~ /^gpg: (.* signature from .*$)/
-      if rc == 0
-        Chunk::CryptoNotice.new :valid, $1, output_lines
-      else
-        Chunk::CryptoNotice.new :invalid, $1, output_lines
+  def verified_ok? verify_result
+    valid = true
+    unknown = false
+    output_lines = []
+
+    verify_result.signatures.each do |signature|
+      output_lines.push(sig_output_lines(signature))
+      output_lines.flatten!
+      err_code = GPGME::gpgme_err_code(signature.status)
+      if err_code == GPGME::GPG_ERR_BAD_SIGNATURE
+        valid = false 
+      elsif err_code != GPGME::GPG_ERR_NO_ERROR
+        valid = false
+        unknown = true
       end
-    elsif output_lines.length == 0 && rc == 0
-      # the message wasn't signed
+    end
+
+    if output_lines.length == 0
       Chunk::CryptoNotice.new :valid, "Encrypted message wasn't signed", output_lines
+    elsif valid
+      Chunk::CryptoNotice.new(:valid, simplify_sig_line(verify_result.signatures[0].to_s), output_lines)
+    elsif !unknown
+      Chunk::CryptoNotice.new(:invalid, simplify_sig_line(verify_result.signatures[0].to_s), output_lines)
     else
       unknown_status output_lines
     end
   end
 
   def verify payload, signature, detached=true # both RubyMail::Message objects
-    return unknown_status(cant_find_binary) unless @cmd
+    return unknown_status(cant_find_gpgme) unless @gpgme_present
 
+    gpg_opts = {:protocol => GPGME::PROTOCOL_OpenPGP}
+    gpg_opts = HookManager.run("gpg-options", 
+                               {:operation => "verify", :options => gpg_opts}) || gpg_opts
+    ctx = GPGME::Ctx.new(gpg_opts) 
+    sig_data = GPGME::Data.from_str signature.decode
     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
-
-    if detached
-      output = run_gpg "--verify #{signature_fn.path} #{payload_fn.path}"
+      signed_text_data = GPGME::Data.from_str(format_payload(payload))
+      plain_data = nil
     else
-      output = run_gpg "--verify #{signature_fn.path}"
+      signed_text_data = nil
+      plain_data = GPGME::Data.empty
     end
-
-    self.verified_ok? output, $?
+    begin
+      ctx.verify(sig_data, signed_text_data, plain_data)
+    rescue GPGME::Error => exc
+      return unknown_status exc.message 
+    end
+    self.verified_ok? ctx.verify_result
   end
 
   ## returns decrypted_message, status, desc, lines
   def decrypt payload, armor=false # a RubyMail::Message object
-    return unknown_status(cant_find_binary) unless @cmd
-
-    payload_fn = Tempfile.new(["redwood.payload", ".asc"])
-    payload_fn.write payload.to_s
-    payload_fn.close
-
-    output_fn = Tempfile.new "redwood.output"
-    output_fn.close
-
-    message = run_gpg "--output #{output_fn.path} --skip-verify --yes --decrypt #{payload_fn.path}", :interactive => true
-
-    unless $?.success?
-      info "Error while running gpg: #{message}"
-      return Chunk::CryptoNotice.new(:invalid, "This message could not be decrypted", message.split("\n"))
+    return unknown_status(cant_find_gpgme) unless @gpgme_present
+
+    gpg_opts = {:protocol => GPGME::PROTOCOL_OpenPGP}
+    gpg_opts = HookManager.run("gpg-options", 
+                               {:operation => "decrypt", :options => gpg_opts}) || gpg_opts
+    ctx = GPGME::Ctx.new(gpg_opts) 
+    cipher_data = GPGME::Data.from_str(format_payload(payload))
+    plain_data = GPGME::Data.empty
+    begin
+      ctx.decrypt_verify(cipher_data, plain_data)
+    rescue GPGME::Error => exc
+      info "Error while running gpg: #{exc.message}"
+      return Chunk::CryptoNotice.new(:invalid, "This message could not be decrypted", exc.message)
     end
-
-    output = IO.read output_fn.path
+    sig = self.verified_ok? ctx.verify_result
+    plain_data.seek(0, IO::SEEK_SET)
+    output = plain_data.read
     output.force_encoding Encoding::ASCII_8BIT if output.respond_to? :force_encoding
 
+    ## TODO: test to see if it is still necessary to do a 2nd run if verify
+    ## fails.
+    #
     ## check for a valid signature in an extra run because gpg aborts if the
     ## signature cannot be verified (but it is still able to decrypt)
-    sigoutput = run_gpg "#{payload_fn.path}"
-    sig = self.verified_ok? sigoutput, $?
+    #sigoutput = run_gpg "#{payload_fn.path}"
+    #sig = self.old_verified_ok? sigoutput, $?
 
     if armor
       msg = RMail::Message.new
@@ -207,8 +230,8 @@ private
     Chunk::CryptoNotice.new :unknown, "Unable to determine validity of cryptographic signature", lines
   end
 
-  def cant_find_binary
-    ["Can't find gpg binary in path."]
+  def cant_find_gpgme
+    ["Can't find gpgme gem."]
   end
 
   ## here's where we munge rmail output into the format that signed/encrypted
@@ -217,6 +240,28 @@ private
     payload.to_s.gsub(/(^|[^\r])\n/, "\\1\r\n")
   end
 
+  # remove the hex key_id and info in ()
+  def simplify_sig_line sig_line
+    sig_line = sig_line.sub(/from [0-9A-F]{16} /, "from ")
+    sig_line.sub(/\(.+\) </, "<")
+  end
+
+  def sig_output_lines signature
+    time_line = "Signature made " + signature.timestamp.strftime("%a %d %b %Y %H:%M:%S %Z") +
+                " using key ID " + signature.fingerprint[-8..-1]
+    first_sig = signature.to_s.sub(/from [0-9A-F]{16} /, 'from "') + '"'
+    output_lines = [time_line, first_sig]
+
+    ctx = GPGME::Ctx.new
+    if from_key = ctx.get_key(signature.fingerprint)
+      if from_key.uids.length > 1
+        aka_list = from_key.uids[1..-1]
+        aka_list.each { |aka| output_lines << '                aka "' + aka.uid + '"' }
+      end
+    end
+    output_lines
+  end
+
   # logic is:
   # if    gpgkey set for this account, then use that
   # elsif only one account,            then leave blank so gpg default will be user
@@ -224,30 +269,14 @@ private
   def gen_sign_user_opts from
     account = AccountManager.account_for from
     if !account.gpgkey.nil?
-      opts = "--local-user '#{account.gpgkey}'"
+      opts = {:signers => account.gpgkey}
     elsif AccountManager.user_emails.length == 1
       # only one account
-      opts = ""
+      opts = {}
     else
-      opts = "--local-user '#{from}'" 
+      opts = {:signers => from}
     end
     opts
   end
-
-  def run_gpg args, opts={}
-    args = HookManager.run("gpg-args", { :args => args }) || args
-    cmd = "LC_MESSAGES=C #{@cmd} #{args}"
-    if opts[:interactive] && BufferManager.instantiated?
-      output_fn = Tempfile.new "redwood.output"
-      output_fn.close
-      cmd += " > #{output_fn.path} 2> /dev/null"
-      debug "crypto: running: #{cmd}"
-      BufferManager.shell_out cmd
-      IO.read(output_fn.path) rescue "can't read output"
-    else
-      debug "crypto: running: #{cmd}"
-      `#{cmd} 2> /dev/null`
-    end
-  end
 end
 end
-- 
1.7.1

---

From: Hamish D <[email-masked]>
Date: Tue, 16 Nov 2010 23:05:50 +0000

> This does not work for me when running the foobacca/gpgme tree (commit
> 7b9a1eeeaaa25931963e2de49410d7cb0c7e6772). The CryptoNotice is empty.

Oops. I had one too many flatten! calls and was using the
output_lines.flatten! as a return value. However flatten! returns nil
if there is no flattening to be done. I've now fixed this in the
github tree. Please try it out and let me know how it works for you.

> This sounds nice. Thanks for your work!

And thank you for your testing and patience :)

The github version also has the sig-output hook set up. From the hook text:

START
Runs when the signature output is being generated, allowing you to
add extra information to your signatures if you want.

Variables:
signature: the signature object (class is GPGME::Signature)
from_key: the key that generated the signature (class is GPGME::Key)

Return value: an array of lines of output
END

I've attached a sample hook file if you want to have a play with it.
I'll document this all on the wiki if it gets accepted.

I'll give you a few days to find some more problems, but if you fail
to find any then I'll package this up as a single patch and resubmit
it all.

Hamish

---

From: Hamish D <[email-masked]>
Date: Mon, 8 Nov 2010 22:32:15 +0000

Hamish Downer

From 52441d1eb749bb1e3b5026e42a334e9c8f455833 Mon Sep 17 00:00:00 2001
From: Hamish Downer <[email-masked]>
Date: Fri, 5 Nov 2010 22:30:55 +0000
Subject: [PATCH 1/2] Converted crypto to use the gpgme gem

---
 bin/sup           |   11 +++

+no_gpgme = false
+begin
+  require 'gpgme'
+rescue LoadError
+  no_gpgme = true
+end
+
 require 'fileutils'
 require 'trollop'
 
+if no_gpgme
 
 class CryptoManager

 Variables:

   def initialize
     @mutex = Mutex.new

 

 

 

 

 
 
     if armor
       msg = RMail::Message.new
 
-  def cant_find_binary

From 7b9a1eeeaaa25931963e2de49410d7cb0c7e6772 Mon Sep 17 00:00:00 2001
From: Hamish Downer <[email-masked]>
Date: Mon, 8 Nov 2010 22:31:01 +0000
Subject: [PATCH 2/2] catch exception when no public key present

---
 lib/sup/crypto.rb |   16 ++++++++++++----
 1 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/lib/sup/crypto.rb b/lib/sup/crypto.rb
index 9d21ea0..83176d9 100644
--- a/lib/sup/crypto.rb
+++ b/lib/sup/crypto.rb
@@ -247,19 +247,27 @@ private
   end
 
   def sig_output_lines signature
+    # It appears that the signature.to_s call can lead to a EOFError if
+    # the key is not found. So start by looking for the key.
+    ctx = GPGME::Ctx.new
+    begin
+      from_key = ctx.get_key(signature.fingerprint)
+      first_sig = signature.to_s.sub(/from [0-9A-F]{16} /, 'from "') + '"'
+    rescue EOFError => error
+      first_sig = "No public key available for #{signature.fingerprint}"
+    end
+
     time_line = "Signature made " + signature.timestamp.strftime("%a %d %b %Y %H:%M:%S %Z") +
                 " using key ID " + signature.fingerprint[-8..-1]
-    first_sig = signature.to_s.sub(/from [0-9A-F]{16} /, 'from "') + '"'
     output_lines = [time_line, first_sig]
 
-    ctx = GPGME::Ctx.new
-    if from_key = ctx.get_key(signature.fingerprint)
+    if from_key 
       if from_key.uids.length > 1
         aka_list = from_key.uids[1..-1]
         aka_list.each { |aka| output_lines << '                aka "' + aka.uid + '"' }
       end
     end
-    output_lines
+    output_lines.flatten!
   end
 
   # logic is:
-- 
1.7.1

---

From: Alvaro Herrera <[email-masked]>
Date: Wed, 19 Jan 2011 13:12:18 -0300

Excerpts from Hamish D's message of dom nov 28 19:51:43 -0300 2010:
> OK, here is a set of 4 patches that implement the change over to the
> gpgme library. There's quite a bit of work in there so I thought I'd
> leave it as a few patches, but I have done some tidying.
> 
> I have some more ideas for improvements, but I'm happy that this
> reproduces the behaviour of using the gpg binary, and I find sup usage
> much smoother with this change.

When the gpgme gem is not installed, this patch makes sup die on start
with:

[mié ene 19 13:05:39 -0300 2011] No 'gpgme' gem detected. Install it for email encryption, decryption and signatures.
[mié ene 19 13:05:39 -0300 2011] dynamically loading setlocale() from libc.so.6
[mié ene 19 13:05:39 -0300 2011] setting locale...
[mié ene 19 13:05:39 -0300 2011] locking /home/alvherre/.sup/lock...
[mié ene 19 13:05:39 -0300 2011] stopped cursing
[mié ene 19 13:05:39 -0300 2011] ERROR: oh crap, an exception
[mié ene 19 13:05:39 -0300 2011] unlocking /home/alvherre/.sup/lock...
----------------------------------------------------------------
I'm very sorry. It seems that an error occurred in Sup. Please
accept my sincere apologies. Please submit the contents of
/home/alvherre/.sup/exception-log.txt and a brief report of the
circumstances to http://masanjin.net/sup-bugs/ so that I might
address this problem. Thank you!

Sincerely,
William
----------------------------------------------------------------
--- NameError from thread: main
uninitialized constant Redwood::CryptoManager::GPGME
/home/alvherre/Code/sup-mail/lib/sup/crypto.rb:49:in `initialize'
...

Ruby is from Debian package ruby1.8,
	ruby 1.8.7 (2010-08-16 patchlevel 302) [x86_64-linux]

I'm gonna install the gem now, but this should probably be fixed
nonetheless.

-- 
Álvaro Herrera -- Se vende casa en Ñuñoa: www.portalinmobiliario.com/993147
_______________________________________________
Sup-devel mailing list
[email-masked]
http://rubyforge.org/mailman/listinfo/sup-devel

---

From: Hamish D <[email-masked]>
Date: Sun, 28 Nov 2010 22:51:43 +0000

I'll leave it for others to decide whether to stick this in sup 0.12 -
I guess it could be risky to stick it in without it being tested, but
maybe it could be applied to the next tree, and then moved to main
after 0.12 has been released.

Hamish Downer

> to find any then I'll package this up as a single patch and resubmit
From 09dc4c17600742572c61605f977450d328296964 Mon Sep 17 00:00:00 2001
From: Hamish Downer <[email-masked]>
Date: Fri, 5 Nov 2010 22:30:55 +0000
Subject: [PATCH 1/4] Converted crypto to use the gpgme gem

---
 bin/sup           |   11 +++

+no_gpgme = false
+begin
+  require 'gpgme'
+rescue LoadError
+  no_gpgme = true
+end
+
 require 'fileutils'
 require 'trollop'
 
+if no_gpgme
 
 class CryptoManager

 Variables:

   def initialize
     @mutex = Mutex.new

 

 

 

 

 
 
     if armor
       msg = RMail::Message.new
 
-  def cant_find_binary

From 6615a325d8640b53e4f5bda3161d8ad56f9c1ed6 Mon Sep 17 00:00:00 2001
From: Hamish Downer <[email-masked]>
Date: Mon, 8 Nov 2010 22:31:01 +0000
Subject: [PATCH 2/4] catch exception when no public key present

---

 
-    ctx = GPGME::Ctx.new
 
   # logic is:
-- 
1.7.1

From 07f12934700cd8d85132d75b307019625cd17076 Mon Sep 17 00:00:00 2001
From: Hamish Downer <[email-masked]>
Date: Tue, 16 Nov 2010 20:58:01 +0000
Subject: [PATCH 3/4] improved signature messages

---
 lib/sup/crypto.rb |   31 ++++++++++++++++++++++++++-----
 1 files changed, 26 insertions(+), 5 deletions(-)

diff --git a/lib/sup/crypto.rb b/lib/sup/crypto.rb
index 83176d9..88228ff 100644
--- a/lib/sup/crypto.rb
+++ b/lib/sup/crypto.rb
@@ -242,8 +242,7 @@ private
 
   # remove the hex key_id and info in ()
   def simplify_sig_line sig_line
-    sig_line = sig_line.sub(/from [0-9A-F]{16} /, "from ")
-    sig_line.sub(/\(.+\) </, "<")
+    sig_line.sub(/from [0-9A-F]{16} /, "from ")
   end
 
   def sig_output_lines signature
@@ -253,21 +252,43 @@ private
     begin
       from_key = ctx.get_key(signature.fingerprint)
       first_sig = signature.to_s.sub(/from [0-9A-F]{16} /, 'from "') + '"'
-    rescue EOFError => error
+    rescue EOFError 
+      from_key = nil
       first_sig = "No public key available for #{signature.fingerprint}"
     end
 
     time_line = "Signature made " + signature.timestamp.strftime("%a %d %b %Y %H:%M:%S %Z") +
-                " using key ID " + signature.fingerprint[-8..-1]
+                " using " + key_type(from_key, signature.fingerprint) + 
+                "key ID " + signature.fingerprint[-8..-1]
     output_lines = [time_line, first_sig]
 
     if from_key 
+      # first list all the uids
       if from_key.uids.length > 1
         aka_list = from_key.uids[1..-1]
         aka_list.each { |aka| output_lines << '                aka "' + aka.uid + '"' }
       end
+
+      # now we want to look at the trust of that key
+      if signature.validity != GPGME::GPGME_VALIDITY_FULL && signature.validity != GPGME::GPGME_VALIDITY_MARGINAL
+        output_lines << "WARNING: This key is not certified with a trusted signature!"
+        output_lines << "There is no indication that the signature belongs to the owner"
+      end
+    end
+    output_lines
+  end
+
+  def key_type key, fpr
+    return "" if key.nil?
+    subkey = key.subkeys.find {|subkey| subkey.fpr == fpr || subkey.keyid == fpr }
+    return "" if subkey.nil?
+
+    case subkey.pubkey_algo
+    when GPGME::PK_RSA then "RSA "
+    when GPGME::PK_DSA then "DSA "
+    when GPGME::PK_ELG then "ElGamel "
+    when GPGME::PK_ELG_E then "ElGamel "
     end
-    output_lines.flatten!
   end
 
   # logic is:
-- 
1.7.1

From 71bae56903351adf43f6a7ebdd9766764f302f16 Mon Sep 17 00:00:00 2001
From: Hamish Downer <[email-masked]>
Date: Tue, 16 Nov 2010 22:54:06 +0000
Subject: [PATCH 4/4] added signature output hook

---
 lib/sup/crypto.rb |   15 +++++++++++++++
 1 files changed, 15 insertions(+), 0 deletions(-)

diff --git a/lib/sup/crypto.rb b/lib/sup/crypto.rb
index 88228ff..b9ffb17 100644
--- a/lib/sup/crypto.rb
+++ b/lib/sup/crypto.rb
@@ -28,6 +28,17 @@ options: a dictionary of values to be passed to GPGME
 Return value: a dictionary to be passed to GPGME
 EOS
 
+  HookManager.register "sig-output", <<EOS
+Runs when the signature output is being generated, allowing you to
+add extra information to your signatures if you want.
+  
+Variables:
+signature: the signature object (class is GPGME::Signature)
+from_key: the key that generated the signature (class is GPGME::Key)
+
+Return value: an array of lines of output
+EOS
+
   def initialize
     @mutex = Mutex.new
 
@@ -274,6 +285,10 @@ private
         output_lines << "WARNING: This key is not certified with a trusted signature!"
         output_lines << "There is no indication that the signature belongs to the owner"
       end
+
+      # finally, run the hook
+      output_lines << HookManager.run("sig-output",
+                               {:signature => signature, :from_key => from_key})
     end
     output_lines
   end
-- 
1.7.1

---

From: Hamish D <[email-masked]>
Date: Sun, 30 Jan 2011 23:57:03 +0000

> I just discovered another problem: If the secret key is not available
> (because it's on a removable media and the media is not mounted), the
> mail is sent anyway. While this is just a bit annoying for signed mail
> it definitely should not happen for encrypted mails. Current sup
> corectly fails in this case.

Finally got around to fixing this issue. I think it may be a bug in
the gpgme gem which I'll investigate further at some point. In the
meantime, I've worked out how to get sup to stop this being a problem.
The fix has been applied to next, and will hopefully move to master
before too long.

http://www.mail-archive.com/[email-masked]/msg00899.html

Hamish

---

From: Rich Lane <[email-masked]>
Date: Tue, 18 Jan 2011 22:11:42 -0500

Excerpts from Rich Lane's message of Thu Dec 23 13:43:45 -0500 2010:
> All 5 patches applied to branch gpgme and merged to next.

Branch gpgme has been merged to master.
_______________________________________________
Sup-devel mailing list
[email-masked]
http://rubyforge.org/mailman/listinfo/sup-devel

---

From: Gaudenz Steinlin <[email-masked]>
Date: Wed, 01 Dec 2010 09:37:58 +0100

> considered stable (enough).

Thanks for clarifying this.At the moment this does not seem to reflect
the current reality in the git repository. Master has quite a lot of
commits next does not have. Also next has not been updated since
several months.

I guess this is due to the fact that there were no major code changes
during the last months and therefore all the work has been done
directly on master. 

Gaudenz

---

From: Hamish D <[email-masked]>
Date: Mon, 6 Dec 2010 23:31:43 +0000

> corectly fails in this case.

I have replicated this (by turning off gpg agent) but I'm confused as
to why this is happening. If I try the same steps in irb I get an
exception, and this should be caught and dealt with in the same way as
current sup does. I guess I'll have to keep trying to replicate more
and more of the path way through ... sigh.

Once I have worked out the proper logic I can then add some extra
checks for ensuring that gpg agent is running and that sup knows where
to find it. I could even have sup ask you for your gpg passphrase with
gpgme. There might be some security issues with having ruby ask you
for your passphrase I guess, but I don't think it would be worse than
gpg agent. gpg agent doesn't seem to have the suid bit set, though
maybe as a C program it can be more rigorous about overwriting your
passphrase in memory. I could always implement it as a hook with gpg
agent as the default.

> It would also be nice to have different colors for different trust
> levels. So you don't have to expand the extra information to see if a
> valid signature is trusted or not. Is this already possible with the
> current hook?

That requires code changes, but I've done that and attached a patch
(intended to go on top of the other 4 patches). Now untrusted
signatures have a blue background. (Trusted signatures have a default
background - black normally, and bad signatures have a red
background). All signatures have yellow text.

I'm quite open to a different colour scheme being chosen if someone
thinks something else would be clearer.

Hamish Downer
From b99343dd358361ac47c9e00198b52df28fbd95cc Mon Sep 17 00:00:00 2001
From: Hamish Downer <[email-masked]>
Date: Mon, 6 Dec 2010 22:33:17 +0000
Subject: [PATCH 5/5] added color for untrusted cryptonotice

---
 lib/sup/colormap.rb       |    1 +
 lib/sup/crypto.rb         |   29 ++++++++++++++++++++---------
 lib/sup/message-chunks.rb |    1 +
 3 files changed, 22 insertions(+), 9 deletions(-)

diff --git a/lib/sup/colormap.rb b/lib/sup/colormap.rb
index 7de48db..8402add 100644
--- a/lib/sup/colormap.rb
+++ b/lib/sup/colormap.rb
@@ -40,6 +40,7 @@ class Colormap
     :missing_message => { :fg => "black", :bg => "red" },
     :attachment => { :fg => "cyan", :bg => "default" },
     :cryptosig_valid => { :fg => "yellow", :bg => "default", :attrs => ["bold"] },
+    :cryptosig_valid_untrusted => { :fg => "yellow", :bg => "blue", :attrs => ["bold"] },
     :cryptosig_unknown => { :fg => "cyan", :bg => "default" },
     :cryptosig_invalid => { :fg => "yellow", :bg => "red", :attrs => ["bold"] },
     :generic_notice_patina => { :fg => "cyan", :bg => "default" },
diff --git a/lib/sup/crypto.rb b/lib/sup/crypto.rb
index b9ffb17..e532261 100644
--- a/lib/sup/crypto.rb
+++ b/lib/sup/crypto.rb
@@ -121,11 +121,15 @@ EOS
   def verified_ok? verify_result
     valid = true
     unknown = false
-    output_lines = []
+    all_output_lines = []
+    all_trusted = true
 
     verify_result.signatures.each do |signature|
-      output_lines.push(sig_output_lines(signature))
-      output_lines.flatten!
+      output_lines, trusted = sig_output_lines signature
+      all_output_lines << output_lines
+      all_output_lines.flatten!
+      all_trusted &&= trusted
+
       err_code = GPGME::gpgme_err_code(signature.status)
       if err_code == GPGME::GPG_ERR_BAD_SIGNATURE
         valid = false 
@@ -135,14 +139,18 @@ EOS
       end
     end
 
-    if output_lines.length == 0
-      Chunk::CryptoNotice.new :valid, "Encrypted message wasn't signed", output_lines
+    if all_output_lines.length == 0
+      Chunk::CryptoNotice.new :valid, "Encrypted message wasn't signed", all_output_lines
     elsif valid
-      Chunk::CryptoNotice.new(:valid, simplify_sig_line(verify_result.signatures[0].to_s), output_lines)
+      if all_trusted
+        Chunk::CryptoNotice.new(:valid, simplify_sig_line(verify_result.signatures[0].to_s), all_output_lines)
+      else
+        Chunk::CryptoNotice.new(:valid_untrusted, simplify_sig_line(verify_result.signatures[0].to_s), all_output_lines)
+      end
     elsif !unknown
-      Chunk::CryptoNotice.new(:invalid, simplify_sig_line(verify_result.signatures[0].to_s), output_lines)
+      Chunk::CryptoNotice.new(:invalid, simplify_sig_line(verify_result.signatures[0].to_s), all_output_lines)
     else
-      unknown_status output_lines
+      unknown_status all_output_lines
     end
   end
 
@@ -273,6 +281,7 @@ private
                 "key ID " + signature.fingerprint[-8..-1]
     output_lines = [time_line, first_sig]
 
+    trusted = false
     if from_key 
       # first list all the uids
       if from_key.uids.length > 1
@@ -284,13 +293,15 @@ private
       if signature.validity != GPGME::GPGME_VALIDITY_FULL && signature.validity != GPGME::GPGME_VALIDITY_MARGINAL
         output_lines << "WARNING: This key is not certified with a trusted signature!"
         output_lines << "There is no indication that the signature belongs to the owner"
+      else
+        trusted = true
       end
 
       # finally, run the hook
       output_lines << HookManager.run("sig-output",
                                {:signature => signature, :from_key => from_key})
     end
-    output_lines
+    return output_lines, trusted
   end
 
   def key_type key, fpr
diff --git a/lib/sup/message-chunks.rb b/lib/sup/message-chunks.rb
index 02d28f6..0097450 100644
--- a/lib/sup/message-chunks.rb
+++ b/lib/sup/message-chunks.rb
@@ -272,6 +272,7 @@ EOS
     def patina_color
       case status
       when :valid then :cryptosig_valid_color
+      when :valid_untrusted then :cryptosig_valid_untrusted_color
       when :invalid then :cryptosig_invalid_color
       else :cryptosig_unknown_color
       end
-- 
1.7.1

---

From: Gaudenz Steinlin <[email-masked]>
Date: Mon, 29 Nov 2010 10:41:35 +0100

Hi Hamish

> after 0.12 has been released.

As far as I understood the branch layout the flow of changes is master
-> next -> release. So applying to next would mean it ends up in the
next release (0.12).

Gaudenz

---

From: Hamish D <[email-masked]>
Date: Sun, 30 Jan 2011 23:59:38 +0000

> The fix has been applied to next, and will hopefully move to master

Link should have been:

http://rubyforge.org/pipermail/sup-devel/2011-January/000966.html

Sorry, late and tired ...

Hamish Downer

---

From: Gaudenz Steinlin <[email-masked]>
Date: Tue, 16 Nov 2010 12:42:33 +0100

Excerpts from Hamish D's message of Don Nov 11 18:25:13 +0100 2010:
> The gpg binary is not called, so there is no output from it. What do
> you mean by the "plus sign" - where does this turn up? I haven't
> noticed that. If you tell me exactly what your expected behaviour is
> I'll see if I can replicate it.

At least if you can't provide any additional information, the "+"
character should not show up as this is an indicator that there is
some collapsed text to show.

Gaudenz

---

From: Hamish D <[email-masked]>
Date: Thu, 11 Nov 2010 17:25:13 +0000

>  signature is valid but not trusted.

I have a hook I'm working on so that you can access the full Signature
object returned by gpgme. I'll post about it when it's working and I
have some example hook code. This would be able to tell you whether
the key that signed the message is trusted by you etc. I'll hopefully
get to that within a week.

Hamish

---

From: Gaudenz Steinlin <[email-masked]>
Date: Tue, 16 Nov 2010 19:36:55 +0100

This does not work for me when running the foobacca/gpgme tree (commit
7b9a1eeeaaa25931963e2de49410d7cb0c7e6772). The CryptoNotice is empty.
I'm using the following packages from Debians testing distribution:
- ruby1.8 1.8.7.302-2
- libgpgme-ruby1.8 1.0.8-3
- libgpgme11 1.2.0-1.2
- libgpg-error0 1.6-1

Please tell me if you need further information to debug the problem.

> have a series of smaller changes?

Dunno about the official policy. I'd say break it if the pieces are
usefull on their own (but may depend on each other), otherwise make
them one patch.

Gaudenz

---

From: Rich Lane <[email-masked]>
Date: Thu, 23 Dec 2010 13:43:45 -0500