ueno / ruby-gpgme

a ruby interface to GnuPG Made Easy (GPGME).
GNU Lesser General Public License v2.1
231 stars 99 forks source link

Misleading error when trying to use :passphrase_callback with GnuPG 2.0.x #31

Open f3ndot opened 10 years ago

f3ndot commented 10 years ago

Right now I'm attempting to develop an application using ruby-gpgme (at commit fd1d6b09d1b169ae433a4f4b4e2389f5c954bf8c). Here is your example sign, using the passphrase callback:

#!/usr/bin/env ruby
# test_sign.rb
require 'gpgme'

def passfunc(hook, uid_hint, passphrase_info, prev_was_bad, fd)
  $stderr.write("Passphrase for #{uid_hint}: ")
  $stderr.flush
  begin
    system('stty -echo')
    io = IO.for_fd(fd, 'w')
    io.puts(gets)
    io.flush
  ensure
    (0 ... $_.length).each do |i| $_[i] = ?0 end if $_
    system('stty echo')
  end
  $stderr.puts
end

crypto = GPGME::Crypto.new
signature = crypto.clearsign('test test test', {
                               :signer => '0xDEADBEEF',
                               :passphrase_callback => method(:passfunc)
                             })
puts signature.read

However when I'm using GnuPG version 2.0.x:

$ gpg --version
gpg (GnuPG/MacGPG2) 2.0.22
libgcrypt 1.5.3
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Home: ~/.gnupg
Supported algorithms:
Pubkey: RSA, ELG, DSA, ?, ?
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
        CAMELLIA128, CAMELLIA192, CAMELLIA256
Hash: MD5, SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
Compression: Uncompressed, ZIP, ZLIB, BZIP2

I get this horribly misleading error when trying to set the callback:

$ bundle exec ruby test_sign.rb
/Users/justinbull/.rvm/gems/ruby-2.0.0-p247/bundler/gems/ruby-gpgme-fd1d6b09d1b1/lib/gpgme/ctx.rb:438:in `sign': Bad passphrase (GPGME::Error::BadPassphrase)
    from /Users/justinbull/.rvm/gems/ruby-2.0.0-p247/bundler/gems/ruby-gpgme-fd1d6b09d1b1/lib/gpgme/crypto.rb:249:in `block in sign'
    from /Users/justinbull/.rvm/gems/ruby-2.0.0-p247/bundler/gems/ruby-gpgme-fd1d6b09d1b1/lib/gpgme/ctx.rb:71:in `new'
    from /Users/justinbull/.rvm/gems/ruby-2.0.0-p247/bundler/gems/ruby-gpgme-fd1d6b09d1b1/lib/gpgme/crypto.rb:242:in `sign'
    from /Users/justinbull/.rvm/gems/ruby-2.0.0-p247/bundler/gems/ruby-gpgme-fd1d6b09d1b1/lib/gpgme/crypto.rb:331:in `clearsign'
    from test_sign.rb:28:in `<main>'

I don't know what you can do to alleviate this error, but perhaps detect the version is 2.0.x and say that the :passphrase_callback logic is not supported?

An aside:

Using GnuPG 2.0.x, what's the best method to programatically sign some data? That is, without a human typing in the passphrase of a private key?

dansketcher commented 10 years ago

I found no way of alleviating that problem - when using GPGME headless with GnuPG 2.x IIRC I would always have to use the gpg-agent, because otherwise the pinentry application pops up with no term and cannot be used, effectively hanging your app waiting for user input.

I remember reading somewhere in the GPG docs (I think) that this was entirely intentional and the desired behaviour as a change from the GPG 1.x codebase... Something to do with it being better to use on interactive systems. You'll also note that 1.x is still being updated. I know that the behaviour you are after works on GPG 1.x, and that while it is probably not in your favourite distro's package manager, it very easy to compile, so that might be worth a look.

f3ndot commented 10 years ago

Thanks for the quick reply @dansketcher.

I've installed GnuPG 1.4 alongside/parallel to GnuPG 2.0.x and ensure its engine is selected by running:

GPGME::gpgme_set_engine_info(GPGME::PROTOCOL_OpenPGP, '/path/to/gpg/1.4/binary', nil)
crypto = GPGME::Crypto.new

Which affords me the headless functionality I'm looking for.

dansketcher commented 10 years ago

No problem

Note as well that that last param is the home dir so in Rails apps I do something like this:

home_dir = Rails.root.join('config', 'keys', Rails.env.to_s).to_s
bin = '/usr/local/bin/gpg'
GPGME::Engine.set_info(GPGME::PROTOCOL_OpenPGP, bin, home_dir)

Obvs prod keys are not stored in the app repo but it does make management of keys much easier, as GPG looks relative to that for its keychains. gpg --gen-key with the --homedir option will get the keys made for you outside the default location

dansketcher commented 10 years ago

Oh and while we're on weird errors, I'll put it here so that it's at least somewhere....

If after generating keys you get a GPGME::Error::General on crypt ops it might be because the key is not trusted. Set the trust to Ultimate (you generated it, hopefully you trust it!).

gpg --homedir ./key_location --edit-key A1B2C3D4 trust