* [sup-devel] [PATCH] Converted crypto to use the gpgme gem
@ 2010-11-06 20:08 Hamish D
2010-11-08 11:21 ` Hamish D
0 siblings, 1 reply; 19+ messages in thread
From: Hamish D @ 2010-11-06 20:08 UTC (permalink / raw)
To: sup-devel
[-- Attachment #1: Type: text/plain, Size: 1558 bytes --]
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
[-- Attachment #2: 0001-Converted-crypto-to-use-the-gpgme-gem.patch --]
[-- Type: text/x-patch, Size: 13169 bytes --]
From 52441d1eb749bb1e3b5026e42a334e9c8f455833 Mon Sep 17 00:00:00 2001
From: Hamish Downer <dmishd@gmail.com>
Date: Fri, 5 Nov 2010 22:30:55 +0000
Subject: [PATCH] Converted crypto to use the gpgme gem
---
| 11 +++
| 231 ++++++++++++++++++++++++++++++-----------------------
2 files changed, 141 insertions(+), 101 deletions(-)
--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
--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?; !@cmd.nil? 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
[-- Attachment #3: Type: text/plain, Size: 143 bytes --]
_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [sup-devel] [PATCH] Converted crypto to use the gpgme gem
2010-11-06 20:08 [sup-devel] [PATCH] Converted crypto to use the gpgme gem Hamish D
@ 2010-11-08 11:21 ` Hamish D
2010-11-08 22:32 ` Hamish D
0 siblings, 1 reply; 19+ messages in thread
From: Hamish D @ 2010-11-08 11:21 UTC (permalink / raw)
To: sup-devel
[-- Attachment #1.1: Type: text/plain, Size: 1839 bytes --]
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" <dmishd@gmail.com> 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
[-- Attachment #1.2: Type: text/html, Size: 2134 bytes --]
[-- Attachment #2: Type: text/plain, Size: 143 bytes --]
_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [sup-devel] [PATCH] Converted crypto to use the gpgme gem
2010-11-08 11:21 ` Hamish D
@ 2010-11-08 22:32 ` Hamish D
2010-11-11 9:09 ` Gaudenz Steinlin
0 siblings, 1 reply; 19+ messages in thread
From: Hamish D @ 2010-11-08 22:32 UTC (permalink / raw)
To: sup-devel
[-- Attachment #1: Type: text/plain, Size: 2052 bytes --]
OK, the second patch fixes the problem with the first patch.
Hamish Downer
On 8 November 2010 11:21, Hamish D <dmishd@gmail.com> 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" <dmishd@gmail.com> 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
>
[-- Attachment #2: 0001-Converted-crypto-to-use-the-gpgme-gem.patch --]
[-- Type: text/x-patch, Size: 13173 bytes --]
From 52441d1eb749bb1e3b5026e42a334e9c8f455833 Mon Sep 17 00:00:00 2001
From: Hamish Downer <dmishd@gmail.com>
Date: Fri, 5 Nov 2010 22:30:55 +0000
Subject: [PATCH 1/2] 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?; !@cmd.nil? 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
[-- Attachment #3: 0002-catch-exception-when-no-public-key-present.patch --]
[-- Type: text/x-patch, Size: 1572 bytes --]
From 7b9a1eeeaaa25931963e2de49410d7cb0c7e6772 Mon Sep 17 00:00:00 2001
From: Hamish Downer <dmishd@gmail.com>
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
[-- Attachment #4: Type: text/plain, Size: 143 bytes --]
_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [sup-devel] [PATCH] Converted crypto to use the gpgme gem
2010-11-08 22:32 ` Hamish D
@ 2010-11-11 9:09 ` Gaudenz Steinlin
2010-11-11 17:25 ` Hamish D
0 siblings, 1 reply; 19+ messages in thread
From: Gaudenz Steinlin @ 2010-11-11 9:09 UTC (permalink / raw)
To: sup-devel
[-- Attachment #1.1: Type: text/plain, Size: 3021 bytes --]
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 <dmishd@gmail.com> 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" <dmishd@gmail.com> 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 ~
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 482 bytes --]
[-- Attachment #2: Type: text/plain, Size: 143 bytes --]
_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [sup-devel] [PATCH] Converted crypto to use the gpgme gem
2010-11-11 9:09 ` Gaudenz Steinlin
@ 2010-11-11 17:25 ` Hamish D
2010-11-16 11:42 ` Gaudenz Steinlin
0 siblings, 1 reply; 19+ messages in thread
From: Hamish D @ 2010-11-11 17:25 UTC (permalink / raw)
To: Sup developer discussion
> - 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?
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.
> - 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.
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
_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [sup-devel] [PATCH] Converted crypto to use the gpgme gem
2010-11-11 17:25 ` Hamish D
@ 2010-11-16 11:42 ` Gaudenz Steinlin
2010-11-16 14:20 ` Hamish D
0 siblings, 1 reply; 19+ messages in thread
From: Gaudenz Steinlin @ 2010-11-16 11:42 UTC (permalink / raw)
To: sup-devel
[-- Attachment #1.1: Type: text/plain, Size: 1203 bytes --]
Excerpts from Hamish D's message of Don Nov 11 18:25:13 +0100 2010:
> > - 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?
>
> 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.
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, ...
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
--
Ever tried. Ever failed. No matter.
Try again. Fail again. Fail better.
~ Samuel Beckett ~
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 482 bytes --]
[-- Attachment #2: Type: text/plain, Size: 143 bytes --]
_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [sup-devel] [PATCH] Converted crypto to use the gpgme gem
2010-11-16 11:42 ` Gaudenz Steinlin
@ 2010-11-16 14:20 ` Hamish D
2010-11-16 18:36 ` Gaudenz Steinlin
0 siblings, 1 reply; 19+ messages in thread
From: Hamish D @ 2010-11-16 14:20 UTC (permalink / raw)
To: Sup developer discussion
> 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
_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [sup-devel] [PATCH] Converted crypto to use the gpgme gem
2010-11-16 14:20 ` Hamish D
@ 2010-11-16 18:36 ` Gaudenz Steinlin
2010-11-16 23:05 ` Hamish D
0 siblings, 1 reply; 19+ messages in thread
From: Gaudenz Steinlin @ 2010-11-16 18:36 UTC (permalink / raw)
To: sup-devel
Excerpts from Hamish D's message of Die Nov 16 15:20:03 +0100 2010:
> > 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.
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.
>
> 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.
This sounds nice. Thanks for your work!
>
> 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?
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
--
Ever tried. Ever failed. No matter.
Try again. Fail again. Fail better.
~ Samuel Beckett ~
_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [sup-devel] [PATCH] Converted crypto to use the gpgme gem
2010-11-16 18:36 ` Gaudenz Steinlin
@ 2010-11-16 23:05 ` Hamish D
2010-11-28 22:51 ` Hamish D
0 siblings, 1 reply; 19+ messages in thread
From: Hamish D @ 2010-11-16 23:05 UTC (permalink / raw)
To: Sup developer discussion
[-- Attachment #1: Type: text/plain, Size: 1478 bytes --]
> 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.
>> 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.
>
> 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
[-- Attachment #2: sig-output.rb --]
[-- Type: application/x-ruby, Size: 1222 bytes --]
[-- Attachment #3: Type: text/plain, Size: 143 bytes --]
_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [sup-devel] [PATCH] Converted crypto to use the gpgme gem
2010-11-16 23:05 ` Hamish D
@ 2010-11-28 22:51 ` Hamish D
2010-11-29 9:41 ` Gaudenz Steinlin
2011-01-19 16:12 ` Alvaro Herrera
0 siblings, 2 replies; 19+ messages in thread
From: Hamish D @ 2010-11-28 22:51 UTC (permalink / raw)
To: Sup developer discussion
[-- Attachment #1: Type: text/plain, Size: 1744 bytes --]
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.
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
>
> >> 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.
> >
> > 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
[-- Attachment #2: 0001-Converted-crypto-to-use-the-gpgme-gem.patch --]
[-- Type: text/x-patch, Size: 13173 bytes --]
From 09dc4c17600742572c61605f977450d328296964 Mon Sep 17 00:00:00 2001
From: Hamish Downer <dmishd@gmail.com>
Date: Fri, 5 Nov 2010 22:30:55 +0000
Subject: [PATCH 1/4] 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?; !@cmd.nil? 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
[-- Attachment #3: 0002-catch-exception-when-no-public-key-present.patch --]
[-- Type: text/x-patch, Size: 1572 bytes --]
From 6615a325d8640b53e4f5bda3161d8ad56f9c1ed6 Mon Sep 17 00:00:00 2001
From: Hamish Downer <dmishd@gmail.com>
Date: Mon, 8 Nov 2010 22:31:01 +0000
Subject: [PATCH 2/4] 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
[-- Attachment #4: 0003-improved-signature-messages.patch --]
[-- Type: text/x-patch, Size: 2447 bytes --]
From 07f12934700cd8d85132d75b307019625cd17076 Mon Sep 17 00:00:00 2001
From: Hamish Downer <dmishd@gmail.com>
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
[-- Attachment #5: 0004-added-signature-output-hook.patch --]
[-- Type: text/x-patch, Size: 1396 bytes --]
From 71bae56903351adf43f6a7ebdd9766764f302f16 Mon Sep 17 00:00:00 2001
From: Hamish Downer <dmishd@gmail.com>
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
[-- Attachment #6: Type: text/plain, Size: 143 bytes --]
_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [sup-devel] [PATCH] Converted crypto to use the gpgme gem
2010-11-28 22:51 ` Hamish D
@ 2010-11-29 9:41 ` Gaudenz Steinlin
2010-11-30 6:22 ` Tero Tilus
` (2 more replies)
2011-01-19 16:12 ` Alvaro Herrera
1 sibling, 3 replies; 19+ messages in thread
From: Gaudenz Steinlin @ 2010-11-29 9:41 UTC (permalink / raw)
To: sup-devel
[-- Attachment #1.1: Type: text/plain, Size: 1537 bytes --]
Hi Hamish
Excerpts from Hamish D's message of Son Nov 28 23:51:43 +0100 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 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.
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?
>
> 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.
>
> 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.
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
--
Ever tried. Ever failed. No matter.
Try again. Fail again. Fail better.
~ Samuel Beckett ~
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 482 bytes --]
[-- Attachment #2: Type: text/plain, Size: 143 bytes --]
_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [sup-devel] [PATCH] Converted crypto to use the gpgme gem
2010-11-29 9:41 ` Gaudenz Steinlin
@ 2010-11-30 6:22 ` Tero Tilus
2010-12-01 8:37 ` Gaudenz Steinlin
2010-12-06 23:31 ` Hamish D
2011-01-30 23:57 ` Hamish D
2 siblings, 1 reply; 19+ messages in thread
From: Tero Tilus @ 2010-11-30 6:22 UTC (permalink / raw)
To: sup-devel
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
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [sup-devel] [PATCH] Converted crypto to use the gpgme gem
2010-11-30 6:22 ` Tero Tilus
@ 2010-12-01 8:37 ` Gaudenz Steinlin
0 siblings, 0 replies; 19+ messages in thread
From: Gaudenz Steinlin @ 2010-12-01 8:37 UTC (permalink / raw)
To: sup-devel
[-- Attachment #1.1: Type: text/plain, Size: 1028 bytes --]
Excerpts from Tero Tilus's message of Die Nov 30 07:22:48 +0100 2010:
> 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).
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
--
Ever tried. Ever failed. No matter.
Try again. Fail again. Fail better.
~ Samuel Beckett ~
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 0 bytes --]
[-- Attachment #2: Type: text/plain, Size: 143 bytes --]
_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [sup-devel] [PATCH] Converted crypto to use the gpgme gem
2010-11-29 9:41 ` Gaudenz Steinlin
2010-11-30 6:22 ` Tero Tilus
@ 2010-12-06 23:31 ` Hamish D
2010-12-23 18:43 ` Rich Lane
2011-01-30 23:57 ` Hamish D
2 siblings, 1 reply; 19+ messages in thread
From: Hamish D @ 2010-12-06 23:31 UTC (permalink / raw)
To: Sup developer discussion
[-- Attachment #1: Type: text/plain, Size: 1867 bytes --]
> 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.
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
[-- Attachment #2: 0005-added-color-for-untrusted-cryptonotice.patch --]
[-- Type: text/x-patch, Size: 4171 bytes --]
From b99343dd358361ac47c9e00198b52df28fbd95cc Mon Sep 17 00:00:00 2001
From: Hamish Downer <dmishd@gmail.com>
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
[-- Attachment #3: Type: text/plain, Size: 143 bytes --]
_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [sup-devel] [PATCH] Converted crypto to use the gpgme gem
2010-12-06 23:31 ` Hamish D
@ 2010-12-23 18:43 ` Rich Lane
2011-01-19 3:11 ` Rich Lane
0 siblings, 1 reply; 19+ messages in thread
From: Rich Lane @ 2010-12-23 18:43 UTC (permalink / raw)
To: Hamish D; +Cc: Sup developer discussion
All 5 patches applied to branch gpgme and merged to next.
_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [sup-devel] [PATCH] Converted crypto to use the gpgme gem
2010-12-23 18:43 ` Rich Lane
@ 2011-01-19 3:11 ` Rich Lane
0 siblings, 0 replies; 19+ messages in thread
From: Rich Lane @ 2011-01-19 3:11 UTC (permalink / raw)
To: Hamish D, Sup developer discussion
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
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [sup-devel] [PATCH] Converted crypto to use the gpgme gem
2010-11-28 22:51 ` Hamish D
2010-11-29 9:41 ` Gaudenz Steinlin
@ 2011-01-19 16:12 ` Alvaro Herrera
1 sibling, 0 replies; 19+ messages in thread
From: Alvaro Herrera @ 2011-01-19 16:12 UTC (permalink / raw)
To: sup-devel
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
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [sup-devel] [PATCH] Converted crypto to use the gpgme gem
2010-11-29 9:41 ` Gaudenz Steinlin
2010-11-30 6:22 ` Tero Tilus
2010-12-06 23:31 ` Hamish D
@ 2011-01-30 23:57 ` Hamish D
2011-01-30 23:59 ` Hamish D
2 siblings, 1 reply; 19+ messages in thread
From: Hamish D @ 2011-01-30 23:57 UTC (permalink / raw)
To: Sup developer discussion
> 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/sup-devel@rubyforge.org/msg00899.html
Hamish
_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [sup-devel] [PATCH] Converted crypto to use the gpgme gem
2011-01-30 23:57 ` Hamish D
@ 2011-01-30 23:59 ` Hamish D
0 siblings, 0 replies; 19+ messages in thread
From: Hamish D @ 2011-01-30 23:59 UTC (permalink / raw)
To: Sup developer discussion
> 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.
Link should have been:
http://rubyforge.org/pipermail/sup-devel/2011-January/000966.html
Sorry, late and tired ...
Hamish Downer
_______________________________________________
Sup-devel mailing list
Sup-devel@rubyforge.org
http://rubyforge.org/mailman/listinfo/sup-devel
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2011-01-31 2:14 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-11-06 20:08 [sup-devel] [PATCH] Converted crypto to use the gpgme gem Hamish D
2010-11-08 11:21 ` Hamish D
2010-11-08 22:32 ` Hamish D
2010-11-11 9:09 ` Gaudenz Steinlin
2010-11-11 17:25 ` Hamish D
2010-11-16 11:42 ` Gaudenz Steinlin
2010-11-16 14:20 ` Hamish D
2010-11-16 18:36 ` Gaudenz Steinlin
2010-11-16 23:05 ` Hamish D
2010-11-28 22:51 ` Hamish D
2010-11-29 9:41 ` Gaudenz Steinlin
2010-11-30 6:22 ` Tero Tilus
2010-12-01 8:37 ` Gaudenz Steinlin
2010-12-06 23:31 ` Hamish D
2010-12-23 18:43 ` Rich Lane
2011-01-19 3:11 ` Rich Lane
2011-01-30 23:57 ` Hamish D
2011-01-30 23:59 ` Hamish D
2011-01-19 16:12 ` Alvaro Herrera
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox