module Redwood class CryptoManager include Singleton def initialize @mutex = Mutex.new self.class.i_am_the_instance self bin = `which gpg`.chomp bin = `which pgp`.chomp unless bin =~ /\S/ @cmd = case bin when /\S/ "#{bin} --quiet --batch --no-verbose --logger-fd 1 --use-agent" else nil end end # returns a cryptosignature def verify payload, signature # both RubyMail::Message objects return unknown_status(cant_find_binary) unless @cmd payload_fn = Tempfile.new "redwood.payload" payload_fn.write payload.to_s.gsub(/(^|[^\r])\n/, "\\1\r\n").gsub(/^MIME-Version: .*\r\n/, "") payload_fn.close signature_fn = Tempfile.new "redwood.signature" signature_fn.write signature.decode signature_fn.close cmd = "#{@cmd} --verify #{signature_fn.path} #{payload_fn.path} 2> /dev/null" #Redwood::log "gpg: running: #{cmd}" gpg_output = `#{cmd}` #Redwood::log "got output: #{gpg_output.inspect}" output_lines = gpg_output.split(/\n/) if gpg_output =~ /^gpg: (.* signature from .*$)/ if $? == 0 Chunk::CryptoNotice.new :valid, $1, output_lines else Chunk::CryptoNotice.new :invalid, $1, output_lines end else unknown_status output_lines end end # returns decrypted_message, status, desc, lines def decrypt payload # RubyMail::Message objects return unknown_status(cant_find_binary) unless @cmd # cmd = "#{@cmd} --decrypt 2> /dev/null" # Redwood::log "gpg: running: #{cmd}" # gpg_output = # IO.popen(cmd, "a+") do |f| # f.puts payload.to_s # f.gets # end payload_fn = Tempfile.new "redwood.payload" payload_fn.write payload.to_s payload_fn.close cmd = "#{@cmd} --decrypt #{payload_fn.path} 2> /dev/null" Redwood::log "gpg: running: #{cmd}" gpg_output = `#{cmd}` Redwood::log "got output: #{gpg_output.inspect}" if $? == 0 # successful decryption decrypted_payload, sig_lines = if gpg_output =~ /\A(.*?)((^gpg: .*$)+)\Z/m [$1, $2] else [gpg_output, nil] end sig = if sig_lines # encrypted & signed if sig_lines =~ /^gpg: (Good signature from .*$)/ Chunk::CryptoNotice.new :valid, $1, sig_lines.split("\n") else Chunk::CryptoNotice.new :invalid, $1, sig_lines.split("\n") end end notice = Chunk::CryptoNotice.new :valid, "This message has been decrypted for display" [RMail::Parser.read(decrypted_payload), sig, notice] else notice = Chunk::CryptoNotice.new :invalid, "This message could not be decrypted", gpg_output.split("\n") [nil, nil, notice] end end private def unknown_status lines=[] Chunk::CryptoNotice.new :unknown, "Unable to determine validity of cryptographic signature", lines end def cant_find_binary ["Can't find gpg or pgp binary in path"] end end end